beautified all static js files
This commit is contained in:
parent
2fa1d8768b
commit
271ee1776b
254
static/js/ace.js
254
static/js/ace.js
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -18,38 +18,54 @@
|
|||
// requires: plugins
|
||||
// requires: undefined
|
||||
|
||||
Ace2Editor.registry = {
|
||||
nextId: 1
|
||||
};
|
||||
|
||||
Ace2Editor.registry = { nextId: 1 };
|
||||
|
||||
function Ace2Editor() {
|
||||
function Ace2Editor()
|
||||
{
|
||||
var thisFunctionsName = "Ace2Editor";
|
||||
var ace2 = Ace2Editor;
|
||||
|
||||
var editor = {};
|
||||
var info = { editor: editor, id: (ace2.registry.nextId++) };
|
||||
var info = {
|
||||
editor: editor,
|
||||
id: (ace2.registry.nextId++)
|
||||
};
|
||||
var loaded = false;
|
||||
|
||||
var actionsPendingInit = [];
|
||||
function pendingInit(func, optDoNow) {
|
||||
return function() {
|
||||
|
||||
function pendingInit(func, optDoNow)
|
||||
{
|
||||
return function()
|
||||
{
|
||||
var that = this;
|
||||
var args = arguments;
|
||||
function action() {
|
||||
func.apply(that, args);
|
||||
|
||||
function action()
|
||||
{
|
||||
func.apply(that, args);
|
||||
}
|
||||
if (optDoNow) {
|
||||
optDoNow.apply(that, args);
|
||||
if (optDoNow)
|
||||
{
|
||||
optDoNow.apply(that, args);
|
||||
}
|
||||
if (loaded) {
|
||||
action();
|
||||
if (loaded)
|
||||
{
|
||||
action();
|
||||
}
|
||||
else {
|
||||
actionsPendingInit.push(action);
|
||||
else
|
||||
{
|
||||
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 = [];
|
||||
|
@ -57,30 +73,69 @@ function Ace2Editor() {
|
|||
|
||||
ace2.registry[info.id] = info;
|
||||
|
||||
editor.importText = pendingInit(function(newCode, undoable) {
|
||||
info.ace_importText(newCode, undoable); });
|
||||
editor.importAText = pendingInit(function(newCode, apoolJsonObj, undoable) {
|
||||
info.ace_importAText(newCode, apoolJsonObj, undoable); });
|
||||
editor.exportText = function() {
|
||||
if (! loaded) return "(awaiting init)\n";
|
||||
editor.importText = pendingInit(function(newCode, undoable)
|
||||
{
|
||||
info.ace_importText(newCode, undoable);
|
||||
});
|
||||
editor.importAText = pendingInit(function(newCode, apoolJsonObj, undoable)
|
||||
{
|
||||
info.ace_importAText(newCode, apoolJsonObj, undoable);
|
||||
});
|
||||
editor.exportText = function()
|
||||
{
|
||||
if (!loaded) return "(awaiting init)\n";
|
||||
return info.ace_exportText();
|
||||
};
|
||||
editor.getFrame = function() { return info.frame || null; };
|
||||
editor.focus = pendingInit(function() { info.ace_focus(); });
|
||||
editor.setEditable = pendingInit(function(newVal) { info.ace_setEditable(newVal); });
|
||||
editor.getFormattedCode = function() { return info.ace_getFormattedCode(); };
|
||||
editor.setOnKeyPress = pendingInit(function (handler) { info.ace_setOnKeyPress(handler); });
|
||||
editor.setOnKeyDown = pendingInit(function (handler) { info.ace_setOnKeyDown(handler); });
|
||||
editor.setNotifyDirty = pendingInit(function (handler) { info.ace_setNotifyDirty(handler); });
|
||||
editor.getFrame = function()
|
||||
{
|
||||
return info.frame || null;
|
||||
};
|
||||
editor.focus = pendingInit(function()
|
||||
{
|
||||
info.ace_focus();
|
||||
});
|
||||
editor.setEditable = pendingInit(function(newVal)
|
||||
{
|
||||
info.ace_setEditable(newVal);
|
||||
});
|
||||
editor.getFormattedCode = function()
|
||||
{
|
||||
return info.ace_getFormattedCode();
|
||||
};
|
||||
editor.setOnKeyPress = pendingInit(function(handler)
|
||||
{
|
||||
info.ace_setOnKeyPress(handler);
|
||||
});
|
||||
editor.setOnKeyDown = pendingInit(function(handler)
|
||||
{
|
||||
info.ace_setOnKeyDown(handler);
|
||||
});
|
||||
editor.setNotifyDirty = pendingInit(function(handler)
|
||||
{
|
||||
info.ace_setNotifyDirty(handler);
|
||||
});
|
||||
|
||||
editor.setProperty = pendingInit(function(key, value) { info.ace_setProperty(key, value); });
|
||||
editor.getDebugProperty = function(prop) { return info.ace_getDebugProperty(prop); };
|
||||
editor.setProperty = pendingInit(function(key, value)
|
||||
{
|
||||
info.ace_setProperty(key, value);
|
||||
});
|
||||
editor.getDebugProperty = function(prop)
|
||||
{
|
||||
return info.ace_getDebugProperty(prop);
|
||||
};
|
||||
|
||||
editor.setBaseText = pendingInit(function(txt) { info.ace_setBaseText(txt); });
|
||||
editor.setBaseAttributedText = pendingInit(function(atxt, apoolJsonObj) {
|
||||
info.ace_setBaseAttributedText(atxt, apoolJsonObj); });
|
||||
editor.applyChangesToBase = pendingInit(function (changes, optAuthor,apoolJsonObj) {
|
||||
info.ace_applyChangesToBase(changes, optAuthor, apoolJsonObj); });
|
||||
editor.setBaseText = pendingInit(function(txt)
|
||||
{
|
||||
info.ace_setBaseText(txt);
|
||||
});
|
||||
editor.setBaseAttributedText = pendingInit(function(atxt, apoolJsonObj)
|
||||
{
|
||||
info.ace_setBaseAttributedText(atxt, apoolJsonObj);
|
||||
});
|
||||
editor.applyChangesToBase = pendingInit(function(changes, optAuthor, apoolJsonObj)
|
||||
{
|
||||
info.ace_applyChangesToBase(changes, optAuthor, apoolJsonObj);
|
||||
});
|
||||
// prepareUserChangeset:
|
||||
// Returns null if no new changes or ACE not ready. Otherwise, bundles up all user changes
|
||||
// to the latest base text into a Changeset, which is returned (as a string if encodeAsString).
|
||||
|
@ -90,36 +145,48 @@ function Ace2Editor() {
|
|||
// to prepareUserChangeset will return an updated changeset that takes into account the
|
||||
// latest user changes, and modify the changeset to be applied by applyPreparedChangesetToBase
|
||||
// accordingly.
|
||||
editor.prepareUserChangeset = function() {
|
||||
if (! loaded) return null;
|
||||
editor.prepareUserChangeset = function()
|
||||
{
|
||||
if (!loaded) return null;
|
||||
return info.ace_prepareUserChangeset();
|
||||
};
|
||||
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);
|
||||
});
|
||||
editor.setAuthorInfo = pendingInit(function(author, authorInfo) {
|
||||
editor.setAuthorInfo = pendingInit(function(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);
|
||||
});
|
||||
|
||||
editor.getUnhandledErrors = function() {
|
||||
if (! loaded) return [];
|
||||
editor.getUnhandledErrors = function()
|
||||
{
|
||||
if (!loaded) return [];
|
||||
// returns array of {error: <browser Error object>, time: +new Date()}
|
||||
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);
|
||||
});
|
||||
|
||||
editor.execCommand = pendingInit(function(cmd, arg1) {
|
||||
editor.execCommand = pendingInit(function(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);
|
||||
});
|
||||
|
||||
|
@ -127,50 +194,58 @@ function Ace2Editor() {
|
|||
// 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
|
||||
// source here.
|
||||
|
||||
var $$INCLUDE_CSS = function(fileName) {
|
||||
return '<link rel="stylesheet" type="text/css" href="'+fileName+'"/>';
|
||||
};
|
||||
var $$INCLUDE_JS = function(fileName) {
|
||||
return '\x3cscript type="text/javascript" src="'+fileName+'">\x3c/script>';
|
||||
};
|
||||
var $$INCLUDE_CSS = function(fileName)
|
||||
{
|
||||
return '<link rel="stylesheet" type="text/css" href="' + fileName + '"/>';
|
||||
};
|
||||
var $$INCLUDE_JS = function(fileName)
|
||||
{
|
||||
return '\x3cscript type="text/javascript" src="' + fileName + '">\x3c/script>';
|
||||
};
|
||||
var $$INCLUDE_JS_DEV = $$INCLUDE_JS;
|
||||
var $$INCLUDE_CSS_DEV = $$INCLUDE_CSS;
|
||||
|
||||
var $$INCLUDE_CSS_Q = function(fileName) {
|
||||
return '\'<link rel="stylesheet" type="text/css" href="'+fileName+'"/>\'';
|
||||
};
|
||||
var $$INCLUDE_JS_Q = function(fileName) {
|
||||
return '\'\\x3cscript type="text/javascript" src="'+fileName+'">\\x3c/script>\'';
|
||||
};
|
||||
var $$INCLUDE_CSS_Q = function(fileName)
|
||||
{
|
||||
return '\'<link rel="stylesheet" type="text/css" href="' + fileName + '"/>\'';
|
||||
};
|
||||
var $$INCLUDE_JS_Q = function(fileName)
|
||||
{
|
||||
return '\'\\x3cscript type="text/javascript" src="' + fileName + '">\\x3c/script>\'';
|
||||
};
|
||||
var $$INCLUDE_JS_Q_DEV = $$INCLUDE_JS_Q;
|
||||
var $$INCLUDE_CSS_Q_DEV = $$INCLUDE_CSS_Q;
|
||||
|
||||
editor.destroy = pendingInit(function() {
|
||||
editor.destroy = pendingInit(function()
|
||||
{
|
||||
info.ace_dispose();
|
||||
info.frame.parentNode.removeChild(info.frame);
|
||||
delete ace2.registry[info.id];
|
||||
info = null; // prevent IE 6 closure memory leaks
|
||||
});
|
||||
|
||||
editor.init = function(containerId, initialCode, doneFunc) {
|
||||
editor.init = function(containerId, initialCode, doneFunc)
|
||||
{
|
||||
|
||||
editor.importText(initialCode);
|
||||
|
||||
info.onEditorReady = function() {
|
||||
info.onEditorReady = function()
|
||||
{
|
||||
loaded = true;
|
||||
doActionsPendingInit();
|
||||
doneFunc();
|
||||
};
|
||||
|
||||
(function() {
|
||||
(function()
|
||||
{
|
||||
var doctype = "<!doctype html>";
|
||||
|
||||
var iframeHTML = ["'"+doctype+"<html><head>'"];
|
||||
|
||||
plugins.callHook(
|
||||
"aceInitInnerdocbodyHead", {iframeHTML:iframeHTML});
|
||||
|
||||
var iframeHTML = ["'" + doctype + "<html><head>'"];
|
||||
|
||||
plugins.callHook("aceInitInnerdocbodyHead", {
|
||||
iframeHTML: iframeHTML
|
||||
});
|
||||
|
||||
// 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/syntax.css"));
|
||||
|
@ -187,39 +262,26 @@ function Ace2Editor() {
|
|||
iframeHTML.push($$INCLUDE_JS_Q("static/js/linestylefilter.js"));
|
||||
iframeHTML.push($$INCLUDE_JS_Q("static/js/domline.js"));
|
||||
iframeHTML.push($$INCLUDE_JS_Q("static/js/ace2_inner.js"));
|
||||
|
||||
|
||||
iframeHTML.push('\'\\n<style type="text/css" title="dynamicsyntax"></style>\\n\'');
|
||||
iframeHTML.push('\'</head><body id="innerdocbody" class="syntax" spellcheck="false"> </body></html>\'');
|
||||
|
||||
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
|
||||
'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 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
|
||||
'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>',
|
||||
$$INCLUDE_CSS("static/css/editor.css"),
|
||||
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
|
||||
// (throbs busy while typing)
|
||||
'<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>'];
|
||||
var outerHTML = [doctype, '<html><head>', $$INCLUDE_CSS("static/css/editor.css"),
|
||||
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
|
||||
// (throbs busy while typing)
|
||||
'<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>'];
|
||||
|
||||
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();
|
||||
var len = this.length;
|
||||
var res = new Array(len);
|
||||
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);
|
||||
}
|
||||
return res;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,53 +15,63 @@
|
|||
*/
|
||||
|
||||
|
||||
function isNodeText(node) {
|
||||
function isNodeText(node)
|
||||
{
|
||||
return (node.nodeType == 3);
|
||||
}
|
||||
|
||||
function object(o) {
|
||||
var f = function() {};
|
||||
function object(o)
|
||||
{
|
||||
var f = function()
|
||||
{};
|
||||
f.prototype = o;
|
||||
return new f();
|
||||
}
|
||||
|
||||
function extend(obj, props) {
|
||||
for(var p in props) {
|
||||
function extend(obj, props)
|
||||
{
|
||||
for (var p in props)
|
||||
{
|
||||
obj[p] = props[p];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function forEach(array, func) {
|
||||
for(var i=0;i<array.length;i++) {
|
||||
function forEach(array, func)
|
||||
{
|
||||
for (var i = 0; i < array.length; i++)
|
||||
{
|
||||
var result = func(array[i], i);
|
||||
if (result) break;
|
||||
}
|
||||
}
|
||||
|
||||
function map(array, func) {
|
||||
function map(array, func)
|
||||
{
|
||||
var result = [];
|
||||
// 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));
|
||||
else result.push(array[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function filter(array, func) {
|
||||
function filter(array, func)
|
||||
{
|
||||
var result = [];
|
||||
// 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]);
|
||||
}
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
function isArray(testObject) {
|
||||
return testObject && typeof testObject === 'object' &&
|
||||
!(testObject.propertyIsEnumerable('length')) &&
|
||||
typeof testObject.length === 'number';
|
||||
function isArray(testObject)
|
||||
{
|
||||
return testObject && typeof testObject === 'object' && !(testObject.propertyIsEnumerable('length')) && typeof testObject.length === 'number';
|
||||
}
|
||||
|
||||
// Figure out what browser is being used (stolen from jquery 1.2.1)
|
||||
|
@ -75,41 +85,48 @@ var browser = {
|
|||
windows: /windows/.test(userAgent) // dgreensp
|
||||
};
|
||||
|
||||
function getAssoc(obj, name) {
|
||||
return obj["_magicdom_"+name];
|
||||
function getAssoc(obj, 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
|
||||
// copied to new nodes that are spawned during editing; also,
|
||||
// properties representable in HTML text can survive copy-and-paste
|
||||
obj["_magicdom_"+name] = value;
|
||||
obj["_magicdom_" + name] = value;
|
||||
}
|
||||
|
||||
// "func" is a function over 0..(numItems-1) that is monotonically
|
||||
// "increasing" with index (false, then true). Finds the boundary
|
||||
// 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 (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 high = numItems-1; // func(high) is always true
|
||||
while ((high - low) > 1) {
|
||||
var x = Math.floor((low+high)/2); // x != low, x != high
|
||||
var high = numItems - 1; // func(high) is always true
|
||||
while ((high - low) > 1)
|
||||
{
|
||||
var x = Math.floor((low + high) / 2); // x != low, x != high
|
||||
if (func(x)) high = x;
|
||||
else low = x;
|
||||
}
|
||||
return high;
|
||||
}
|
||||
|
||||
function binarySearchInfinite(expectedLength, func) {
|
||||
function binarySearchInfinite(expectedLength, func)
|
||||
{
|
||||
var i = 0;
|
||||
while (!func(i)) i += expectedLength;
|
||||
return binarySearch(i, func);
|
||||
}
|
||||
|
||||
function htmlPrettyEscape(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/\r?\n/g, '\\n');
|
||||
function htmlPrettyEscape(str)
|
||||
{
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\r?\n/g, '\\n');
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -22,7 +22,7 @@ function loadBroadcastJS()
|
|||
// Below Array#map code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_map.htm
|
||||
if (!Array.prototype.map)
|
||||
{
|
||||
Array.prototype.map = function (fun /*, thisp*/ )
|
||||
Array.prototype.map = function(fun /*, thisp*/ )
|
||||
{
|
||||
var len = this.length >>> 0;
|
||||
if (typeof fun != "function") throw new TypeError();
|
||||
|
@ -41,7 +41,7 @@ function loadBroadcastJS()
|
|||
// Below Array#forEach code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_foreach.htm
|
||||
if (!Array.prototype.forEach)
|
||||
{
|
||||
Array.prototype.forEach = function (fun /*, thisp*/ )
|
||||
Array.prototype.forEach = function(fun /*, thisp*/ )
|
||||
{
|
||||
var len = this.length >>> 0;
|
||||
if (typeof fun != "function") throw new TypeError();
|
||||
|
@ -57,7 +57,7 @@ function loadBroadcastJS()
|
|||
// Below Array#indexOf code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_indexof.htm
|
||||
if (!Array.prototype.indexOf)
|
||||
{
|
||||
Array.prototype.indexOf = function (elt /*, from*/ )
|
||||
Array.prototype.indexOf = function(elt /*, from*/ )
|
||||
{
|
||||
var len = this.length >>> 0;
|
||||
|
||||
|
@ -77,11 +77,11 @@ function loadBroadcastJS()
|
|||
{
|
||||
try
|
||||
{
|
||||
if(window.console) console.log.apply(console, arguments);
|
||||
if (window.console) console.log.apply(console, arguments);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
if(window.console) console.log("error printing: ", e);
|
||||
if (window.console) console.log("error printing: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,6 @@ function loadBroadcastJS()
|
|||
var userId = "hiddenUser" + randomString();
|
||||
var socketId;
|
||||
//var socket;
|
||||
|
||||
var channelState = "DISCONNECTED";
|
||||
|
||||
var appLevelDisconnectReason = null;
|
||||
|
@ -120,7 +119,7 @@ function loadBroadcastJS()
|
|||
clientVars.initialStyledContents.atext.attribs, clientVars.initialStyledContents.atext.text),
|
||||
|
||||
// generates a jquery element containing HTML for a line
|
||||
lineToElement: function (line, aline)
|
||||
lineToElement: function(line, aline)
|
||||
{
|
||||
var element = document.createElement("div");
|
||||
var emptyLine = (line == '\n');
|
||||
|
@ -133,7 +132,7 @@ function loadBroadcastJS()
|
|||
return $(element);
|
||||
},
|
||||
|
||||
applySpliceToDivs: function (start, numRemoved, newLines)
|
||||
applySpliceToDivs: function(start, numRemoved, newLines)
|
||||
{
|
||||
// remove spliced-out lines from DOM
|
||||
for (var i = start; i < start + numRemoved && i < this.currentDivs.length; i++)
|
||||
|
@ -176,11 +175,11 @@ function loadBroadcastJS()
|
|||
},
|
||||
|
||||
// splice the lines
|
||||
splice: function (start, numRemoved, newLinesVA)
|
||||
splice: function(start, numRemoved, newLinesVA)
|
||||
{
|
||||
var newLines = Array.prototype.slice.call(arguments, 2).map(
|
||||
|
||||
function (s)
|
||||
function(s)
|
||||
{
|
||||
return s;
|
||||
});
|
||||
|
@ -194,17 +193,17 @@ function loadBroadcastJS()
|
|||
this.currentLines.splice.apply(this.currentLines, arguments);
|
||||
},
|
||||
// returns the contents of the specified line I
|
||||
get: function (i)
|
||||
get: function(i)
|
||||
{
|
||||
return this.currentLines[i];
|
||||
},
|
||||
// returns the number of lines in the document
|
||||
length: function ()
|
||||
length: function()
|
||||
{
|
||||
return this.currentLines.length;
|
||||
},
|
||||
|
||||
getActiveAuthors: function ()
|
||||
getActiveAuthors: function()
|
||||
{
|
||||
var self = this;
|
||||
var authors = [];
|
||||
|
@ -212,7 +211,7 @@ function loadBroadcastJS()
|
|||
var alines = self.alines;
|
||||
for (var i = 0; i < alines.length; i++)
|
||||
{
|
||||
Changeset.eachAttribNumber(alines[i], function (n)
|
||||
Changeset.eachAttribNumber(alines[i], function(n)
|
||||
{
|
||||
if (!seenNums[n])
|
||||
{
|
||||
|
@ -246,7 +245,7 @@ function loadBroadcastJS()
|
|||
|
||||
function wrapRecordingErrors(catcher, func)
|
||||
{
|
||||
return function ()
|
||||
return function()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -273,7 +272,7 @@ function loadBroadcastJS()
|
|||
if (broadcasting) applyChangeset(changesetForward, revision + 1, false, timeDelta);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
At this point, we must be certain that the changeset really does map from
|
||||
the current revision to the specified revision. Any mistakes here will
|
||||
cause the whole slider to get out of sync.
|
||||
|
@ -303,7 +302,7 @@ function loadBroadcastJS()
|
|||
padContents.currentTime += timeDelta * 1000;
|
||||
debugLog('Time Delta: ', timeDelta)
|
||||
updateTimer();
|
||||
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function (name)
|
||||
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name)
|
||||
{
|
||||
return authorData[name];
|
||||
}));
|
||||
|
@ -311,7 +310,7 @@ function loadBroadcastJS()
|
|||
|
||||
function updateTimer()
|
||||
{
|
||||
var zpad = function (str, length)
|
||||
var zpad = function(str, length)
|
||||
{
|
||||
str = str + "";
|
||||
while (str.length < length)
|
||||
|
@ -319,8 +318,10 @@ function loadBroadcastJS()
|
|||
return str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var date = new Date(padContents.currentTime);
|
||||
var dateFormat = function ()
|
||||
var dateFormat = function()
|
||||
{
|
||||
var month = zpad(date.getMonth() + 1, 2);
|
||||
var day = zpad(date.getDate(), 2);
|
||||
|
@ -333,6 +334,8 @@ function loadBroadcastJS()
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
$('#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(" ")
|
||||
|
@ -365,7 +368,7 @@ function loadBroadcastJS()
|
|||
var sliderLocation = padContents.currentRevision;
|
||||
// callback is called after changeset information is pulled from server
|
||||
// this may never get called, if the changeset has already been loaded
|
||||
var update = function (start, end)
|
||||
var update = function(start, end)
|
||||
{
|
||||
// if we've called goToRevision in the time since, don't goToRevision
|
||||
goToRevision(padContents.targetRevision);
|
||||
|
@ -400,7 +403,7 @@ function loadBroadcastJS()
|
|||
|
||||
changesetLoader.queueUp(start, 1, update);
|
||||
}
|
||||
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function (name)
|
||||
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name)
|
||||
{
|
||||
return authorData[name];
|
||||
}));
|
||||
|
@ -413,7 +416,7 @@ function loadBroadcastJS()
|
|||
requestQueue2: [],
|
||||
requestQueue3: [],
|
||||
reqCallbacks: [],
|
||||
queueUp: function (revision, width, callback)
|
||||
queueUp: function(revision, width, callback)
|
||||
{
|
||||
if (revision < 0) revision = 0;
|
||||
// if(changesetLoader.requestQueue.indexOf(revision) != -1)
|
||||
|
@ -434,7 +437,7 @@ function loadBroadcastJS()
|
|||
setTimeout(changesetLoader.loadFromQueue, 10);
|
||||
}
|
||||
},
|
||||
loadFromQueue: function ()
|
||||
loadFromQueue: function()
|
||||
{
|
||||
var self = changesetLoader;
|
||||
var requestQueue = self.requestQueue1.length > 0 ? self.requestQueue1 : self.requestQueue2.length > 0 ? self.requestQueue2 : self.requestQueue3.length > 0 ? self.requestQueue3 : null;
|
||||
|
@ -449,9 +452,9 @@ function loadBroadcastJS()
|
|||
var granularity = request.res;
|
||||
var callback = request.callback;
|
||||
var start = request.rev;
|
||||
var requestID = Math.floor(Math.random()*100000);
|
||||
|
||||
/*var msg = { "component" : "timeslider",
|
||||
var requestID = Math.floor(Math.random() * 100000);
|
||||
|
||||
/*var msg = { "component" : "timeslider",
|
||||
"type":"CHANGESET_REQ",
|
||||
"padId": padId,
|
||||
"token": token,
|
||||
|
@ -463,12 +466,16 @@ function loadBroadcastJS()
|
|||
}};
|
||||
|
||||
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;
|
||||
|
||||
/*debugLog("loadinging revision", start, "through ajax");
|
||||
|
||||
/*debugLog("loadinging revision", start, "through ajax");
|
||||
$.getJSON("/ep/pad/changes/" + clientVars.padIdForUrl + "?s=" + start + "&g=" + granularity, function (data, textStatus)
|
||||
{
|
||||
if (textStatus !== "success")
|
||||
|
@ -481,19 +488,19 @@ function loadBroadcastJS()
|
|||
setTimeout(self.loadFromQueue, 10); // load the next ajax function
|
||||
});*/
|
||||
},
|
||||
handleSocketResponse: function (message)
|
||||
handleSocketResponse: function(message)
|
||||
{
|
||||
var self = changesetLoader;
|
||||
|
||||
var start = message.data.start;
|
||||
var granularity = message.data.granularity;
|
||||
var callback = self.reqCallbacks[message.data.requestID];
|
||||
delete self.reqCallbacks[message.data.requestID];
|
||||
|
||||
self.handleResponse(message.data, start, granularity, callback);
|
||||
setTimeout(self.loadFromQueue, 10);
|
||||
var self = changesetLoader;
|
||||
|
||||
var start = message.data.start;
|
||||
var granularity = message.data.granularity;
|
||||
var callback = self.reqCallbacks[message.data.requestID];
|
||||
delete self.reqCallbacks[message.data.requestID];
|
||||
|
||||
self.handleResponse(message.data, start, granularity, callback);
|
||||
setTimeout(self.loadFromQueue, 10);
|
||||
},
|
||||
handleResponse: function (data, start, granularity, callback)
|
||||
handleResponse: function(data, start, granularity, callback)
|
||||
{
|
||||
debugLog("response: ", data);
|
||||
var pool = (new AttribPool()).fromJsonable(data.apool);
|
||||
|
@ -538,7 +545,7 @@ function loadBroadcastJS()
|
|||
var authorMap = {};
|
||||
authorMap[obj.author] = obj.data;
|
||||
receiveAuthorData(authorMap);
|
||||
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function (name)
|
||||
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name)
|
||||
{
|
||||
return authorData[name];
|
||||
}));
|
||||
|
@ -593,7 +600,7 @@ function loadBroadcastJS()
|
|||
}));
|
||||
}
|
||||
|
||||
/*function setUpSocket()
|
||||
/*function setUpSocket()
|
||||
{
|
||||
// required for Comet
|
||||
if ((!$.browser.msie) && (!($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0)))
|
||||
|
@ -654,9 +661,9 @@ function loadBroadcastJS()
|
|||
{
|
||||
if (socket)
|
||||
{
|
||||
socket.onclosed = function ()
|
||||
socket.onclosed = function()
|
||||
{};
|
||||
socket.onhiccup = function ()
|
||||
socket.onhiccup = function()
|
||||
{};
|
||||
socket.disconnect();
|
||||
}
|
||||
|
@ -664,7 +671,7 @@ function loadBroadcastJS()
|
|||
setChannelState("DISCONNECTED", reason);
|
||||
}
|
||||
|
||||
/*window['onloadFuncts'] = [];
|
||||
/*window['onloadFuncts'] = [];
|
||||
window.onload = function ()
|
||||
{
|
||||
window['isloaded'] = true;
|
||||
|
@ -677,7 +684,7 @@ function loadBroadcastJS()
|
|||
// to start upon window load, just push a function onto this array
|
||||
//window['onloadFuncts'].push(setUpSocket);
|
||||
//window['onloadFuncts'].push(function ()
|
||||
fireWhenAllScriptsAreLoaded.push(function ()
|
||||
fireWhenAllScriptsAreLoaded.push(function()
|
||||
{
|
||||
// set up the currentDivs and DOM
|
||||
padContents.currentDivs = [];
|
||||
|
@ -694,7 +701,7 @@ function loadBroadcastJS()
|
|||
// this is necessary to keep infinite loops of events firing,
|
||||
// since goToRevision changes the slider position
|
||||
var goToRevisionIfEnabledCount = 0;
|
||||
var goToRevisionIfEnabled = function ()
|
||||
var goToRevisionIfEnabled = function()
|
||||
{
|
||||
if (goToRevisionIfEnabledCount > 0)
|
||||
{
|
||||
|
@ -708,9 +715,11 @@ function loadBroadcastJS()
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
BroadcastSlider.onSlider(goToRevisionIfEnabled);
|
||||
|
||||
(function ()
|
||||
(function()
|
||||
{
|
||||
for (var i = 0; i < clientVars.initialChangesets.length; i++)
|
||||
{
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -16,7 +16,6 @@
|
|||
// revision info is a skip list whos entries represent a particular revision
|
||||
// of the document. These revisions are connected together by various
|
||||
// changesets, or deltas, between any two revisions.
|
||||
|
||||
var global = this;
|
||||
|
||||
function loadBroadcastRevisionsJS()
|
||||
|
@ -27,25 +26,25 @@ function loadBroadcastRevisionsJS()
|
|||
this.changesets = [];
|
||||
}
|
||||
|
||||
Revision.prototype.addChangeset = function (destIndex, changeset, timeDelta)
|
||||
Revision.prototype.addChangeset = function(destIndex, changeset, timeDelta)
|
||||
{
|
||||
var changesetWrapper = {
|
||||
deltaRev: destIndex - this.rev,
|
||||
deltaTime: timeDelta,
|
||||
getValue: function ()
|
||||
getValue: function()
|
||||
{
|
||||
return changeset;
|
||||
}
|
||||
};
|
||||
this.changesets.push(changesetWrapper);
|
||||
this.changesets.sort(function (a, b)
|
||||
this.changesets.sort(function(a, b)
|
||||
{
|
||||
return (b.deltaRev - a.deltaRev)
|
||||
});
|
||||
}
|
||||
|
||||
revisionInfo = {};
|
||||
revisionInfo.addChangeset = function (fromIndex, toIndex, changeset, backChangeset, timeDelta)
|
||||
revisionInfo.addChangeset = function(fromIndex, toIndex, changeset, backChangeset, timeDelta)
|
||||
{
|
||||
var startRevision = revisionInfo[fromIndex] || revisionInfo.createNew(fromIndex);
|
||||
var endRevision = revisionInfo[toIndex] || revisionInfo.createNew(toIndex);
|
||||
|
@ -55,7 +54,7 @@ function loadBroadcastRevisionsJS()
|
|||
|
||||
revisionInfo.latest = clientVars.totalRevs || -1;
|
||||
|
||||
revisionInfo.createNew = function (index)
|
||||
revisionInfo.createNew = function(index)
|
||||
{
|
||||
revisionInfo[index] = new Revision(index);
|
||||
if (index > revisionInfo.latest)
|
||||
|
@ -68,7 +67,7 @@ function loadBroadcastRevisionsJS()
|
|||
|
||||
// assuming that there is a path from fromIndex to toIndex, and that the links
|
||||
// are laid out in a skip-list format
|
||||
revisionInfo.getPath = function (fromIndex, toIndex)
|
||||
revisionInfo.getPath = function(fromIndex, toIndex)
|
||||
{
|
||||
var changesets = [];
|
||||
var spans = [];
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -18,7 +18,7 @@ var global = this;
|
|||
function loadBroadcastSliderJS()
|
||||
{
|
||||
|
||||
(function ()
|
||||
(function()
|
||||
{ // wrap this code in its own namespace
|
||||
var sliderLength = 1000;
|
||||
var sliderPos = 0;
|
||||
|
@ -29,7 +29,7 @@ function loadBroadcastSliderJS()
|
|||
|
||||
function disableSelection(element)
|
||||
{
|
||||
element.onselectstart = function ()
|
||||
element.onselectstart = function()
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
@ -37,7 +37,7 @@ function loadBroadcastSliderJS()
|
|||
element.style.MozUserSelect = "none";
|
||||
element.style.cursor = "default";
|
||||
}
|
||||
var _callSliderCallbacks = function (newval)
|
||||
var _callSliderCallbacks = function(newval)
|
||||
{
|
||||
sliderPos = newval;
|
||||
for (var i = 0; i < slidercallbacks.length; i++)
|
||||
|
@ -48,7 +48,9 @@ function loadBroadcastSliderJS()
|
|||
|
||||
|
||||
|
||||
var updateSliderElements = function ()
|
||||
|
||||
|
||||
var updateSliderElements = function()
|
||||
{
|
||||
for (var i = 0; i < savedRevisions.length; i++)
|
||||
{
|
||||
|
@ -60,7 +62,9 @@ function loadBroadcastSliderJS()
|
|||
|
||||
|
||||
|
||||
var addSavedRevision = function (position, info)
|
||||
|
||||
|
||||
var addSavedRevision = function(position, info)
|
||||
{
|
||||
var newSavedRevision = $('<div></div>');
|
||||
newSavedRevision.addClass("star");
|
||||
|
@ -69,14 +73,14 @@ function loadBroadcastSliderJS()
|
|||
newSavedRevision.css('position', 'absolute');
|
||||
newSavedRevision.css('left', (position * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)) - 1);
|
||||
$("#timeslider-slider").append(newSavedRevision);
|
||||
newSavedRevision.mouseup(function (evt)
|
||||
newSavedRevision.mouseup(function(evt)
|
||||
{
|
||||
BroadcastSlider.setSliderPosition(position);
|
||||
});
|
||||
savedRevisions.push(newSavedRevision);
|
||||
};
|
||||
|
||||
var removeSavedRevision = function (position)
|
||||
var removeSavedRevision = function(position)
|
||||
{
|
||||
var element = $("div.star [pos=" + position + "]");
|
||||
savedRevisions.remove(element);
|
||||
|
@ -101,7 +105,7 @@ function loadBroadcastSliderJS()
|
|||
newpos = Number(newpos);
|
||||
if (newpos < 0 || newpos > sliderLength) return;
|
||||
$("#ui-slider-handle").css('left', newpos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0));
|
||||
$("a.tlink").map(function ()
|
||||
$("a.tlink").map(function()
|
||||
{
|
||||
$(this).attr('href', $(this).attr('thref').replace("%revision%", newpos));
|
||||
});
|
||||
|
@ -146,7 +150,6 @@ function loadBroadcastSliderJS()
|
|||
|
||||
// just take over the whole slider screen with a reconnect message
|
||||
|
||||
|
||||
function showReconnectUI()
|
||||
{
|
||||
if (!clientVars.sliderEnabled || !clientVars.supportsSlider)
|
||||
|
@ -162,7 +165,7 @@ function loadBroadcastSliderJS()
|
|||
$("#authorstable").empty();
|
||||
var numAnonymous = 0;
|
||||
var numNamed = 0;
|
||||
authors.forEach(function (author)
|
||||
authors.forEach(function(author)
|
||||
{
|
||||
if (author.name)
|
||||
{
|
||||
|
@ -200,7 +203,7 @@ function loadBroadcastSliderJS()
|
|||
setSliderPosition: setSliderPosition,
|
||||
getSliderLength: getSliderLength,
|
||||
setSliderLength: setSliderLength,
|
||||
isSliderActive: function ()
|
||||
isSliderActive: function()
|
||||
{
|
||||
return sliderActive;
|
||||
},
|
||||
|
@ -244,14 +247,14 @@ function loadBroadcastSliderJS()
|
|||
|
||||
// assign event handlers to html UI elements after page load
|
||||
//$(window).load(function ()
|
||||
fireWhenAllScriptsAreLoaded.push(function ()
|
||||
fireWhenAllScriptsAreLoaded.push(function()
|
||||
{
|
||||
disableSelection($("#playpause_button")[0]);
|
||||
disableSelection($("#timeslider")[0]);
|
||||
|
||||
if (clientVars.sliderEnabled && clientVars.supportsSlider)
|
||||
{
|
||||
$(document).keyup(function (e)
|
||||
$(document).keyup(function(e)
|
||||
{
|
||||
var code = -1;
|
||||
if (!e) var e = window.event;
|
||||
|
@ -297,12 +300,12 @@ function loadBroadcastSliderJS()
|
|||
});
|
||||
}
|
||||
|
||||
$(window).resize(function ()
|
||||
$(window).resize(function()
|
||||
{
|
||||
updateSliderElements();
|
||||
});
|
||||
|
||||
$("#ui-slider-bar").mousedown(function (evt)
|
||||
$("#ui-slider-bar").mousedown(function(evt)
|
||||
{
|
||||
setSliderPosition(Math.floor((evt.clientX - $("#ui-slider-bar").offset().left) * sliderLength / 742));
|
||||
$("#ui-slider-handle").css('left', (evt.clientX - $("#ui-slider-bar").offset().left));
|
||||
|
@ -310,13 +313,13 @@ function loadBroadcastSliderJS()
|
|||
});
|
||||
|
||||
// Slider dragging
|
||||
$("#ui-slider-handle").mousedown(function (evt)
|
||||
$("#ui-slider-handle").mousedown(function(evt)
|
||||
{
|
||||
this.startLoc = evt.clientX;
|
||||
this.currentLoc = parseInt($(this).css('left'));
|
||||
var self = this;
|
||||
sliderActive = true;
|
||||
$(document).mousemove(function (evt2)
|
||||
$(document).mousemove(function(evt2)
|
||||
{
|
||||
$(self).css('pointer', 'move')
|
||||
var newloc = self.currentLoc + (evt2.clientX - self.startLoc);
|
||||
|
@ -326,7 +329,7 @@ function loadBroadcastSliderJS()
|
|||
$(self).css('left', newloc);
|
||||
if (getSliderPosition() != Math.floor(newloc * sliderLength / ($("#ui-slider-bar").width() - 2))) _callSliderCallbacks(Math.floor(newloc * sliderLength / ($("#ui-slider-bar").width() - 2)))
|
||||
});
|
||||
$(document).mouseup(function (evt2)
|
||||
$(document).mouseup(function(evt2)
|
||||
{
|
||||
$(document).unbind('mousemove');
|
||||
$(document).unbind('mouseup');
|
||||
|
@ -342,18 +345,18 @@ function loadBroadcastSliderJS()
|
|||
})
|
||||
|
||||
// play/pause toggling
|
||||
$("#playpause_button").mousedown(function (evt)
|
||||
$("#playpause_button").mousedown(function(evt)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
$(self).css('background-image', 'url(/static/img/crushed_button_depressed.png)');
|
||||
$(self).mouseup(function (evt2)
|
||||
$(self).mouseup(function(evt2)
|
||||
{
|
||||
$(self).css('background-image', 'url(/static/img/crushed_button_undepressed.png)');
|
||||
$(self).unbind('mouseup');
|
||||
BroadcastSlider.playpause();
|
||||
});
|
||||
$(document).mouseup(function (evt2)
|
||||
$(document).mouseup(function(evt2)
|
||||
{
|
||||
$(self).css('background-image', 'url(/static/img/crushed_button_undepressed.png)');
|
||||
$(document).unbind('mouseup');
|
||||
|
@ -361,7 +364,7 @@ function loadBroadcastSliderJS()
|
|||
});
|
||||
|
||||
// next/prev saved revision and changeset
|
||||
$('.stepper').mousedown(function (evt)
|
||||
$('.stepper').mousedown(function(evt)
|
||||
{
|
||||
var self = this;
|
||||
var origcss = $(self).css('background-position');
|
||||
|
@ -378,7 +381,7 @@ function loadBroadcastSliderJS()
|
|||
|
||||
$(self).css('background-position', newcss)
|
||||
|
||||
$(self).mouseup(function (evt2)
|
||||
$(self).mouseup(function(evt2)
|
||||
{
|
||||
$(self).css('background-position', origcss);
|
||||
$(self).unbind('mouseup');
|
||||
|
@ -412,7 +415,7 @@ function loadBroadcastSliderJS()
|
|||
setSliderPosition(nextStar);
|
||||
}
|
||||
});
|
||||
$(document).mouseup(function (evt2)
|
||||
$(document).mouseup(function(evt2)
|
||||
{
|
||||
$(self).css('background-position', origcss);
|
||||
$(self).unbind('mouseup');
|
||||
|
@ -456,7 +459,7 @@ function loadBroadcastSliderJS()
|
|||
$("#timeslider").show();
|
||||
setSliderLength(clientVars.totalRevs);
|
||||
setSliderPosition(clientVars.revNum);
|
||||
clientVars.savedRevisions.forEach(function (revision)
|
||||
clientVars.savedRevisions.forEach(function(revision)
|
||||
{
|
||||
addSavedRevision(revision.revNum, revision);
|
||||
})
|
||||
|
@ -482,7 +485,7 @@ function loadBroadcastSliderJS()
|
|||
});
|
||||
})();
|
||||
|
||||
BroadcastSlider.onSlider(function (loc)
|
||||
BroadcastSlider.onSlider(function(loc)
|
||||
{
|
||||
$("#viewlatest").html(loc == BroadcastSlider.getSliderLength() ? "Viewing latest content" : "View latest content");
|
||||
})
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
|
||||
|
||||
function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) {
|
||||
function makeChangesetTracker(scheduler, apool, aceCallbacksProvider)
|
||||
{
|
||||
|
||||
// latest official text from server
|
||||
var baseAText = Changeset.makeAText("\n");
|
||||
|
@ -34,136 +35,168 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) {
|
|||
var changeCallback = null;
|
||||
|
||||
var changeCallbackTimeout = null;
|
||||
function setChangeCallbackTimeout() {
|
||||
|
||||
function setChangeCallbackTimeout()
|
||||
{
|
||||
// can call this multiple times per call-stack, because
|
||||
// we only schedule a call to changeCallback if it exists
|
||||
// and if there isn't a timeout already scheduled.
|
||||
if (changeCallback && changeCallbackTimeout === null) {
|
||||
changeCallbackTimeout = scheduler.setTimeout(function() {
|
||||
try {
|
||||
changeCallback();
|
||||
}
|
||||
finally {
|
||||
changeCallbackTimeout = null;
|
||||
}
|
||||
if (changeCallback && changeCallbackTimeout === null)
|
||||
{
|
||||
changeCallbackTimeout = scheduler.setTimeout(function()
|
||||
{
|
||||
try
|
||||
{
|
||||
changeCallback();
|
||||
}
|
||||
finally
|
||||
{
|
||||
changeCallbackTimeout = null;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
var self;
|
||||
return self = {
|
||||
isTracking: function() { return tracking; },
|
||||
setBaseText: function(text) {
|
||||
isTracking: function()
|
||||
{
|
||||
return tracking;
|
||||
},
|
||||
setBaseText: function(text)
|
||||
{
|
||||
self.setBaseAttributedText(Changeset.makeAText(text), null);
|
||||
},
|
||||
setBaseAttributedText: function(atext, apoolJsonObj) {
|
||||
aceCallbacksProvider.withCallbacks("setBaseText", function(callbacks) {
|
||||
setBaseAttributedText: function(atext, apoolJsonObj)
|
||||
{
|
||||
aceCallbacksProvider.withCallbacks("setBaseText", function(callbacks)
|
||||
{
|
||||
tracking = true;
|
||||
baseAText = Changeset.cloneAText(atext);
|
||||
if (apoolJsonObj) {
|
||||
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
||||
baseAText.attribs = Changeset.moveOpsToNewPool(baseAText.attribs, wireApool, apool);
|
||||
if (apoolJsonObj)
|
||||
{
|
||||
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
||||
baseAText.attribs = Changeset.moveOpsToNewPool(baseAText.attribs, wireApool, apool);
|
||||
}
|
||||
submittedChangeset = null;
|
||||
userChangeset = Changeset.identity(atext.text.length);
|
||||
applyingNonUserChanges = true;
|
||||
try {
|
||||
try
|
||||
{
|
||||
callbacks.setDocumentAttributedText(atext);
|
||||
}
|
||||
finally {
|
||||
applyingNonUserChanges = false;
|
||||
finally
|
||||
{
|
||||
applyingNonUserChanges = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
composeUserChangeset: function(c) {
|
||||
if (! tracking) return;
|
||||
composeUserChangeset: function(c)
|
||||
{
|
||||
if (!tracking) return;
|
||||
if (applyingNonUserChanges) return;
|
||||
if (Changeset.isIdentity(c)) return;
|
||||
userChangeset = Changeset.compose(userChangeset, c, apool);
|
||||
|
||||
setChangeCallbackTimeout();
|
||||
},
|
||||
applyChangesToBase: function (c, optAuthor, apoolJsonObj) {
|
||||
if (! tracking) return;
|
||||
applyChangesToBase: function(c, optAuthor, apoolJsonObj)
|
||||
{
|
||||
if (!tracking) return;
|
||||
|
||||
aceCallbacksProvider.withCallbacks("applyChangesToBase", function(callbacks) {
|
||||
aceCallbacksProvider.withCallbacks("applyChangesToBase", function(callbacks)
|
||||
{
|
||||
|
||||
if (apoolJsonObj) {
|
||||
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
||||
c = Changeset.moveOpsToNewPool(c, wireApool, apool);
|
||||
if (apoolJsonObj)
|
||||
{
|
||||
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
||||
c = Changeset.moveOpsToNewPool(c, wireApool, apool);
|
||||
}
|
||||
|
||||
baseAText = Changeset.applyToAText(c, baseAText, apool);
|
||||
|
||||
var c2 = c;
|
||||
if (submittedChangeset) {
|
||||
var oldSubmittedChangeset = submittedChangeset;
|
||||
submittedChangeset = Changeset.follow(c, oldSubmittedChangeset, false, apool);
|
||||
c2 = Changeset.follow(oldSubmittedChangeset, c, true, apool);
|
||||
if (submittedChangeset)
|
||||
{
|
||||
var oldSubmittedChangeset = submittedChangeset;
|
||||
submittedChangeset = Changeset.follow(c, oldSubmittedChangeset, false, apool);
|
||||
c2 = Changeset.follow(oldSubmittedChangeset, c, true, apool);
|
||||
}
|
||||
|
||||
var preferInsertingAfterUserChanges = true;
|
||||
var oldUserChangeset = userChangeset;
|
||||
userChangeset = Changeset.follow(c2, oldUserChangeset, preferInsertingAfterUserChanges, apool);
|
||||
var postChange =
|
||||
Changeset.follow(oldUserChangeset, c2, ! preferInsertingAfterUserChanges, apool);
|
||||
var postChange = Changeset.follow(oldUserChangeset, c2, !preferInsertingAfterUserChanges, apool);
|
||||
|
||||
var preferInsertionAfterCaret = true; //(optAuthor && optAuthor > thisAuthor);
|
||||
|
||||
applyingNonUserChanges = true;
|
||||
try {
|
||||
try
|
||||
{
|
||||
callbacks.applyChangesetToDocument(postChange, preferInsertionAfterCaret);
|
||||
}
|
||||
finally {
|
||||
applyingNonUserChanges = false;
|
||||
finally
|
||||
{
|
||||
applyingNonUserChanges = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
prepareUserChangeset: function() {
|
||||
prepareUserChangeset: function()
|
||||
{
|
||||
// If there are user changes to submit, 'changeset' will be the
|
||||
// changeset, else it will be null.
|
||||
var toSubmit;
|
||||
if (submittedChangeset) {
|
||||
// submission must have been canceled, prepare new changeset
|
||||
// that includes old submittedChangeset
|
||||
toSubmit = Changeset.compose(submittedChangeset, userChangeset, apool);
|
||||
if (submittedChangeset)
|
||||
{
|
||||
// submission must have been canceled, prepare new changeset
|
||||
// that includes old submittedChangeset
|
||||
toSubmit = Changeset.compose(submittedChangeset, userChangeset, apool);
|
||||
}
|
||||
else {
|
||||
if (Changeset.isIdentity(userChangeset)) toSubmit = null;
|
||||
else toSubmit = userChangeset;
|
||||
else
|
||||
{
|
||||
if (Changeset.isIdentity(userChangeset)) toSubmit = null;
|
||||
else toSubmit = userChangeset;
|
||||
}
|
||||
|
||||
var cs = null;
|
||||
if (toSubmit) {
|
||||
submittedChangeset = toSubmit;
|
||||
userChangeset = Changeset.identity(Changeset.newLen(toSubmit));
|
||||
if (toSubmit)
|
||||
{
|
||||
submittedChangeset = toSubmit;
|
||||
userChangeset = Changeset.identity(Changeset.newLen(toSubmit));
|
||||
|
||||
cs = toSubmit;
|
||||
cs = toSubmit;
|
||||
}
|
||||
var wireApool = null;
|
||||
if (cs) {
|
||||
var forWire = Changeset.prepareForWire(cs, apool);
|
||||
wireApool = forWire.pool.toJsonable();
|
||||
cs = forWire.translated;
|
||||
if (cs)
|
||||
{
|
||||
var forWire = Changeset.prepareForWire(cs, apool);
|
||||
wireApool = forWire.pool.toJsonable();
|
||||
cs = forWire.translated;
|
||||
}
|
||||
|
||||
var data = { changeset: cs, apool: wireApool };
|
||||
var data = {
|
||||
changeset: cs,
|
||||
apool: wireApool
|
||||
};
|
||||
return data;
|
||||
},
|
||||
applyPreparedChangesetToBase: function() {
|
||||
if (! submittedChangeset) {
|
||||
// violation of protocol; use prepareUserChangeset first
|
||||
throw new Error("applySubmittedChangesToBase: no submitted changes to apply");
|
||||
applyPreparedChangesetToBase: function()
|
||||
{
|
||||
if (!submittedChangeset)
|
||||
{
|
||||
// violation of protocol; use prepareUserChangeset first
|
||||
throw new Error("applySubmittedChangesToBase: no submitted changes to apply");
|
||||
}
|
||||
//bumpDebug("applying committed changeset: "+submittedChangeset.encodeToString(false));
|
||||
baseAText = Changeset.applyToAText(submittedChangeset, baseAText, apool);
|
||||
submittedChangeset = null;
|
||||
},
|
||||
setUserChangeNotificationCallback: function (callback) {
|
||||
setUserChangeNotificationCallback: function(callback)
|
||||
{
|
||||
changeCallback = callback;
|
||||
},
|
||||
hasUncommittedChanges: function() {
|
||||
return !!(submittedChangeset || (! Changeset.isIdentity(userChangeset)));
|
||||
hasUncommittedChanges: function()
|
||||
{
|
||||
return !!(submittedChangeset || (!Changeset.isIdentity(userChangeset)));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,5 @@
|
|||
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/colorutils.js
|
||||
// THIS FILE IS ALSO SERVED AS CLIENT-SIDE JS
|
||||
|
||||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
|
@ -20,73 +19,97 @@
|
|||
var colorutils = {};
|
||||
|
||||
// "#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);
|
||||
function hexToFloat(hh) {
|
||||
return Number("0x"+hh)/255;
|
||||
|
||||
function hexToFloat(hh)
|
||||
{
|
||||
return Number("0x" + hh) / 255;
|
||||
}
|
||||
return [hexToFloat(sixHex.substr(0,2)),
|
||||
hexToFloat(sixHex.substr(2,2)),
|
||||
hexToFloat(sixHex.substr(4,2))];
|
||||
return [hexToFloat(sixHex.substr(0, 2)), hexToFloat(sixHex.substr(2, 2)), hexToFloat(sixHex.substr(4, 2))];
|
||||
}
|
||||
|
||||
// "#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];
|
||||
if (h.length != 6) {
|
||||
if (h.length != 6)
|
||||
{
|
||||
var a = h.charAt(0);
|
||||
var b = h.charAt(1);
|
||||
var c = h.charAt(2);
|
||||
h = a+a+b+b+c+c;
|
||||
h = a + a + b + b + c + c;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
// [1.0, 1.0, 1.0] -> "#ffffff"
|
||||
colorutils.triple2css = function(triple) {
|
||||
function floatToHex(n) {
|
||||
var n2 = colorutils.clamp(Math.round(n*255), 0, 255);
|
||||
return ("0"+n2.toString(16)).slice(-2);
|
||||
colorutils.triple2css = function(triple)
|
||||
{
|
||||
function floatToHex(n)
|
||||
{
|
||||
var n2 = colorutils.clamp(Math.round(n * 255), 0, 255);
|
||||
return ("0" + n2.toString(16)).slice(-2);
|
||||
}
|
||||
return "#" + floatToHex(triple[0]) +
|
||||
floatToHex(triple[1]) + floatToHex(triple[2]);
|
||||
return "#" + floatToHex(triple[0]) + floatToHex(triple[1]) + floatToHex(triple[2]);
|
||||
}
|
||||
|
||||
|
||||
colorutils.clamp = function(v,bot,top) { return v < bot ? bot : (v > top ? top : v); };
|
||||
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); };
|
||||
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.clamp = function(v, bot, top)
|
||||
{
|
||||
return v < bot ? bot : (v > top ? top : v);
|
||||
};
|
||||
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);
|
||||
};
|
||||
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) {
|
||||
return [colorutils.scale(c[0], bot, top),
|
||||
colorutils.scale(c[1], bot, top),
|
||||
colorutils.scale(c[2], bot, top)];
|
||||
colorutils.scaleColor = function(c, bot, top)
|
||||
{
|
||||
return [colorutils.scale(c[0], bot, top), colorutils.scale(c[1], bot, top), colorutils.scale(c[2], bot, top)];
|
||||
}
|
||||
|
||||
colorutils.unscaleColor = function(c, bot, top) {
|
||||
return [colorutils.unscale(c[0], bot, top),
|
||||
colorutils.unscale(c[1], bot, top),
|
||||
colorutils.unscale(c[2], bot, top)];
|
||||
colorutils.unscaleColor = function(c, bot, top)
|
||||
{
|
||||
return [colorutils.unscale(c[0], bot, top), colorutils.unscale(c[1], 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
|
||||
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 max = colorutils.colorMax(c);
|
||||
if (max - min <= 0) return [1.0, 1.0, 1.0];
|
||||
return colorutils.unscaleColor(c, min, max);
|
||||
}
|
||||
|
||||
colorutils.blend = function(c1, c2, t) {
|
||||
return [colorutils.scale(t, c1[0], c2[0]),
|
||||
colorutils.scale(t, c1[1], c2[1]),
|
||||
colorutils.scale(t, c1[2], c2[2])];
|
||||
colorutils.blend = function(c1, c2, t)
|
||||
{
|
||||
return [colorutils.scale(t, c1[0], c2[0]), colorutils.scale(t, c1[1], c2[1]), colorutils.scale(t, c1[2], c2[2])];
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector
|
||||
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
|
||||
// %APPJET%: import("etherpad.admin.plugins");
|
||||
|
||||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
|
@ -20,88 +19,129 @@
|
|||
|
||||
var _MAX_LIST_LEVEL = 8;
|
||||
|
||||
function sanitizeUnicode(s) {
|
||||
function sanitizeUnicode(s)
|
||||
{
|
||||
return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?');
|
||||
}
|
||||
|
||||
function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||
className2Author) {
|
||||
function makeContentCollector(collectStyles, browser, apool, domInterface, className2Author)
|
||||
{
|
||||
browser = browser || {};
|
||||
|
||||
var plugins_;
|
||||
if (typeof(plugins)!='undefined') {
|
||||
if (typeof(plugins) != 'undefined')
|
||||
{
|
||||
plugins_ = plugins;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
plugins_ = parent.parent.plugins;
|
||||
}
|
||||
|
||||
var dom = domInterface || {
|
||||
isNodeText: function(n) {
|
||||
isNodeText: function(n)
|
||||
{
|
||||
return (n.nodeType == 3);
|
||||
},
|
||||
nodeTagName: function(n) {
|
||||
nodeTagName: function(n)
|
||||
{
|
||||
return n.tagName;
|
||||
},
|
||||
nodeValue: function(n) {
|
||||
nodeValue: function(n)
|
||||
{
|
||||
return n.nodeValue;
|
||||
},
|
||||
nodeNumChildren: function(n) {
|
||||
nodeNumChildren: function(n)
|
||||
{
|
||||
return n.childNodes.length;
|
||||
},
|
||||
nodeChild: function(n, i) {
|
||||
nodeChild: function(n, i)
|
||||
{
|
||||
return n.childNodes.item(i);
|
||||
},
|
||||
nodeProp: function(n, p) {
|
||||
nodeProp: function(n, p)
|
||||
{
|
||||
return n[p];
|
||||
},
|
||||
nodeAttr: function(n, a) {
|
||||
nodeAttr: function(n, a)
|
||||
{
|
||||
return n.getAttribute(a);
|
||||
},
|
||||
optNodeInnerHTML: function(n) {
|
||||
optNodeInnerHTML: function(n)
|
||||
{
|
||||
return n.innerHTML;
|
||||
}
|
||||
};
|
||||
|
||||
var _blockElems = { "div":1, "p":1, "pre":1, "li":1 };
|
||||
function isBlockElement(n) {
|
||||
var _blockElems = {
|
||||
"div": 1,
|
||||
"p": 1,
|
||||
"pre": 1,
|
||||
"li": 1
|
||||
};
|
||||
|
||||
function isBlockElement(n)
|
||||
{
|
||||
return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()];
|
||||
}
|
||||
function textify(str) {
|
||||
|
||||
function textify(str)
|
||||
{
|
||||
return sanitizeUnicode(
|
||||
str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
|
||||
}
|
||||
function getAssoc(node, name) {
|
||||
return dom.nodeProp(node, "_magicdom_"+name);
|
||||
str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
|
||||
}
|
||||
|
||||
var lines = (function() {
|
||||
function getAssoc(node, name)
|
||||
{
|
||||
return dom.nodeProp(node, "_magicdom_" + name);
|
||||
}
|
||||
|
||||
var lines = (function()
|
||||
{
|
||||
var textArray = [];
|
||||
var attribsArray = [];
|
||||
var attribsBuilder = null;
|
||||
var op = Changeset.newOp('+');
|
||||
var self = {
|
||||
length: function() { return textArray.length; },
|
||||
atColumnZero: function() {
|
||||
return textArray[textArray.length-1] === "";
|
||||
length: function()
|
||||
{
|
||||
return textArray.length;
|
||||
},
|
||||
startNew: function() {
|
||||
atColumnZero: function()
|
||||
{
|
||||
return textArray[textArray.length - 1] === "";
|
||||
},
|
||||
startNew: function()
|
||||
{
|
||||
textArray.push("");
|
||||
self.flush(true);
|
||||
attribsBuilder = Changeset.smartOpAssembler();
|
||||
},
|
||||
textOfLine: function(i) { return textArray[i]; },
|
||||
appendText: function(txt, attrString) {
|
||||
textArray[textArray.length-1] += txt;
|
||||
textOfLine: function(i)
|
||||
{
|
||||
return textArray[i];
|
||||
},
|
||||
appendText: function(txt, attrString)
|
||||
{
|
||||
textArray[textArray.length - 1] += txt;
|
||||
//dmesg(txt+" / "+attrString);
|
||||
op.attribs = attrString;
|
||||
op.chars = txt.length;
|
||||
attribsBuilder.append(op);
|
||||
},
|
||||
textLines: function() { return textArray.slice(); },
|
||||
attribLines: function() { return attribsArray; },
|
||||
textLines: function()
|
||||
{
|
||||
return textArray.slice();
|
||||
},
|
||||
attribLines: function()
|
||||
{
|
||||
return attribsArray;
|
||||
},
|
||||
// call flush only when you're done
|
||||
flush: function(withNewline) {
|
||||
if (attribsBuilder) {
|
||||
flush: function(withNewline)
|
||||
{
|
||||
if (attribsBuilder)
|
||||
{
|
||||
attribsArray.push(attribsBuilder.toString());
|
||||
attribsBuilder = null;
|
||||
}
|
||||
|
@ -111,21 +151,31 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
|||
return self;
|
||||
}());
|
||||
var cc = {};
|
||||
function _ensureColumnZero(state) {
|
||||
if (! lines.atColumnZero()) {
|
||||
|
||||
function _ensureColumnZero(state)
|
||||
{
|
||||
if (!lines.atColumnZero())
|
||||
{
|
||||
cc.startNewLine(state);
|
||||
}
|
||||
}
|
||||
var selection, startPoint, endPoint;
|
||||
var selStart = [-1,-1], selEnd = [-1,-1];
|
||||
var blockElems = { "div":1, "p":1, "pre":1 };
|
||||
function _isEmpty(node, state) {
|
||||
var selStart = [-1, -1],
|
||||
selEnd = [-1, -1];
|
||||
var blockElems = {
|
||||
"div": 1,
|
||||
"p": 1,
|
||||
"pre": 1
|
||||
};
|
||||
|
||||
function _isEmpty(node, state)
|
||||
{
|
||||
// consider clean blank lines pasted in IE to be empty
|
||||
if (dom.nodeNumChildren(node) == 0) return true;
|
||||
if (dom.nodeNumChildren(node) == 1 &&
|
||||
getAssoc(node, "shouldBeEmpty") && dom.optNodeInnerHTML(node) == " "
|
||||
&& ! getAssoc(node, "unpasted")) {
|
||||
if (state) {
|
||||
if (dom.nodeNumChildren(node) == 1 && getAssoc(node, "shouldBeEmpty") && dom.optNodeInnerHTML(node) == " " && !getAssoc(node, "unpasted"))
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
var child = dom.nodeChild(node, 0);
|
||||
_reachPoint(child, 0, state);
|
||||
_reachPoint(child, 1, state);
|
||||
|
@ -134,85 +184,116 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
function _pointHere(charsAfter, state) {
|
||||
var ln = lines.length()-1;
|
||||
|
||||
function _pointHere(charsAfter, state)
|
||||
{
|
||||
var ln = lines.length() - 1;
|
||||
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 += charsAfter;
|
||||
return [ln, chr];
|
||||
}
|
||||
function _reachBlockPoint(nd, idx, state) {
|
||||
if (! dom.isNodeText(nd)) _reachPoint(nd, idx, state);
|
||||
|
||||
function _reachBlockPoint(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);
|
||||
}
|
||||
if (endPoint && nd == endPoint.node && endPoint.index == idx) {
|
||||
if (endPoint && nd == endPoint.node && endPoint.index == idx)
|
||||
{
|
||||
selEnd = _pointHere(0, state);
|
||||
}
|
||||
}
|
||||
cc.incrementFlag = function(state, flagName) {
|
||||
state.flags[flagName] = (state.flags[flagName] || 0)+1;
|
||||
cc.incrementFlag = function(state, flagName)
|
||||
{
|
||||
state.flags[flagName] = (state.flags[flagName] || 0) + 1;
|
||||
}
|
||||
cc.decrementFlag = function(state, flagName) {
|
||||
cc.decrementFlag = function(state, flagName)
|
||||
{
|
||||
state.flags[flagName]--;
|
||||
}
|
||||
cc.incrementAttrib = function(state, attribName) {
|
||||
if (! state.attribs[attribName]) {
|
||||
cc.incrementAttrib = function(state, attribName)
|
||||
{
|
||||
if (!state.attribs[attribName])
|
||||
{
|
||||
state.attribs[attribName] = 1;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
state.attribs[attribName]++;
|
||||
}
|
||||
_recalcAttribString(state);
|
||||
}
|
||||
cc.decrementAttrib = function(state, attribName) {
|
||||
cc.decrementAttrib = function(state, attribName)
|
||||
{
|
||||
state.attribs[attribName]--;
|
||||
_recalcAttribString(state);
|
||||
}
|
||||
function _enterList(state, listType) {
|
||||
|
||||
function _enterList(state, listType)
|
||||
{
|
||||
var oldListType = state.listType;
|
||||
state.listLevel = (state.listLevel || 0)+1;
|
||||
if (listType != 'none') {
|
||||
state.listNesting = (state.listNesting || 0)+1;
|
||||
state.listLevel = (state.listLevel || 0) + 1;
|
||||
if (listType != 'none')
|
||||
{
|
||||
state.listNesting = (state.listNesting || 0) + 1;
|
||||
}
|
||||
state.listType = listType;
|
||||
_recalcAttribString(state);
|
||||
return oldListType;
|
||||
}
|
||||
function _exitList(state, oldListType) {
|
||||
|
||||
function _exitList(state, oldListType)
|
||||
{
|
||||
state.listLevel--;
|
||||
if (state.listType != 'none') {
|
||||
if (state.listType != 'none')
|
||||
{
|
||||
state.listNesting--;
|
||||
}
|
||||
state.listType = oldListType;
|
||||
_recalcAttribString(state);
|
||||
}
|
||||
function _enterAuthor(state, author) {
|
||||
|
||||
function _enterAuthor(state, author)
|
||||
{
|
||||
var oldAuthor = state.author;
|
||||
state.authorLevel = (state.authorLevel || 0)+1;
|
||||
state.authorLevel = (state.authorLevel || 0) + 1;
|
||||
state.author = author;
|
||||
_recalcAttribString(state);
|
||||
return oldAuthor;
|
||||
}
|
||||
function _exitAuthor(state, oldAuthor) {
|
||||
|
||||
function _exitAuthor(state, oldAuthor)
|
||||
{
|
||||
state.authorLevel--;
|
||||
state.author = oldAuthor;
|
||||
_recalcAttribString(state);
|
||||
}
|
||||
function _recalcAttribString(state) {
|
||||
|
||||
function _recalcAttribString(state)
|
||||
{
|
||||
var lst = [];
|
||||
for(var a in state.attribs) {
|
||||
if (state.attribs[a]) {
|
||||
lst.push([a,'true']);
|
||||
for (var a in state.attribs)
|
||||
{
|
||||
if (state.attribs[a])
|
||||
{
|
||||
lst.push([a, 'true']);
|
||||
}
|
||||
}
|
||||
if (state.authorLevel > 0) {
|
||||
if (state.authorLevel > 0)
|
||||
{
|
||||
var authorAttrib = ['author', state.author];
|
||||
if (apool.putAttrib(authorAttrib, true) >= 0) {
|
||||
if (apool.putAttrib(authorAttrib, true) >= 0)
|
||||
{
|
||||
// require that author already be in pool
|
||||
// (don't add authors from other documents, etc.)
|
||||
lst.push(authorAttrib);
|
||||
|
@ -220,155 +301,197 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
|||
}
|
||||
state.attribString = Changeset.makeAttribsString('+', lst, apool);
|
||||
}
|
||||
function _produceListMarker(state) {
|
||||
lines.appendText('*', Changeset.makeAttribsString(
|
||||
'+', [['list', state.listType],
|
||||
['insertorder', 'first']],
|
||||
apool));
|
||||
|
||||
function _produceListMarker(state)
|
||||
{
|
||||
lines.appendText('*', Changeset.makeAttribsString('+', [
|
||||
['list', state.listType],
|
||||
['insertorder', 'first']
|
||||
], apool));
|
||||
}
|
||||
cc.startNewLine = function(state) {
|
||||
if (state) {
|
||||
var atBeginningOfLine = lines.textOfLine(lines.length()-1).length == 0;
|
||||
if (atBeginningOfLine && state.listType && state.listType != 'none') {
|
||||
cc.startNewLine = function(state)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
|
||||
if (atBeginningOfLine && state.listType && state.listType != 'none')
|
||||
{
|
||||
_produceListMarker(state);
|
||||
}
|
||||
}
|
||||
lines.startNew();
|
||||
}
|
||||
cc.notifySelection = function (sel) {
|
||||
if (sel) {
|
||||
cc.notifySelection = function(sel)
|
||||
{
|
||||
if (sel)
|
||||
{
|
||||
selection = sel;
|
||||
startPoint = selection.startPoint;
|
||||
endPoint = selection.endPoint;
|
||||
}
|
||||
};
|
||||
cc.doAttrib = function(state, na) {
|
||||
cc.doAttrib = function(state, na)
|
||||
{
|
||||
state.localAttribs = (state.localAttribs || []);
|
||||
state.localAttribs.push(na);
|
||||
cc.incrementAttrib(state, na);
|
||||
};
|
||||
cc.collectContent = function (node, state) {
|
||||
if (! state) {
|
||||
state = {flags: {/*name -> nesting counter*/},
|
||||
localAttribs: null,
|
||||
attribs: {/*name -> nesting counter*/},
|
||||
attribString: ''};
|
||||
cc.collectContent = function(node, state)
|
||||
{
|
||||
if (!state)
|
||||
{
|
||||
state = {
|
||||
flags: { /*name -> nesting counter*/
|
||||
},
|
||||
localAttribs: null,
|
||||
attribs: { /*name -> nesting counter*/
|
||||
},
|
||||
attribString: ''
|
||||
};
|
||||
}
|
||||
var localAttribs = state.localAttribs;
|
||||
state.localAttribs = null;
|
||||
var isBlock = isBlockElement(node);
|
||||
var isEmpty = _isEmpty(node, state);
|
||||
if (isBlock) _ensureColumnZero(state);
|
||||
var startLine = lines.length()-1;
|
||||
var startLine = lines.length() - 1;
|
||||
_reachBlockPoint(node, 0, state);
|
||||
if (dom.isNodeText(node)) {
|
||||
if (dom.isNodeText(node))
|
||||
{
|
||||
var txt = dom.nodeValue(node);
|
||||
var rest = '';
|
||||
var x = 0; // offset into original text
|
||||
if (txt.length == 0) {
|
||||
if (startPoint && node == startPoint.node) {
|
||||
if (txt.length == 0)
|
||||
{
|
||||
if (startPoint && node == startPoint.node)
|
||||
{
|
||||
selStart = _pointHere(0, state);
|
||||
}
|
||||
if (endPoint && node == endPoint.node) {
|
||||
if (endPoint && node == endPoint.node)
|
||||
{
|
||||
selEnd = _pointHere(0, state);
|
||||
}
|
||||
}
|
||||
while (txt.length > 0) {
|
||||
while (txt.length > 0)
|
||||
{
|
||||
var consumed = 0;
|
||||
if (state.flags.preMode) {
|
||||
var firstLine = txt.split('\n',1)[0];
|
||||
consumed = firstLine.length+1;
|
||||
if (state.flags.preMode)
|
||||
{
|
||||
var firstLine = txt.split('\n', 1)[0];
|
||||
consumed = firstLine.length + 1;
|
||||
rest = txt.substring(consumed);
|
||||
txt = firstLine;
|
||||
}
|
||||
else { /* will only run this loop body once */ }
|
||||
if (startPoint && node == startPoint.node &&
|
||||
startPoint.index-x <= txt.length) {
|
||||
selStart = _pointHere(startPoint.index-x, state);
|
||||
else
|
||||
{ /* will only run this loop body once */
|
||||
}
|
||||
if (endPoint && node == endPoint.node &&
|
||||
endPoint.index-x <= txt.length) {
|
||||
selEnd = _pointHere(endPoint.index-x, state);
|
||||
if (startPoint && node == startPoint.node && startPoint.index - x <= txt.length)
|
||||
{
|
||||
selStart = _pointHere(startPoint.index - x, state);
|
||||
}
|
||||
if (endPoint && node == endPoint.node && endPoint.index - x <= txt.length)
|
||||
{
|
||||
selEnd = _pointHere(endPoint.index - x, state);
|
||||
}
|
||||
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
|
||||
// in safari when pasting text, now that we convert them to
|
||||
// spaces instead of removing them, because in other cases
|
||||
// removing "\n" from pasted HTML will collapse words together.
|
||||
txt2 = "";
|
||||
}
|
||||
var atBeginningOfLine = lines.textOfLine(lines.length()-1).length == 0;
|
||||
if (atBeginningOfLine) {
|
||||
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
|
||||
if (atBeginningOfLine)
|
||||
{
|
||||
// newlines in the source mustn't become spaces at beginning of line box
|
||||
txt2 = txt2.replace(/^\n*/, '');
|
||||
}
|
||||
if (atBeginningOfLine && state.listType && state.listType != 'none') {
|
||||
if (atBeginningOfLine && state.listType && state.listType != 'none')
|
||||
{
|
||||
_produceListMarker(state);
|
||||
}
|
||||
lines.appendText(textify(txt2), state.attribString);
|
||||
x += consumed;
|
||||
txt = rest;
|
||||
if (txt.length > 0) {
|
||||
if (txt.length > 0)
|
||||
{
|
||||
cc.startNewLine(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
var tname = (dom.nodeTagName(node) || "").toLowerCase();
|
||||
if (tname == "br") {
|
||||
if (tname == "br")
|
||||
{
|
||||
cc.startNewLine(state);
|
||||
}
|
||||
else if (tname == "script" || tname == "style") {
|
||||
else if (tname == "script" || tname == "style")
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
else if (! isEmpty) {
|
||||
else if (!isEmpty)
|
||||
{
|
||||
var styl = dom.nodeAttr(node, "style");
|
||||
var cls = dom.nodeProp(node, "className");
|
||||
|
||||
var isPre = (tname == "pre");
|
||||
if ((! isPre) && browser.safari) {
|
||||
if ((!isPre) && browser.safari)
|
||||
{
|
||||
isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl));
|
||||
}
|
||||
if (isPre) cc.incrementFlag(state, 'preMode');
|
||||
var oldListTypeOrNull = null;
|
||||
var oldAuthorOrNull = null;
|
||||
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)) ||
|
||||
tname == "strong") {
|
||||
cc.doAttrib(state, "bold");
|
||||
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)) || tname == "strong")
|
||||
{
|
||||
cc.doAttrib(state, "bold");
|
||||
}
|
||||
if (tname == "i" || (styl && /\bfont-style:\s*italic\b/i.exec(styl)) ||
|
||||
tname == "em") {
|
||||
cc.doAttrib(state, "italic");
|
||||
if (tname == "i" || (styl && /\bfont-style:\s*italic\b/i.exec(styl)) || tname == "em")
|
||||
{
|
||||
cc.doAttrib(state, "italic");
|
||||
}
|
||||
if (tname == "u" || (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) ||
|
||||
tname == "ins") {
|
||||
cc.doAttrib(state, "underline");
|
||||
if (tname == "u" || (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) || tname == "ins")
|
||||
{
|
||||
cc.doAttrib(state, "underline");
|
||||
}
|
||||
if (tname == "s" || (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) ||
|
||||
tname == "del") {
|
||||
cc.doAttrib(state, "strikethrough");
|
||||
if (tname == "s" || (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) || tname == "del")
|
||||
{
|
||||
cc.doAttrib(state, "strikethrough");
|
||||
}
|
||||
if (tname == "ul") {
|
||||
if (tname == "ul")
|
||||
{
|
||||
var type;
|
||||
var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls);
|
||||
type = rr && rr[1] || "bullet"+
|
||||
String(Math.min(_MAX_LIST_LEVEL, (state.listNesting||0)+1));
|
||||
var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls);
|
||||
type = rr && rr[1] || "bullet" + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1));
|
||||
oldListTypeOrNull = (_enterList(state, type) || 'none');
|
||||
}
|
||||
else if ((tname == "div" || tname == "p") && cls &&
|
||||
cls.match(/(?:^| )ace-line\b/)) {
|
||||
else if ((tname == "div" || tname == "p") && cls && cls.match(/(?:^| )ace-line\b/))
|
||||
{
|
||||
oldListTypeOrNull = (_enterList(state, type) || 'none');
|
||||
}
|
||||
if (className2Author && cls) {
|
||||
if (className2Author && cls)
|
||||
{
|
||||
var classes = cls.match(/\S+/g);
|
||||
if (classes && classes.length > 0) {
|
||||
for(var i=0;i<classes.length;i++) {
|
||||
if (classes && classes.length > 0)
|
||||
{
|
||||
for (var i = 0; i < classes.length; i++)
|
||||
{
|
||||
var c = classes[i];
|
||||
var a = className2Author(c);
|
||||
if (a) {
|
||||
if (a)
|
||||
{
|
||||
oldAuthorOrNull = (_enterAuthor(state, a) || 'none');
|
||||
break;
|
||||
}
|
||||
|
@ -378,42 +501,59 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
|||
}
|
||||
|
||||
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);
|
||||
cc.collectContent(c, state);
|
||||
}
|
||||
|
||||
if (collectStyles) {
|
||||
plugins_.callHook('collectContentPost', {cc: cc, state:state, tname:tname, styl:styl, cls:cls});
|
||||
if (collectStyles)
|
||||
{
|
||||
plugins_.callHook('collectContentPost', {
|
||||
cc: cc,
|
||||
state: state,
|
||||
tname: tname,
|
||||
styl: styl,
|
||||
cls: cls
|
||||
});
|
||||
}
|
||||
|
||||
if (isPre) cc.decrementFlag(state, 'preMode');
|
||||
if (state.localAttribs) {
|
||||
for(var i=0;i<state.localAttribs.length;i++) {
|
||||
if (state.localAttribs)
|
||||
{
|
||||
for (var i = 0; i < state.localAttribs.length; i++)
|
||||
{
|
||||
cc.decrementAttrib(state, state.localAttribs[i]);
|
||||
}
|
||||
}
|
||||
if (oldListTypeOrNull) {
|
||||
if (oldListTypeOrNull)
|
||||
{
|
||||
_exitList(state, oldListTypeOrNull);
|
||||
}
|
||||
if (oldAuthorOrNull) {
|
||||
if (oldAuthorOrNull)
|
||||
{
|
||||
_exitAuthor(state, oldAuthorOrNull);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! browser.msie) {
|
||||
if (!browser.msie)
|
||||
{
|
||||
_reachBlockPoint(node, 1, state);
|
||||
}
|
||||
if (isBlock) {
|
||||
if (lines.length()-1 == startLine) {
|
||||
if (isBlock)
|
||||
{
|
||||
if (lines.length() - 1 == startLine)
|
||||
{
|
||||
cc.startNewLine(state);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
_ensureColumnZero(state);
|
||||
}
|
||||
}
|
||||
|
||||
if (browser.msie) {
|
||||
if (browser.msie)
|
||||
{
|
||||
// in IE, a point immediately after a DIV appears on the next line
|
||||
_reachBlockPoint(node, 1, state);
|
||||
}
|
||||
|
@ -421,26 +561,38 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
|||
state.localAttribs = localAttribs;
|
||||
};
|
||||
// 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
|
||||
// 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.
|
||||
// it is incorporated as dirty by the rule that a dirty region has
|
||||
// to end a line.
|
||||
if ((!node) || (isBlockElement(node) && !_isEmpty(node))) {
|
||||
if ((!node) || (isBlockElement(node) && !_isEmpty(node)))
|
||||
{
|
||||
_ensureColumnZero(null);
|
||||
}
|
||||
};
|
||||
// each returns [line, char] or [-1,-1]
|
||||
var getSelectionStart = function() { return selStart; };
|
||||
var getSelectionEnd = function() { return selEnd; };
|
||||
var getSelectionStart = function()
|
||||
{
|
||||
return selStart;
|
||||
};
|
||||
var getSelectionEnd = function()
|
||||
{
|
||||
return selEnd;
|
||||
};
|
||||
|
||||
// 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).
|
||||
// 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();
|
||||
var lineAttribs = lines.attribLines();
|
||||
var lineStrings = cc.getLines();
|
||||
|
@ -451,69 +603,85 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
|||
var ss = getSelectionStart();
|
||||
var se = getSelectionEnd();
|
||||
|
||||
function fixLongLines() {
|
||||
function fixLongLines()
|
||||
{
|
||||
// design mode does not deal with with really long lines!
|
||||
var lineLimit = 2000; // chars
|
||||
var buffer = 10; // chars allowed over before wrapping
|
||||
var linesWrapped = 0;
|
||||
var numLinesAfter = 0;
|
||||
for(var i=lineStrings.length-1; i>=0; i--) {
|
||||
var oldString = lineStrings[i];
|
||||
var oldAttribString = lineAttribs[i];
|
||||
if (oldString.length > lineLimit+buffer) {
|
||||
var newStrings = [];
|
||||
var newAttribStrings = [];
|
||||
while (oldString.length > lineLimit) {
|
||||
//var semiloc = oldString.lastIndexOf(';', lineLimit-1);
|
||||
//var lengthToTake = (semiloc >= 0 ? (semiloc+1) : lineLimit);
|
||||
lengthToTake = lineLimit;
|
||||
newStrings.push(oldString.substring(0, lengthToTake));
|
||||
oldString = oldString.substring(lengthToTake);
|
||||
newAttribStrings.push(Changeset.subattribution(oldAttribString,
|
||||
0, lengthToTake));
|
||||
oldAttribString = Changeset.subattribution(oldAttribString,
|
||||
lengthToTake);
|
||||
}
|
||||
if (oldString.length > 0) {
|
||||
newStrings.push(oldString);
|
||||
newAttribStrings.push(oldAttribString);
|
||||
}
|
||||
function fixLineNumber(lineChar) {
|
||||
if (lineChar[0] < 0) return;
|
||||
var n = lineChar[0];
|
||||
var c = lineChar[1];
|
||||
if (n > i) {
|
||||
n += (newStrings.length-1);
|
||||
}
|
||||
else if (n == i) {
|
||||
var a = 0;
|
||||
while (c > newStrings[a].length) {
|
||||
c -= newStrings[a].length;
|
||||
a++;
|
||||
}
|
||||
n += a;
|
||||
}
|
||||
lineChar[0] = n;
|
||||
lineChar[1] = c;
|
||||
}
|
||||
fixLineNumber(ss);
|
||||
fixLineNumber(se);
|
||||
linesWrapped++;
|
||||
numLinesAfter += newStrings.length;
|
||||
for (var i = lineStrings.length - 1; i >= 0; i--)
|
||||
{
|
||||
var oldString = lineStrings[i];
|
||||
var oldAttribString = lineAttribs[i];
|
||||
if (oldString.length > lineLimit + buffer)
|
||||
{
|
||||
var newStrings = [];
|
||||
var newAttribStrings = [];
|
||||
while (oldString.length > lineLimit)
|
||||
{
|
||||
//var semiloc = oldString.lastIndexOf(';', lineLimit-1);
|
||||
//var lengthToTake = (semiloc >= 0 ? (semiloc+1) : lineLimit);
|
||||
lengthToTake = lineLimit;
|
||||
newStrings.push(oldString.substring(0, lengthToTake));
|
||||
oldString = oldString.substring(lengthToTake);
|
||||
newAttribStrings.push(Changeset.subattribution(oldAttribString, 0, lengthToTake));
|
||||
oldAttribString = Changeset.subattribution(oldAttribString, lengthToTake);
|
||||
}
|
||||
if (oldString.length > 0)
|
||||
{
|
||||
newStrings.push(oldString);
|
||||
newAttribStrings.push(oldAttribString);
|
||||
}
|
||||
|
||||
newStrings.unshift(i, 1);
|
||||
lineStrings.splice.apply(lineStrings, newStrings);
|
||||
newAttribStrings.unshift(i, 1);
|
||||
lineAttribs.splice.apply(lineAttribs, newAttribStrings);
|
||||
}
|
||||
function fixLineNumber(lineChar)
|
||||
{
|
||||
if (lineChar[0] < 0) return;
|
||||
var n = lineChar[0];
|
||||
var c = lineChar[1];
|
||||
if (n > i)
|
||||
{
|
||||
n += (newStrings.length - 1);
|
||||
}
|
||||
else if (n == i)
|
||||
{
|
||||
var a = 0;
|
||||
while (c > newStrings[a].length)
|
||||
{
|
||||
c -= newStrings[a].length;
|
||||
a++;
|
||||
}
|
||||
n += a;
|
||||
}
|
||||
lineChar[0] = n;
|
||||
lineChar[1] = c;
|
||||
}
|
||||
fixLineNumber(ss);
|
||||
fixLineNumber(se);
|
||||
linesWrapped++;
|
||||
numLinesAfter += newStrings.length;
|
||||
|
||||
newStrings.unshift(i, 1);
|
||||
lineStrings.splice.apply(lineStrings, newStrings);
|
||||
newAttribStrings.unshift(i, 1);
|
||||
lineAttribs.splice.apply(lineAttribs, newAttribStrings);
|
||||
}
|
||||
}
|
||||
return {linesWrapped:linesWrapped, numLinesAfter:numLinesAfter};
|
||||
return {
|
||||
linesWrapped: linesWrapped,
|
||||
numLinesAfter: numLinesAfter
|
||||
};
|
||||
}
|
||||
var wrapData = fixLongLines();
|
||||
|
||||
return { selStart: ss, selEnd: se, linesWrapped: wrapData.linesWrapped,
|
||||
numLinesAfter: wrapData.numLinesAfter,
|
||||
lines: lineStrings, lineAttribs: lineAttribs };
|
||||
return {
|
||||
selStart: ss,
|
||||
selEnd: se,
|
||||
linesWrapped: wrapData.linesWrapped,
|
||||
numLinesAfter: wrapData.numLinesAfter,
|
||||
lines: lineStrings,
|
||||
lineAttribs: lineAttribs
|
||||
};
|
||||
}
|
||||
|
||||
return cc;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
|
@ -16,20 +14,24 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function makeCSSManager(emptyStylesheetTitle) {
|
||||
function makeCSSManager(emptyStylesheetTitle)
|
||||
{
|
||||
|
||||
function getSheetByTitle(title) {
|
||||
function getSheetByTitle(title)
|
||||
{
|
||||
var allSheets = document.styleSheets;
|
||||
for(var i=0;i<allSheets.length;i++) {
|
||||
for (var i = 0; i < allSheets.length; i++)
|
||||
{
|
||||
var s = allSheets[i];
|
||||
if (s.title == title) {
|
||||
return s;
|
||||
if (s.title == title)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*function getSheetTagByTitle(title) {
|
||||
/*function getSheetTagByTitle(title) {
|
||||
var allStyleTags = document.getElementsByTagName("style");
|
||||
for(var i=0;i<allStyleTags.length;i++) {
|
||||
var t = allStyleTags[i];
|
||||
|
@ -42,29 +44,43 @@ function makeCSSManager(emptyStylesheetTitle) {
|
|||
|
||||
var browserSheet = getSheetByTitle(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);
|
||||
else browserSheet.removeRule(i);
|
||||
}
|
||||
function browserInsertRule(i, selector) {
|
||||
if (browserSheet.insertRule) browserSheet.insertRule(selector+' {}', i);
|
||||
|
||||
function browserInsertRule(i, selector)
|
||||
{
|
||||
if (browserSheet.insertRule) browserSheet.insertRule(selector + ' {}', i);
|
||||
else browserSheet.addRule(selector, null, i);
|
||||
}
|
||||
var selectorList = [];
|
||||
|
||||
function indexOfSelector(selector) {
|
||||
for(var i=0;i<selectorList.length;i++) {
|
||||
if (selectorList[i] == selector) {
|
||||
return i;
|
||||
function indexOfSelector(selector)
|
||||
{
|
||||
for (var i = 0; i < selectorList.length; i++)
|
||||
{
|
||||
if (selectorList[i] == selector)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function selectorStyle(selector) {
|
||||
function selectorStyle(selector)
|
||||
{
|
||||
var i = indexOfSelector(selector);
|
||||
if (i < 0) {
|
||||
if (i < 0)
|
||||
{
|
||||
// add selector
|
||||
browserInsertRule(0, selector);
|
||||
selectorList.splice(0, 0, selector);
|
||||
|
@ -73,16 +89,22 @@ function makeCSSManager(emptyStylesheetTitle) {
|
|||
return browserRules().item(i).style;
|
||||
}
|
||||
|
||||
function removeSelectorStyle(selector) {
|
||||
function removeSelectorStyle(selector)
|
||||
{
|
||||
var i = indexOfSelector(selector);
|
||||
if (i >= 0) {
|
||||
if (i >= 0)
|
||||
{
|
||||
browserDeleteRule(i);
|
||||
selectorList.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {selectorStyle:selectorStyle, removeSelectorStyle:removeSelectorStyle,
|
||||
info: function() {
|
||||
return selectorList.length+":"+browserRules().length;
|
||||
}};
|
||||
return {
|
||||
selectorStyle: selectorStyle,
|
||||
removeSelectorStyle: removeSelectorStyle,
|
||||
info: function()
|
||||
{
|
||||
return selectorList.length + ":" + browserRules().length;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ function makeCSSManager(emptyStylesheetTitle)
|
|||
var browserSheet = getSheetByTitle(emptyStylesheetTitle);
|
||||
//var browserTag = getSheetTagByTitle(emptyStylesheetTitle);
|
||||
|
||||
|
||||
function browserRules()
|
||||
{
|
||||
return (browserSheet.cssRules || browserSheet.rules);
|
||||
|
@ -103,7 +102,7 @@ function makeCSSManager(emptyStylesheetTitle)
|
|||
return {
|
||||
selectorStyle: selectorStyle,
|
||||
removeSelectorStyle: removeSelectorStyle,
|
||||
info: function ()
|
||||
info: function()
|
||||
{
|
||||
return selectorList.length + ":" + browserRules().length;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline
|
||||
// %APPJET%: import("etherpad.admin.plugins");
|
||||
|
||||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
|
@ -20,19 +19,25 @@
|
|||
// requires: top
|
||||
// requires: plugins
|
||||
// requires: undefined
|
||||
|
||||
var domline = {};
|
||||
domline.noop = function() {};
|
||||
domline.identity = function(x) { return x; };
|
||||
domline.noop = function()
|
||||
{};
|
||||
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
|
||||
// the line, using line:className. otherwise, we ignore
|
||||
// the span.
|
||||
cls.replace(/\S+/g, function (c) {
|
||||
if (c.indexOf("line:") == 0) {
|
||||
cls.replace(/\S+/g, function(c)
|
||||
{
|
||||
if (c.indexOf("line:") == 0)
|
||||
{
|
||||
// add class to line
|
||||
lineClass = (lineClass ? lineClass+' ' : '')+c.substring(5);
|
||||
lineClass = (lineClass ? lineClass + ' ' : '') + c.substring(5);
|
||||
}
|
||||
});
|
||||
return lineClass;
|
||||
|
@ -40,42 +45,56 @@ domline.addToLineClass = function(lineClass, cls) {
|
|||
|
||||
// if "document" is falsy we don't create a DOM node, just
|
||||
// an object with innerHTML and className
|
||||
domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||
var result = { node: null,
|
||||
appendSpan: domline.noop,
|
||||
prepareForAdd: domline.noop,
|
||||
notifyAdded: domline.noop,
|
||||
clearSpans: domline.noop,
|
||||
finishUpdate: domline.noop,
|
||||
lineMarker: 0 };
|
||||
domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
|
||||
{
|
||||
var result = {
|
||||
node: null,
|
||||
appendSpan: domline.noop,
|
||||
prepareForAdd: domline.noop,
|
||||
notifyAdded: domline.noop,
|
||||
clearSpans: domline.noop,
|
||||
finishUpdate: domline.noop,
|
||||
lineMarker: 0
|
||||
};
|
||||
|
||||
var browser = (optBrowser || {});
|
||||
var document = optDocument;
|
||||
|
||||
if (document) {
|
||||
if (document)
|
||||
{
|
||||
result.node = document.createElement("div");
|
||||
}
|
||||
else {
|
||||
result.node = {innerHTML: '', className: ''};
|
||||
else
|
||||
{
|
||||
result.node = {
|
||||
innerHTML: '',
|
||||
className: ''
|
||||
};
|
||||
}
|
||||
|
||||
var html = [];
|
||||
var preHtml, postHtml;
|
||||
var curHTML = null;
|
||||
function processSpaces(s) {
|
||||
|
||||
function processSpaces(s)
|
||||
{
|
||||
return domline.processSpaces(s, doesWrap);
|
||||
}
|
||||
var identity = domline.identity;
|
||||
var perTextNodeProcess = (doesWrap ? identity : processSpaces);
|
||||
var perHtmlLineProcess = (doesWrap ? processSpaces : identity);
|
||||
var lineClass = 'ace-line';
|
||||
result.appendSpan = function(txt, cls) {
|
||||
if (cls.indexOf('list') >= 0) {
|
||||
result.appendSpan = function(txt, cls)
|
||||
{
|
||||
if (cls.indexOf('list') >= 0)
|
||||
{
|
||||
var listType = /(?:^| )list:(\S+)/.exec(cls);
|
||||
if (listType) {
|
||||
if (listType)
|
||||
{
|
||||
listType = listType[1];
|
||||
if (listType) {
|
||||
preHtml = '<ul class="list-'+listType+'"><li>';
|
||||
if (listType)
|
||||
{
|
||||
preHtml = '<ul class="list-' + listType + '"><li>';
|
||||
postHtml = '</li></ul>';
|
||||
}
|
||||
result.lineMarker += txt.length;
|
||||
|
@ -84,17 +103,21 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) {
|
|||
}
|
||||
var href = null;
|
||||
var simpleTags = null;
|
||||
if (cls.indexOf('url') >= 0) {
|
||||
cls = cls.replace(/(^| )url:(\S+)/g, function(x0, space, url) {
|
||||
href = url;
|
||||
return space+"url";
|
||||
if (cls.indexOf('url') >= 0)
|
||||
{
|
||||
cls = cls.replace(/(^| )url:(\S+)/g, function(x0, space, url)
|
||||
{
|
||||
href = url;
|
||||
return space + "url";
|
||||
});
|
||||
}
|
||||
if (cls.indexOf('tag') >= 0) {
|
||||
cls = cls.replace(/(^| )tag:(\S+)/g, function(x0, space, tag) {
|
||||
if (! simpleTags) simpleTags = [];
|
||||
simpleTags.push(tag.toLowerCase());
|
||||
return space+tag;
|
||||
if (cls.indexOf('tag') >= 0)
|
||||
{
|
||||
cls = cls.replace(/(^| )tag:(\S+)/g, function(x0, space, tag)
|
||||
{
|
||||
if (!simpleTags) simpleTags = [];
|
||||
simpleTags.push(tag.toLowerCase());
|
||||
return space + tag;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -102,60 +125,74 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) {
|
|||
var extraCloseTags = "";
|
||||
|
||||
var plugins_;
|
||||
if (typeof(plugins)!='undefined') {
|
||||
if (typeof(plugins) != 'undefined')
|
||||
{
|
||||
plugins_ = plugins;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
plugins_ = parent.parent.plugins;
|
||||
}
|
||||
|
||||
plugins_.callHook(
|
||||
"aceCreateDomLine", {domline:domline, cls:cls}
|
||||
).map(function (modifier) {
|
||||
plugins_.callHook("aceCreateDomLine", {
|
||||
domline: domline,
|
||||
cls: cls
|
||||
}).map(function(modifier)
|
||||
{
|
||||
cls = modifier.cls;
|
||||
extraOpenTags = extraOpenTags+modifier.extraOpenTags;
|
||||
extraCloseTags = modifier.extraCloseTags+extraCloseTags;
|
||||
extraOpenTags = extraOpenTags + modifier.extraOpenTags;
|
||||
extraCloseTags = modifier.extraCloseTags + extraCloseTags;
|
||||
});
|
||||
|
||||
if ((! txt) && cls) {
|
||||
if ((!txt) && cls)
|
||||
{
|
||||
lineClass = domline.addToLineClass(lineClass, cls);
|
||||
}
|
||||
else if (txt) {
|
||||
if (href) {
|
||||
extraOpenTags = extraOpenTags+'<a href="'+
|
||||
href.replace(/\"/g, '"')+'">';
|
||||
extraCloseTags = '</a>'+extraCloseTags;
|
||||
else if (txt)
|
||||
{
|
||||
if (href)
|
||||
{
|
||||
extraOpenTags = extraOpenTags + '<a href="' + href.replace(/\"/g, '"') + '">';
|
||||
extraCloseTags = '</a>' + extraCloseTags;
|
||||
}
|
||||
if (simpleTags) {
|
||||
simpleTags.sort();
|
||||
extraOpenTags = extraOpenTags+'<'+simpleTags.join('><')+'>';
|
||||
simpleTags.reverse();
|
||||
extraCloseTags = '</'+simpleTags.join('></')+'>'+extraCloseTags;
|
||||
if (simpleTags)
|
||||
{
|
||||
simpleTags.sort();
|
||||
extraOpenTags = extraOpenTags + '<' + simpleTags.join('><') + '>';
|
||||
simpleTags.reverse();
|
||||
extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags;
|
||||
}
|
||||
html.push('<span class="',cls||'','">',extraOpenTags,
|
||||
perTextNodeProcess(domline.escapeHTML(txt)),
|
||||
extraCloseTags,'</span>');
|
||||
html.push('<span class="', cls || '', '">', extraOpenTags, perTextNodeProcess(domline.escapeHTML(txt)), extraCloseTags, '</span>');
|
||||
}
|
||||
};
|
||||
result.clearSpans = function() {
|
||||
result.clearSpans = function()
|
||||
{
|
||||
html = [];
|
||||
lineClass = ''; // non-null to cause update
|
||||
result.lineMarker = 0;
|
||||
};
|
||||
function writeHTML() {
|
||||
|
||||
function writeHTML()
|
||||
{
|
||||
var newHTML = perHtmlLineProcess(html.join(''));
|
||||
if (! newHTML) {
|
||||
if ((! document) || (! optBrowser)) {
|
||||
if (!newHTML)
|
||||
{
|
||||
if ((!document) || (!optBrowser))
|
||||
{
|
||||
newHTML += ' ';
|
||||
}
|
||||
else if (! browser.msie) {
|
||||
else if (!browser.msie)
|
||||
{
|
||||
newHTML += '<br/>';
|
||||
}
|
||||
}
|
||||
if (nonEmpty) {
|
||||
newHTML = (preHtml||'')+newHTML+(postHtml||'');
|
||||
if (nonEmpty)
|
||||
{
|
||||
newHTML = (preHtml || '') + newHTML + (postHtml || '');
|
||||
}
|
||||
html = preHtml = postHtml = null; // free memory
|
||||
if (newHTML !== curHTML) {
|
||||
if (newHTML !== curHTML)
|
||||
{
|
||||
curHTML = newHTML;
|
||||
result.node.innerHTML = curHTML;
|
||||
}
|
||||
|
@ -163,14 +200,20 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) {
|
|||
}
|
||||
result.prepareForAdd = writeHTML;
|
||||
result.finishUpdate = writeHTML;
|
||||
result.getInnerHTML = function() { return curHTML || ''; };
|
||||
result.getInnerHTML = function()
|
||||
{
|
||||
return curHTML || '';
|
||||
};
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
domline.escapeHTML = function(s) {
|
||||
var re = /[&<>'"]/g; /']/; // stupid indentation thing
|
||||
if (! re.MAP) {
|
||||
domline.escapeHTML = function(s)
|
||||
{
|
||||
var re = /[&<>'"]/g;
|
||||
/']/; // stupid indentation thing
|
||||
if (!re.MAP)
|
||||
{
|
||||
// persisted across function calls!
|
||||
re.MAP = {
|
||||
'&': '&',
|
||||
|
@ -180,51 +223,68 @@ 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) {
|
||||
if (s.indexOf("<") < 0 && ! doesWrap) {
|
||||
domline.processSpaces = function(s, doesWrap)
|
||||
{
|
||||
if (s.indexOf("<") < 0 && !doesWrap)
|
||||
{
|
||||
// short-cut
|
||||
return s.replace(/ /g, ' ');
|
||||
}
|
||||
var parts = [];
|
||||
s.replace(/<[^>]*>?| |[^ <]+/g, function(m) { parts.push(m); });
|
||||
if (doesWrap) {
|
||||
s.replace(/<[^>]*>?| |[^ <]+/g, function(m)
|
||||
{
|
||||
parts.push(m);
|
||||
});
|
||||
if (doesWrap)
|
||||
{
|
||||
var endOfLine = true;
|
||||
var beforeSpace = false;
|
||||
// last space in a run is normal, others are 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];
|
||||
if (p == " ") {
|
||||
if (endOfLine || beforeSpace)
|
||||
parts[i] = ' ';
|
||||
endOfLine = false;
|
||||
beforeSpace = true;
|
||||
if (p == " ")
|
||||
{
|
||||
if (endOfLine || beforeSpace) parts[i] = ' ';
|
||||
endOfLine = false;
|
||||
beforeSpace = true;
|
||||
}
|
||||
else if (p.charAt(0) != "<") {
|
||||
endOfLine = false;
|
||||
beforeSpace = false;
|
||||
else if (p.charAt(0) != "<")
|
||||
{
|
||||
endOfLine = false;
|
||||
beforeSpace = false;
|
||||
}
|
||||
}
|
||||
// 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];
|
||||
if (p == " ") {
|
||||
parts[i] = ' ';
|
||||
break;
|
||||
if (p == " ")
|
||||
{
|
||||
parts[i] = ' ';
|
||||
break;
|
||||
}
|
||||
else if (p.charAt(0) != "<") {
|
||||
break;
|
||||
else if (p.charAt(0) != "<")
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(var i=0;i<parts.length;i++) {
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < parts.length; i++)
|
||||
{
|
||||
var p = parts[i];
|
||||
if (p == " ") {
|
||||
parts[i] = ' ';
|
||||
if (p == " ")
|
||||
{
|
||||
parts[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,19 +19,19 @@
|
|||
// requires: plugins
|
||||
// requires: undefined
|
||||
var domline = {};
|
||||
domline.noop = function ()
|
||||
domline.noop = function()
|
||||
{};
|
||||
domline.identity = function (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
|
||||
// the line, using line:className. otherwise, we ignore
|
||||
// the span.
|
||||
cls.replace(/\S+/g, function (c)
|
||||
cls.replace(/\S+/g, function(c)
|
||||
{
|
||||
if (c.indexOf("line:") == 0)
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ domline.addToLineClass = function (lineClass, cls)
|
|||
|
||||
// if "document" is falsy we don't create a DOM node, just
|
||||
// an object with innerHTML and className
|
||||
domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument)
|
||||
domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
|
||||
{
|
||||
var result = {
|
||||
node: null,
|
||||
|
@ -83,7 +83,7 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument)
|
|||
var perTextNodeProcess = (doesWrap ? identity : processSpaces);
|
||||
var perHtmlLineProcess = (doesWrap ? processSpaces : identity);
|
||||
var lineClass = 'ace-line';
|
||||
result.appendSpan = function (txt, cls)
|
||||
result.appendSpan = function(txt, cls)
|
||||
{
|
||||
if (cls.indexOf('list') >= 0)
|
||||
{
|
||||
|
@ -104,7 +104,7 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument)
|
|||
var simpleTags = null;
|
||||
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;
|
||||
return space + "url";
|
||||
|
@ -112,7 +112,7 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument)
|
|||
}
|
||||
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 = [];
|
||||
simpleTags.push(tag.toLowerCase());
|
||||
|
@ -124,7 +124,7 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument)
|
|||
var extraCloseTags = "";
|
||||
|
||||
var plugins_;
|
||||
if (typeof (plugins) != 'undefined')
|
||||
if (typeof(plugins) != 'undefined')
|
||||
{
|
||||
plugins_ = plugins;
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument)
|
|||
domline: domline,
|
||||
cls: cls,
|
||||
document: document
|
||||
}).map(function (modifier)
|
||||
}).map(function(modifier)
|
||||
{
|
||||
cls = modifier.cls;
|
||||
extraOpenTags = extraOpenTags + modifier.extraOpenTags;
|
||||
|
@ -165,7 +165,7 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument)
|
|||
html.push('<span class="', cls || '', '">', extraOpenTags, perTextNodeProcess(domline.escapeHTML(txt)), extraCloseTags, '</span>');
|
||||
}
|
||||
};
|
||||
result.clearSpans = function ()
|
||||
result.clearSpans = function()
|
||||
{
|
||||
html = [];
|
||||
lineClass = ''; // non-null to cause update
|
||||
|
@ -200,7 +200,7 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument)
|
|||
}
|
||||
result.prepareForAdd = writeHTML;
|
||||
result.finishUpdate = writeHTML;
|
||||
result.getInnerHTML = function ()
|
||||
result.getInnerHTML = function()
|
||||
{
|
||||
return curHTML || '';
|
||||
};
|
||||
|
@ -208,7 +208,7 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument)
|
|||
return result;
|
||||
};
|
||||
|
||||
domline.escapeHTML = function (s)
|
||||
domline.escapeHTML = function(s)
|
||||
{
|
||||
var re = /[&<>'"]/g;
|
||||
/']/; // stupid indentation thing
|
||||
|
@ -223,13 +223,13 @@ domline.escapeHTML = function (s)
|
|||
"'": '''
|
||||
};
|
||||
}
|
||||
return s.replace(re, function (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)
|
||||
{
|
||||
|
@ -237,7 +237,7 @@ domline.processSpaces = function (s, doesWrap)
|
|||
return s.replace(/ /g, ' ');
|
||||
}
|
||||
var parts = [];
|
||||
s.replace(/<[^>]*>?| |[^ <]+/g, function (m)
|
||||
s.replace(/<[^>]*>?| |[^ <]+/g, function(m)
|
||||
{
|
||||
parts.push(m);
|
||||
});
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -16,7 +16,7 @@
|
|||
|
||||
function makeDraggable(jqueryNodes, eventHandler)
|
||||
{
|
||||
jqueryNodes.each(function ()
|
||||
jqueryNodes.each(function()
|
||||
{
|
||||
var node = $(this);
|
||||
var state = {};
|
||||
|
@ -77,7 +77,7 @@ function makeResizableVPane(top, sep, bottom, minTop, minBottom, callback)
|
|||
if (minTop === undefined) minTop = 0;
|
||||
if (minBottom === undefined) minBottom = 0;
|
||||
|
||||
makeDraggable($(sep), function (eType, evt, state)
|
||||
makeDraggable($(sep), function(eType, evt, state)
|
||||
{
|
||||
if (eType == 'dragstart')
|
||||
{
|
||||
|
@ -125,7 +125,7 @@ function makeResizableHPane(left, sep, right, minLeft, minRight, sepWidth, sepOf
|
|||
if (minLeft === undefined) minLeft = 0;
|
||||
if (minRight === undefined) minRight = 0;
|
||||
|
||||
makeDraggable($(sep), function (eType, evt, state)
|
||||
makeDraggable($(sep), function(eType, evt, state)
|
||||
{
|
||||
if (eType == 'dragstart')
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,7 +25,7 @@ function AttribPool()
|
|||
p.attribToNum = {}; // e.g. {'foo,bar': 0}
|
||||
p.nextNum = 0;
|
||||
|
||||
p.putAttrib = function (attrib, dontAddIfAbsent)
|
||||
p.putAttrib = function(attrib, dontAddIfAbsent)
|
||||
{
|
||||
var str = String(attrib);
|
||||
if (str in p.attribToNum)
|
||||
|
@ -42,28 +42,28 @@ function AttribPool()
|
|||
return num;
|
||||
};
|
||||
|
||||
p.getAttrib = function (num)
|
||||
p.getAttrib = function(num)
|
||||
{
|
||||
var pair = p.numToAttrib[num];
|
||||
if (!pair) return pair;
|
||||
return [pair[0], pair[1]]; // return a mutable copy
|
||||
};
|
||||
|
||||
p.getAttribKey = function (num)
|
||||
p.getAttribKey = function(num)
|
||||
{
|
||||
var pair = p.numToAttrib[num];
|
||||
if (!pair) return '';
|
||||
return pair[0];
|
||||
};
|
||||
|
||||
p.getAttribValue = function (num)
|
||||
p.getAttribValue = function(num)
|
||||
{
|
||||
var pair = p.numToAttrib[num];
|
||||
if (!pair) return '';
|
||||
return pair[1];
|
||||
};
|
||||
|
||||
p.eachAttrib = function (func)
|
||||
p.eachAttrib = function(func)
|
||||
{
|
||||
for (var n in p.numToAttrib)
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ function AttribPool()
|
|||
}
|
||||
};
|
||||
|
||||
p.toJsonable = function ()
|
||||
p.toJsonable = function()
|
||||
{
|
||||
return {
|
||||
numToAttrib: p.numToAttrib,
|
||||
|
@ -80,7 +80,7 @@ function AttribPool()
|
|||
};
|
||||
};
|
||||
|
||||
p.fromJsonable = function (obj)
|
||||
p.fromJsonable = function(obj)
|
||||
{
|
||||
p.numToAttrib = obj.numToAttrib;
|
||||
p.nextNum = obj.nextNum;
|
||||
|
@ -112,35 +112,35 @@ Changeset.assert = function assert(b, msgParts)
|
|||
}
|
||||
};
|
||||
|
||||
Changeset.parseNum = function (str)
|
||||
Changeset.parseNum = function(str)
|
||||
{
|
||||
return parseInt(str, 36);
|
||||
};
|
||||
Changeset.numToString = function (num)
|
||||
Changeset.numToString = function(num)
|
||||
{
|
||||
return num.toString(36).toLowerCase();
|
||||
};
|
||||
Changeset.toBaseTen = function (cs)
|
||||
Changeset.toBaseTen = function(cs)
|
||||
{
|
||||
var dollarIndex = cs.indexOf('$');
|
||||
var beforeDollar = cs.substring(0, dollarIndex);
|
||||
var fromDollar = cs.substring(dollarIndex);
|
||||
return beforeDollar.replace(/[0-9a-z]+/g, function (s)
|
||||
return beforeDollar.replace(/[0-9a-z]+/g, function(s)
|
||||
{
|
||||
return String(Changeset.parseNum(s));
|
||||
}) + fromDollar;
|
||||
};
|
||||
|
||||
Changeset.oldLen = function (cs)
|
||||
Changeset.oldLen = function(cs)
|
||||
{
|
||||
return Changeset.unpack(cs).oldLen;
|
||||
};
|
||||
Changeset.newLen = function (cs)
|
||||
Changeset.newLen = function(cs)
|
||||
{
|
||||
return Changeset.unpack(cs).newLen;
|
||||
};
|
||||
|
||||
Changeset.opIterator = function (opsStr, optStartIndex)
|
||||
Changeset.opIterator = function(opsStr, optStartIndex)
|
||||
{
|
||||
//print(opsStr);
|
||||
var regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|\?|/g;
|
||||
|
@ -221,14 +221,14 @@ Changeset.opIterator = function (opsStr, optStartIndex)
|
|||
};
|
||||
};
|
||||
|
||||
Changeset.clearOp = function (op)
|
||||
Changeset.clearOp = function(op)
|
||||
{
|
||||
op.opcode = '';
|
||||
op.chars = 0;
|
||||
op.lines = 0;
|
||||
op.attribs = '';
|
||||
};
|
||||
Changeset.newOp = function (optOpcode)
|
||||
Changeset.newOp = function(optOpcode)
|
||||
{
|
||||
return {
|
||||
opcode: (optOpcode || ''),
|
||||
|
@ -237,7 +237,7 @@ Changeset.newOp = function (optOpcode)
|
|||
attribs: ''
|
||||
};
|
||||
};
|
||||
Changeset.cloneOp = function (op)
|
||||
Changeset.cloneOp = function(op)
|
||||
{
|
||||
return {
|
||||
opcode: op.opcode,
|
||||
|
@ -246,14 +246,14 @@ Changeset.cloneOp = function (op)
|
|||
attribs: op.attribs
|
||||
};
|
||||
};
|
||||
Changeset.copyOp = function (op1, op2)
|
||||
Changeset.copyOp = function(op1, op2)
|
||||
{
|
||||
op2.opcode = op1.opcode;
|
||||
op2.chars = op1.chars;
|
||||
op2.lines = op1.lines;
|
||||
op2.attribs = op1.attribs;
|
||||
};
|
||||
Changeset.opString = function (op)
|
||||
Changeset.opString = function(op)
|
||||
{
|
||||
// just for debugging
|
||||
if (!op.opcode) return 'null';
|
||||
|
@ -261,13 +261,13 @@ Changeset.opString = function (op)
|
|||
assem.append(op);
|
||||
return assem.toString();
|
||||
};
|
||||
Changeset.stringOp = function (str)
|
||||
Changeset.stringOp = function(str)
|
||||
{
|
||||
// just for debugging
|
||||
return Changeset.opIterator(str).next();
|
||||
};
|
||||
|
||||
Changeset.checkRep = function (cs)
|
||||
Changeset.checkRep = function(cs)
|
||||
{
|
||||
// doesn't check things that require access to attrib pool (e.g. attribute order)
|
||||
// or original string (e.g. newline positions)
|
||||
|
@ -320,7 +320,7 @@ Changeset.checkRep = function (cs)
|
|||
return cs;
|
||||
}
|
||||
|
||||
Changeset.smartOpAssembler = function ()
|
||||
Changeset.smartOpAssembler = function()
|
||||
{
|
||||
// Like opAssembler but able to produce conforming changesets
|
||||
// from slightly looser input, at the cost of speed.
|
||||
|
@ -444,7 +444,7 @@ Changeset.smartOpAssembler = function ()
|
|||
|
||||
if (_opt)
|
||||
{
|
||||
Changeset.mergingOpAssembler = function ()
|
||||
Changeset.mergingOpAssembler = function()
|
||||
{
|
||||
var assem = _opt.mergingOpAssembler();
|
||||
|
||||
|
@ -478,7 +478,7 @@ if (_opt)
|
|||
}
|
||||
else
|
||||
{
|
||||
Changeset.mergingOpAssembler = function ()
|
||||
Changeset.mergingOpAssembler = function()
|
||||
{
|
||||
// This assembler can be used in production; it efficiently
|
||||
// merges consecutive operations that are mergeable, ignores
|
||||
|
@ -575,12 +575,11 @@ else
|
|||
|
||||
if (_opt)
|
||||
{
|
||||
Changeset.opAssembler = function ()
|
||||
Changeset.opAssembler = function()
|
||||
{
|
||||
var assem = _opt.opAssembler();
|
||||
// this function allows op to be mutated later (doesn't keep a ref)
|
||||
|
||||
|
||||
function append(op)
|
||||
{
|
||||
assem.append(op.opcode, op.chars, op.lines, op.attribs);
|
||||
|
@ -604,12 +603,11 @@ if (_opt)
|
|||
}
|
||||
else
|
||||
{
|
||||
Changeset.opAssembler = function ()
|
||||
Changeset.opAssembler = function()
|
||||
{
|
||||
var pieces = [];
|
||||
// this function allows op to be mutated later (doesn't keep a ref)
|
||||
|
||||
|
||||
function append(op)
|
||||
{
|
||||
pieces.push(op.attribs);
|
||||
|
@ -638,7 +636,7 @@ else
|
|||
};
|
||||
}
|
||||
|
||||
Changeset.stringIterator = function (str)
|
||||
Changeset.stringIterator = function(str)
|
||||
{
|
||||
var curIndex = 0;
|
||||
|
||||
|
@ -680,7 +678,7 @@ Changeset.stringIterator = function (str)
|
|||
};
|
||||
};
|
||||
|
||||
Changeset.stringAssembler = function ()
|
||||
Changeset.stringAssembler = function()
|
||||
{
|
||||
var pieces = [];
|
||||
|
||||
|
@ -700,7 +698,7 @@ Changeset.stringAssembler = function ()
|
|||
};
|
||||
|
||||
// "lines" need not be an array as long as it supports certain calls (lines_foo inside).
|
||||
Changeset.textLinesMutator = function (lines)
|
||||
Changeset.textLinesMutator = function(lines)
|
||||
{
|
||||
// Mutates lines, an array of strings, in place.
|
||||
// Mutation operations have the same constraints as changeset operations
|
||||
|
@ -743,7 +741,6 @@ Changeset.textLinesMutator = function (lines)
|
|||
}
|
||||
// can be unimplemented if removeLines's return value not needed
|
||||
|
||||
|
||||
function lines_slice(start, end)
|
||||
{
|
||||
if (lines.slice)
|
||||
|
@ -1024,7 +1021,7 @@ Changeset.textLinesMutator = function (lines)
|
|||
return self;
|
||||
};
|
||||
|
||||
Changeset.applyZip = function (in1, idx1, in2, idx2, func)
|
||||
Changeset.applyZip = function(in1, idx1, in2, idx2, func)
|
||||
{
|
||||
var iter1 = Changeset.opIterator(in1, idx1);
|
||||
var iter2 = Changeset.opIterator(in2, idx2);
|
||||
|
@ -1048,7 +1045,7 @@ Changeset.applyZip = function (in1, idx1, in2, idx2, func)
|
|||
return assem.toString();
|
||||
};
|
||||
|
||||
Changeset.unpack = function (cs)
|
||||
Changeset.unpack = function(cs)
|
||||
{
|
||||
var headerRegex = /Z:([0-9a-z]+)([><])([0-9a-z]+)|/;
|
||||
var headerMatch = headerRegex.exec(cs);
|
||||
|
@ -1071,7 +1068,7 @@ Changeset.unpack = function (cs)
|
|||
};
|
||||
};
|
||||
|
||||
Changeset.pack = function (oldLen, newLen, opsStr, bank)
|
||||
Changeset.pack = function(oldLen, newLen, opsStr, bank)
|
||||
{
|
||||
var lenDiff = newLen - oldLen;
|
||||
var lenDiffStr = (lenDiff >= 0 ? '>' + Changeset.numToString(lenDiff) : '<' + Changeset.numToString(-lenDiff));
|
||||
|
@ -1080,7 +1077,7 @@ Changeset.pack = function (oldLen, newLen, opsStr, bank)
|
|||
return a.join('');
|
||||
};
|
||||
|
||||
Changeset.applyToText = function (cs, str)
|
||||
Changeset.applyToText = function(cs, str)
|
||||
{
|
||||
var unpacked = Changeset.unpack(cs);
|
||||
Changeset.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen);
|
||||
|
@ -1108,7 +1105,7 @@ Changeset.applyToText = function (cs, str)
|
|||
return assem.toString();
|
||||
};
|
||||
|
||||
Changeset.mutateTextLines = function (cs, lines)
|
||||
Changeset.mutateTextLines = function(cs, lines)
|
||||
{
|
||||
var unpacked = Changeset.unpack(cs);
|
||||
var csIter = Changeset.opIterator(unpacked.ops);
|
||||
|
@ -1133,7 +1130,7 @@ Changeset.mutateTextLines = function (cs, lines)
|
|||
mut.close();
|
||||
};
|
||||
|
||||
Changeset.composeAttributes = function (att1, att2, resultIsMutation, pool)
|
||||
Changeset.composeAttributes = function(att1, att2, resultIsMutation, pool)
|
||||
{
|
||||
// att1 and att2 are strings like "*3*f*1c", asMutation is a boolean.
|
||||
// Sometimes attribute (key,value) pairs are treated as attribute presence
|
||||
|
@ -1158,12 +1155,12 @@ Changeset.composeAttributes = function (att1, att2, resultIsMutation, pool)
|
|||
}
|
||||
if (!att2) return att1;
|
||||
var atts = [];
|
||||
att1.replace(/\*([0-9a-z]+)/g, function (_, a)
|
||||
att1.replace(/\*([0-9a-z]+)/g, function(_, a)
|
||||
{
|
||||
atts.push(pool.getAttrib(Changeset.parseNum(a)));
|
||||
return '';
|
||||
});
|
||||
att2.replace(/\*([0-9a-z]+)/g, function (_, a)
|
||||
att2.replace(/\*([0-9a-z]+)/g, function(_, a)
|
||||
{
|
||||
var pair = pool.getAttrib(Changeset.parseNum(a));
|
||||
var found = false;
|
||||
|
@ -1201,7 +1198,7 @@ Changeset.composeAttributes = function (att1, att2, resultIsMutation, pool)
|
|||
return buf.toString();
|
||||
};
|
||||
|
||||
Changeset._slicerZipperFunc = function (attOp, csOp, opOut, pool)
|
||||
Changeset._slicerZipperFunc = function(attOp, csOp, opOut, pool)
|
||||
{
|
||||
// attOp is the op from the sequence that is being operated on, either an
|
||||
// attribution string or the earlier of two changesets being composed.
|
||||
|
@ -1304,11 +1301,11 @@ Changeset._slicerZipperFunc = function (attOp, csOp, opOut, pool)
|
|||
}
|
||||
};
|
||||
|
||||
Changeset.applyToAttribution = function (cs, astr, pool)
|
||||
Changeset.applyToAttribution = function(cs, astr, pool)
|
||||
{
|
||||
var unpacked = Changeset.unpack(cs);
|
||||
|
||||
return Changeset.applyZip(astr, 0, unpacked.ops, 0, function (op1, op2, opOut)
|
||||
return Changeset.applyZip(astr, 0, unpacked.ops, 0, function(op1, op2, opOut)
|
||||
{
|
||||
return Changeset._slicerZipperFunc(op1, op2, opOut, pool);
|
||||
});
|
||||
|
@ -1320,7 +1317,7 @@ Changeset.applyToAttribution = function (cs, astr, pool)
|
|||
|
||||
};*/
|
||||
|
||||
Changeset.mutateAttributionLines = function (cs, lines, pool)
|
||||
Changeset.mutateAttributionLines = function(cs, lines, pool)
|
||||
{
|
||||
//dmesg(cs);
|
||||
//dmesg(lines.toSource()+" ->");
|
||||
|
@ -1438,7 +1435,7 @@ Changeset.mutateAttributionLines = function (cs, lines, pool)
|
|||
//dmesg("-> "+lines.toSource());
|
||||
};
|
||||
|
||||
Changeset.joinAttributionLines = function (theAlines)
|
||||
Changeset.joinAttributionLines = function(theAlines)
|
||||
{
|
||||
var assem = Changeset.mergingOpAssembler();
|
||||
for (var i = 0; i < theAlines.length; i++)
|
||||
|
@ -1453,7 +1450,7 @@ Changeset.joinAttributionLines = function (theAlines)
|
|||
return assem.toString();
|
||||
};
|
||||
|
||||
Changeset.splitAttributionLines = function (attrOps, text)
|
||||
Changeset.splitAttributionLines = function(attrOps, text)
|
||||
{
|
||||
var iter = Changeset.opIterator(attrOps);
|
||||
var assem = Changeset.mergingOpAssembler();
|
||||
|
@ -1497,12 +1494,12 @@ Changeset.splitAttributionLines = function (attrOps, text)
|
|||
return lines;
|
||||
};
|
||||
|
||||
Changeset.splitTextLines = function (text)
|
||||
Changeset.splitTextLines = function(text)
|
||||
{
|
||||
return text.match(/[^\n]*(?:\n|[^\n]$)/g);
|
||||
};
|
||||
|
||||
Changeset.compose = function (cs1, cs2, pool)
|
||||
Changeset.compose = function(cs1, cs2, pool)
|
||||
{
|
||||
var unpacked1 = Changeset.unpack(cs1);
|
||||
var unpacked2 = Changeset.unpack(cs2);
|
||||
|
@ -1514,7 +1511,7 @@ Changeset.compose = function (cs1, cs2, pool)
|
|||
var bankIter2 = Changeset.stringIterator(unpacked2.charBank);
|
||||
var bankAssem = Changeset.stringAssembler();
|
||||
|
||||
var newOps = Changeset.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function (op1, op2, opOut)
|
||||
var newOps = Changeset.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function(op1, op2, opOut)
|
||||
{
|
||||
//var debugBuilder = Changeset.stringAssembler();
|
||||
//debugBuilder.append(Changeset.opString(op1));
|
||||
|
@ -1551,7 +1548,7 @@ Changeset.compose = function (cs1, cs2, pool)
|
|||
return Changeset.pack(len1, len3, newOps, bankAssem.toString());
|
||||
};
|
||||
|
||||
Changeset.attributeTester = function (attribPair, pool)
|
||||
Changeset.attributeTester = function(attribPair, pool)
|
||||
{
|
||||
// returns a function that tests if a string of attributes
|
||||
// (e.g. *3*4) contains a given attribute key,value that
|
||||
|
@ -1568,7 +1565,7 @@ Changeset.attributeTester = function (attribPair, pool)
|
|||
else
|
||||
{
|
||||
var re = new RegExp('\\*' + Changeset.numToString(attribNum) + '(?!\\w)');
|
||||
return function (attribs)
|
||||
return function(attribs)
|
||||
{
|
||||
return re.test(attribs);
|
||||
};
|
||||
|
@ -1580,12 +1577,12 @@ Changeset.attributeTester = function (attribPair, pool)
|
|||
}
|
||||
};
|
||||
|
||||
Changeset.identity = function (N)
|
||||
Changeset.identity = function(N)
|
||||
{
|
||||
return Changeset.pack(N, N, "", "");
|
||||
};
|
||||
|
||||
Changeset.makeSplice = function (oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool)
|
||||
Changeset.makeSplice = function(oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool)
|
||||
{
|
||||
var oldLen = oldFullText.length;
|
||||
|
||||
|
@ -1608,7 +1605,7 @@ Changeset.makeSplice = function (oldFullText, spliceStart, numRemoved, newText,
|
|||
return Changeset.pack(oldLen, newLen, assem.toString(), newText);
|
||||
};
|
||||
|
||||
Changeset.toSplices = function (cs)
|
||||
Changeset.toSplices = function(cs)
|
||||
{
|
||||
// get a list of splices, [startChar, endChar, newText]
|
||||
var unpacked = Changeset.unpack(cs);
|
||||
|
@ -1648,7 +1645,7 @@ Changeset.toSplices = function (cs)
|
|||
return splices;
|
||||
};
|
||||
|
||||
Changeset.characterRangeFollow = function (cs, startChar, endChar, insertionsAfter)
|
||||
Changeset.characterRangeFollow = function(cs, startChar, endChar, insertionsAfter)
|
||||
{
|
||||
var newStartChar = startChar;
|
||||
var newEndChar = endChar;
|
||||
|
@ -1708,7 +1705,7 @@ Changeset.characterRangeFollow = function (cs, startChar, endChar, insertionsAft
|
|||
return [newStartChar, newEndChar];
|
||||
};
|
||||
|
||||
Changeset.moveOpsToNewPool = function (cs, oldPool, newPool)
|
||||
Changeset.moveOpsToNewPool = function(cs, oldPool, newPool)
|
||||
{
|
||||
// works on changeset or attribution string
|
||||
var dollarPos = cs.indexOf('$');
|
||||
|
@ -1719,7 +1716,7 @@ Changeset.moveOpsToNewPool = function (cs, oldPool, newPool)
|
|||
var upToDollar = cs.substring(0, dollarPos);
|
||||
var fromDollar = cs.substring(dollarPos);
|
||||
// order of attribs stays the same
|
||||
return upToDollar.replace(/\*([0-9a-z]+)/g, function (_, a)
|
||||
return upToDollar.replace(/\*([0-9a-z]+)/g, function(_, a)
|
||||
{
|
||||
var oldNum = Changeset.parseNum(a);
|
||||
var pair = oldPool.getAttrib(oldNum);
|
||||
|
@ -1728,7 +1725,7 @@ Changeset.moveOpsToNewPool = function (cs, oldPool, newPool)
|
|||
}) + fromDollar;
|
||||
};
|
||||
|
||||
Changeset.makeAttribution = function (text)
|
||||
Changeset.makeAttribution = function(text)
|
||||
{
|
||||
var assem = Changeset.smartOpAssembler();
|
||||
assem.appendOpWithText('+', text);
|
||||
|
@ -1736,7 +1733,7 @@ Changeset.makeAttribution = function (text)
|
|||
};
|
||||
|
||||
// callable on a changeset, attribution string, or attribs property of an op
|
||||
Changeset.eachAttribNumber = function (cs, func)
|
||||
Changeset.eachAttribNumber = function(cs, func)
|
||||
{
|
||||
var dollarPos = cs.indexOf('$');
|
||||
if (dollarPos < 0)
|
||||
|
@ -1745,7 +1742,7 @@ Changeset.eachAttribNumber = function (cs, func)
|
|||
}
|
||||
var upToDollar = cs.substring(0, dollarPos);
|
||||
|
||||
upToDollar.replace(/\*([0-9a-z]+)/g, function (_, a)
|
||||
upToDollar.replace(/\*([0-9a-z]+)/g, function(_, a)
|
||||
{
|
||||
func(Changeset.parseNum(a));
|
||||
return '';
|
||||
|
@ -1754,12 +1751,12 @@ Changeset.eachAttribNumber = function (cs, func)
|
|||
|
||||
// callable on a changeset, attribution string, or attribs property of an op,
|
||||
// though it may easily create adjacent ops that can be merged.
|
||||
Changeset.filterAttribNumbers = function (cs, filter)
|
||||
Changeset.filterAttribNumbers = function(cs, filter)
|
||||
{
|
||||
return Changeset.mapAttribNumbers(cs, filter);
|
||||
};
|
||||
|
||||
Changeset.mapAttribNumbers = function (cs, func)
|
||||
Changeset.mapAttribNumbers = function(cs, func)
|
||||
{
|
||||
var dollarPos = cs.indexOf('$');
|
||||
if (dollarPos < 0)
|
||||
|
@ -1768,7 +1765,7 @@ Changeset.mapAttribNumbers = function (cs, func)
|
|||
}
|
||||
var upToDollar = cs.substring(0, dollarPos);
|
||||
|
||||
var newUpToDollar = upToDollar.replace(/\*([0-9a-z]+)/g, function (s, a)
|
||||
var newUpToDollar = upToDollar.replace(/\*([0-9a-z]+)/g, function(s, a)
|
||||
{
|
||||
var n = func(Changeset.parseNum(a));
|
||||
if (n === true)
|
||||
|
@ -1788,7 +1785,7 @@ Changeset.mapAttribNumbers = function (cs, func)
|
|||
return newUpToDollar + cs.substring(dollarPos);
|
||||
};
|
||||
|
||||
Changeset.makeAText = function (text, attribs)
|
||||
Changeset.makeAText = function(text, attribs)
|
||||
{
|
||||
return {
|
||||
text: text,
|
||||
|
@ -1796,7 +1793,7 @@ Changeset.makeAText = function (text, attribs)
|
|||
};
|
||||
};
|
||||
|
||||
Changeset.applyToAText = function (cs, atext, pool)
|
||||
Changeset.applyToAText = function(cs, atext, pool)
|
||||
{
|
||||
return {
|
||||
text: Changeset.applyToText(cs, atext.text),
|
||||
|
@ -1804,7 +1801,7 @@ Changeset.applyToAText = function (cs, atext, pool)
|
|||
};
|
||||
};
|
||||
|
||||
Changeset.cloneAText = function (atext)
|
||||
Changeset.cloneAText = function(atext)
|
||||
{
|
||||
return {
|
||||
text: atext.text,
|
||||
|
@ -1812,13 +1809,13 @@ Changeset.cloneAText = function (atext)
|
|||
};
|
||||
};
|
||||
|
||||
Changeset.copyAText = function (atext1, atext2)
|
||||
Changeset.copyAText = function(atext1, atext2)
|
||||
{
|
||||
atext2.text = atext1.text;
|
||||
atext2.attribs = atext1.attribs;
|
||||
};
|
||||
|
||||
Changeset.appendATextToAssembler = function (atext, assem)
|
||||
Changeset.appendATextToAssembler = function(atext, assem)
|
||||
{
|
||||
// intentionally skips last newline char of atext
|
||||
var iter = Changeset.opIterator(atext.attribs);
|
||||
|
@ -1860,7 +1857,7 @@ Changeset.appendATextToAssembler = function (atext, assem)
|
|||
}
|
||||
};
|
||||
|
||||
Changeset.prepareForWire = function (cs, pool)
|
||||
Changeset.prepareForWire = function(cs, pool)
|
||||
{
|
||||
var newPool = new AttribPool();
|
||||
var newCs = Changeset.moveOpsToNewPool(cs, pool, newPool);
|
||||
|
@ -1870,23 +1867,23 @@ Changeset.prepareForWire = function (cs, pool)
|
|||
};
|
||||
};
|
||||
|
||||
Changeset.isIdentity = function (cs)
|
||||
Changeset.isIdentity = function(cs)
|
||||
{
|
||||
var unpacked = Changeset.unpack(cs);
|
||||
return unpacked.ops == "" && unpacked.oldLen == unpacked.newLen;
|
||||
};
|
||||
|
||||
Changeset.opAttributeValue = function (op, key, pool)
|
||||
Changeset.opAttributeValue = function(op, key, pool)
|
||||
{
|
||||
return Changeset.attribsAttributeValue(op.attribs, key, pool);
|
||||
};
|
||||
|
||||
Changeset.attribsAttributeValue = function (attribs, key, pool)
|
||||
Changeset.attribsAttributeValue = function(attribs, key, pool)
|
||||
{
|
||||
var value = '';
|
||||
if (attribs)
|
||||
{
|
||||
Changeset.eachAttribNumber(attribs, function (n)
|
||||
Changeset.eachAttribNumber(attribs, function(n)
|
||||
{
|
||||
if (pool.getAttribKey(n) == key)
|
||||
{
|
||||
|
@ -1897,7 +1894,7 @@ Changeset.attribsAttributeValue = function (attribs, key, pool)
|
|||
return value;
|
||||
};
|
||||
|
||||
Changeset.builder = function (oldLen)
|
||||
Changeset.builder = function(oldLen)
|
||||
{
|
||||
var assem = Changeset.smartOpAssembler();
|
||||
var o = Changeset.newOp();
|
||||
|
@ -1905,7 +1902,7 @@ Changeset.builder = function (oldLen)
|
|||
|
||||
var self = {
|
||||
// attribs are [[key1,value1],[key2,value2],...] or '*0*1...' (no pool needed in latter case)
|
||||
keep: function (N, L, attribs, pool)
|
||||
keep: function(N, L, attribs, pool)
|
||||
{
|
||||
o.opcode = '=';
|
||||
o.attribs = (attribs && Changeset.makeAttribsString('=', attribs, pool)) || '';
|
||||
|
@ -1914,18 +1911,18 @@ Changeset.builder = function (oldLen)
|
|||
assem.append(o);
|
||||
return self;
|
||||
},
|
||||
keepText: function (text, attribs, pool)
|
||||
keepText: function(text, attribs, pool)
|
||||
{
|
||||
assem.appendOpWithText('=', text, attribs, pool);
|
||||
return self;
|
||||
},
|
||||
insert: function (text, attribs, pool)
|
||||
insert: function(text, attribs, pool)
|
||||
{
|
||||
assem.appendOpWithText('+', text, attribs, pool);
|
||||
charBank.append(text);
|
||||
return self;
|
||||
},
|
||||
remove: function (N, L)
|
||||
remove: function(N, L)
|
||||
{
|
||||
o.opcode = '-';
|
||||
o.attribs = '';
|
||||
|
@ -1934,7 +1931,7 @@ Changeset.builder = function (oldLen)
|
|||
assem.append(o);
|
||||
return self;
|
||||
},
|
||||
toString: function ()
|
||||
toString: function()
|
||||
{
|
||||
assem.endDocument();
|
||||
var newLen = oldLen + assem.getLengthChange();
|
||||
|
@ -1945,7 +1942,7 @@ Changeset.builder = function (oldLen)
|
|||
return self;
|
||||
};
|
||||
|
||||
Changeset.makeAttribsString = function (opcode, attribs, pool)
|
||||
Changeset.makeAttribsString = function(opcode, attribs, pool)
|
||||
{
|
||||
// makeAttribsString(opcode, '*3') or makeAttribsString(opcode, [['foo','bar']], myPool) work
|
||||
if (!attribs)
|
||||
|
@ -1977,7 +1974,7 @@ Changeset.makeAttribsString = function (opcode, attribs, pool)
|
|||
};
|
||||
|
||||
// like "substring" but on a single-line attribution string
|
||||
Changeset.subattribution = function (astr, start, optEnd)
|
||||
Changeset.subattribution = function(astr, start, optEnd)
|
||||
{
|
||||
var iter = Changeset.opIterator(astr, 0);
|
||||
var assem = Changeset.smartOpAssembler();
|
||||
|
@ -2035,13 +2032,12 @@ Changeset.subattribution = function (astr, start, optEnd)
|
|||
return assem.toString();
|
||||
};
|
||||
|
||||
Changeset.inverse = function (cs, lines, alines, pool)
|
||||
Changeset.inverse = function(cs, lines, alines, pool)
|
||||
{
|
||||
// lines and alines are what the changeset is meant to apply to.
|
||||
// They may be arrays or objects with .get(i) and .length methods.
|
||||
// They include final newlines on lines.
|
||||
|
||||
|
||||
function lines_get(idx)
|
||||
{
|
||||
if (lines.get)
|
||||
|
@ -2164,7 +2160,7 @@ Changeset.inverse = function (cs, lines, alines, pool)
|
|||
{
|
||||
if (curLineOpIter && curLineOpIterLine == curLine)
|
||||
{
|
||||
consumeAttribRuns(N, function ()
|
||||
consumeAttribRuns(N, function()
|
||||
{});
|
||||
}
|
||||
else
|
||||
|
@ -2197,7 +2193,7 @@ Changeset.inverse = function (cs, lines, alines, pool)
|
|||
function cachedStrFunc(func)
|
||||
{
|
||||
var cache = {};
|
||||
return function (s)
|
||||
return function(s)
|
||||
{
|
||||
if (!cache[s])
|
||||
{
|
||||
|
@ -2218,12 +2214,12 @@ Changeset.inverse = function (cs, lines, alines, pool)
|
|||
{
|
||||
attribKeys.length = 0;
|
||||
attribValues.length = 0;
|
||||
Changeset.eachAttribNumber(csOp.attribs, function (n)
|
||||
Changeset.eachAttribNumber(csOp.attribs, function(n)
|
||||
{
|
||||
attribKeys.push(pool.getAttribKey(n));
|
||||
attribValues.push(pool.getAttribValue(n));
|
||||
});
|
||||
var undoBackToAttribs = cachedStrFunc(function (attribs)
|
||||
var undoBackToAttribs = cachedStrFunc(function(attribs)
|
||||
{
|
||||
var backAttribs = [];
|
||||
for (var i = 0; i < attribKeys.length; i++)
|
||||
|
@ -2238,7 +2234,7 @@ Changeset.inverse = function (cs, lines, alines, pool)
|
|||
}
|
||||
return Changeset.makeAttribsString('=', backAttribs, pool);
|
||||
});
|
||||
consumeAttribRuns(csOp.chars, function (len, attribs, endsLine)
|
||||
consumeAttribRuns(csOp.chars, function(len, attribs, endsLine)
|
||||
{
|
||||
builder.keep(len, endsLine ? 1 : 0, undoBackToAttribs(attribs));
|
||||
});
|
||||
|
@ -2257,7 +2253,7 @@ Changeset.inverse = function (cs, lines, alines, pool)
|
|||
{
|
||||
var textBank = nextText(csOp.chars);
|
||||
var textBankIndex = 0;
|
||||
consumeAttribRuns(csOp.chars, function (len, attribs, endsLine)
|
||||
consumeAttribRuns(csOp.chars, function(len, attribs, endsLine)
|
||||
{
|
||||
builder.insert(textBank.substr(textBankIndex, len), attribs);
|
||||
textBankIndex += len;
|
||||
|
|
|
@ -158,323 +158,314 @@
|
|||
|
||||
// Create a JSON object only if one does not already exist. We create the
|
||||
// methods in a closure to avoid creating global variables.
|
||||
|
||||
var JSON;
|
||||
if (!JSON) {
|
||||
JSON = {};
|
||||
if (!JSON)
|
||||
{
|
||||
JSON = {};
|
||||
}
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
(function()
|
||||
{
|
||||
"use strict";
|
||||
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
function f(n)
|
||||
{
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
if (typeof Date.prototype.toJSON !== 'function')
|
||||
{
|
||||
|
||||
Date.prototype.toJSON = function(key)
|
||||
{
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function(key)
|
||||
{
|
||||
return this.valueOf();
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
gap, indent, meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"': '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
rep;
|
||||
|
||||
|
||||
function quote(string)
|
||||
{
|
||||
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can safely slap some quotes around it.
|
||||
// Otherwise we must also replace the offending characters with safe escape
|
||||
// sequences.
|
||||
escapable.lastIndex = 0;
|
||||
return escapable.test(string) ? '"' + string.replace(escapable, function(a)
|
||||
{
|
||||
var c = meta[a];
|
||||
return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
}) + '"' : '"' + string + '"';
|
||||
}
|
||||
|
||||
|
||||
function str(key, holder)
|
||||
{
|
||||
|
||||
// Produce a string from holder[key].
|
||||
var i, // The loop counter.
|
||||
k, // The member key.
|
||||
v, // The member value.
|
||||
length, mind = gap,
|
||||
partial, value = holder[key];
|
||||
|
||||
// If the value has a toJSON method, call it to obtain a replacement value.
|
||||
if (value && typeof value === 'object' && typeof value.toJSON === 'function')
|
||||
{
|
||||
value = value.toJSON(key);
|
||||
}
|
||||
|
||||
if (typeof Date.prototype.toJSON !== 'function') {
|
||||
|
||||
Date.prototype.toJSON = function (key) {
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
String.prototype.toJSON =
|
||||
Number.prototype.toJSON =
|
||||
Boolean.prototype.toJSON = function (key) {
|
||||
return this.valueOf();
|
||||
};
|
||||
// If we were called with a replacer function, then call the replacer to
|
||||
// obtain a replacement value.
|
||||
if (typeof rep === 'function')
|
||||
{
|
||||
value = rep.call(holder, key, value);
|
||||
}
|
||||
|
||||
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,
|
||||
gap,
|
||||
indent,
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
rep;
|
||||
// What happens next depends on the value's type.
|
||||
switch (typeof value)
|
||||
{
|
||||
case 'string':
|
||||
return quote(value);
|
||||
|
||||
case 'number':
|
||||
|
||||
function quote(string) {
|
||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
return isFinite(value) ? String(value) : 'null';
|
||||
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can safely slap some quotes around it.
|
||||
// Otherwise we must also replace the offending characters with safe escape
|
||||
// sequences.
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
|
||||
escapable.lastIndex = 0;
|
||||
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
||||
var c = meta[a];
|
||||
return typeof c === 'string' ? c :
|
||||
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
}) + '"' : '"' + string + '"';
|
||||
}
|
||||
// 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
|
||||
// the remote chance that this gets fixed someday.
|
||||
return String(value);
|
||||
|
||||
// If the type is 'object', we might be dealing with an object or an array or
|
||||
// null.
|
||||
case 'object':
|
||||
|
||||
function str(key, holder) {
|
||||
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
||||
// so watch out for that case.
|
||||
if (!value)
|
||||
{
|
||||
return 'null';
|
||||
}
|
||||
|
||||
// Produce a string from holder[key].
|
||||
// Make an array to hold the partial results of stringifying this object value.
|
||||
gap += indent;
|
||||
partial = [];
|
||||
|
||||
var i, // The loop counter.
|
||||
k, // The member key.
|
||||
v, // The member value.
|
||||
length,
|
||||
mind = gap,
|
||||
partial,
|
||||
value = holder[key];
|
||||
// Is the value an array?
|
||||
if (Object.prototype.toString.apply(value) === '[object Array]')
|
||||
{
|
||||
|
||||
// If the value has a toJSON method, call it to obtain a replacement value.
|
||||
|
||||
if (value && typeof value === 'object' &&
|
||||
typeof value.toJSON === 'function') {
|
||||
value = value.toJSON(key);
|
||||
// The value is an array. Stringify every element. Use null as a placeholder
|
||||
// for non-JSON values.
|
||||
length = value.length;
|
||||
for (i = 0; i < length; i += 1)
|
||||
{
|
||||
partial[i] = str(i, value) || 'null';
|
||||
}
|
||||
|
||||
// If we were called with a replacer function, then call the replacer to
|
||||
// obtain a replacement value.
|
||||
// Join all of the elements together, separated with commas, and wrap them in
|
||||
// brackets.
|
||||
v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
|
||||
if (typeof rep === 'function') {
|
||||
value = rep.call(holder, key, value);
|
||||
// If the replacer is an array, use it to select the members to be stringified.
|
||||
if (rep && typeof rep === 'object')
|
||||
{
|
||||
length = rep.length;
|
||||
for (i = 0; i < length; i += 1)
|
||||
{
|
||||
if (typeof rep[i] === 'string')
|
||||
{
|
||||
k = rep[i];
|
||||
v = str(k, value);
|
||||
if (v)
|
||||
{
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Otherwise, iterate through all of the keys in the object.
|
||||
for (k in value)
|
||||
{
|
||||
if (Object.prototype.hasOwnProperty.call(value, k))
|
||||
{
|
||||
v = str(k, value);
|
||||
if (v)
|
||||
{
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join all of the member texts together, separated with commas,
|
||||
// and wrap them in braces.
|
||||
v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// If the JSON object does not yet have a stringify method, give it one.
|
||||
if (typeof JSON.stringify !== 'function')
|
||||
{
|
||||
JSON.stringify = function(value, replacer, space)
|
||||
{
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// produce text that is more easily readable.
|
||||
var i;
|
||||
gap = '';
|
||||
indent = '';
|
||||
|
||||
// If the space parameter is a number, make an indent string containing that
|
||||
// many spaces.
|
||||
if (typeof space === 'number')
|
||||
{
|
||||
for (i = 0; i < space; i += 1)
|
||||
{
|
||||
indent += ' ';
|
||||
}
|
||||
|
||||
// What happens next depends on the value's type.
|
||||
// If the space parameter is a string, it will be used as the indent string.
|
||||
}
|
||||
else if (typeof space === 'string')
|
||||
{
|
||||
indent = space;
|
||||
}
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return quote(value);
|
||||
// If there is a replacer, it must be a function or an array.
|
||||
// Otherwise, throw an error.
|
||||
rep = replacer;
|
||||
if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number'))
|
||||
{
|
||||
throw new Error('JSON.stringify');
|
||||
}
|
||||
|
||||
case 'number':
|
||||
// Make a fake root object containing our value under the key of ''.
|
||||
// Return the result of stringifying the value.
|
||||
return str('', {
|
||||
'': value
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
|
||||
return isFinite(value) ? String(value) : 'null';
|
||||
// If the JSON object does not yet have a parse method, give it one.
|
||||
if (typeof JSON.parse !== 'function')
|
||||
{
|
||||
JSON.parse = function(text, reviver)
|
||||
{
|
||||
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
// The parse method takes a text and an optional reviver function, and returns
|
||||
// a JavaScript value if the text is a valid JSON text.
|
||||
var j;
|
||||
|
||||
// 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
|
||||
// the remote chance that this gets fixed someday.
|
||||
function walk(holder, key)
|
||||
{
|
||||
|
||||
return String(value);
|
||||
|
||||
// If the type is 'object', we might be dealing with an object or an array or
|
||||
// null.
|
||||
|
||||
case 'object':
|
||||
|
||||
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
||||
// so watch out for that case.
|
||||
|
||||
if (!value) {
|
||||
return 'null';
|
||||
// The walk method is used to recursively walk the resulting structure so
|
||||
// that modifications can be made.
|
||||
var k, v, value = holder[key];
|
||||
if (value && typeof value === 'object')
|
||||
{
|
||||
for (k in value)
|
||||
{
|
||||
if (Object.prototype.hasOwnProperty.call(value, k))
|
||||
{
|
||||
v = walk(value, k);
|
||||
if (v !== undefined)
|
||||
{
|
||||
value[k] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete value[k];
|
||||
}
|
||||
}
|
||||
|
||||
// Make an array to hold the partial results of stringifying this object value.
|
||||
|
||||
gap += indent;
|
||||
partial = [];
|
||||
|
||||
// Is the value an array?
|
||||
|
||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
||||
|
||||
// The value is an array. Stringify every element. Use null as a placeholder
|
||||
// for non-JSON values.
|
||||
|
||||
length = value.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
partial[i] = str(i, value) || 'null';
|
||||
}
|
||||
|
||||
// Join all of the elements together, separated with commas, and wrap them in
|
||||
// brackets.
|
||||
|
||||
v = partial.length === 0 ? '[]' : gap ?
|
||||
'[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
|
||||
'[' + partial.join(',') + ']';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
|
||||
// If the replacer is an array, use it to select the members to be stringified.
|
||||
|
||||
if (rep && typeof rep === 'object') {
|
||||
length = rep.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (typeof rep[i] === 'string') {
|
||||
k = rep[i];
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Otherwise, iterate through all of the keys in the object.
|
||||
|
||||
for (k in value) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join all of the member texts together, separated with commas,
|
||||
// and wrap them in braces.
|
||||
|
||||
v = partial.length === 0 ? '{}' : gap ?
|
||||
'{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
|
||||
'{' + partial.join(',') + '}';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the JSON object does not yet have a stringify method, give it one.
|
||||
|
||||
if (typeof JSON.stringify !== 'function') {
|
||||
JSON.stringify = function (value, replacer, space) {
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// produce text that is more easily readable.
|
||||
|
||||
var i;
|
||||
gap = '';
|
||||
indent = '';
|
||||
|
||||
// If the space parameter is a number, make an indent string containing that
|
||||
// many spaces.
|
||||
|
||||
if (typeof space === 'number') {
|
||||
for (i = 0; i < space; i += 1) {
|
||||
indent += ' ';
|
||||
}
|
||||
|
||||
// If the space parameter is a string, it will be used as the indent string.
|
||||
|
||||
} else if (typeof space === 'string') {
|
||||
indent = space;
|
||||
}
|
||||
|
||||
// If there is a replacer, it must be a function or an array.
|
||||
// Otherwise, throw an error.
|
||||
|
||||
rep = replacer;
|
||||
if (replacer && typeof replacer !== 'function' &&
|
||||
(typeof replacer !== 'object' ||
|
||||
typeof replacer.length !== 'number')) {
|
||||
throw new Error('JSON.stringify');
|
||||
}
|
||||
|
||||
// Make a fake root object containing our value under the key of ''.
|
||||
// Return the result of stringifying the value.
|
||||
|
||||
return str('', {'': value});
|
||||
};
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}
|
||||
|
||||
|
||||
// If the JSON object does not yet have a parse method, give it one.
|
||||
// Parsing happens in four stages. In the first stage, we replace certain
|
||||
// Unicode characters with escape sequences. JavaScript handles many characters
|
||||
// incorrectly, either silently deleting them, or treating them as line endings.
|
||||
text = String(text);
|
||||
cx.lastIndex = 0;
|
||||
if (cx.test(text))
|
||||
{
|
||||
text = text.replace(cx, function(a)
|
||||
{
|
||||
return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof JSON.parse !== 'function') {
|
||||
JSON.parse = function (text, reviver) {
|
||||
// In the second stage, we run the text against regular expressions that look
|
||||
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
||||
// because they can cause invocation, and '=' because it can cause mutation.
|
||||
// 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
|
||||
// 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 all simple value tokens with ']' characters. Third, we delete all
|
||||
// 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
|
||||
// ',' 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, '')))
|
||||
{
|
||||
|
||||
// The parse method takes a text and an optional reviver function, and returns
|
||||
// a JavaScript value if the text is a valid JSON text.
|
||||
// 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
|
||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||
// in parens to eliminate the ambiguity.
|
||||
j = eval('(' + text + ')');
|
||||
|
||||
var j;
|
||||
// In the optional fourth stage, we recursively walk the new structure, passing
|
||||
// each name/value pair to a reviver function for possible transformation.
|
||||
return typeof reviver === 'function' ? walk(
|
||||
{
|
||||
'': j
|
||||
}, '') : j;
|
||||
}
|
||||
|
||||
function walk(holder, key) {
|
||||
|
||||
// The walk method is used to recursively walk the resulting structure so
|
||||
// that modifications can be made.
|
||||
|
||||
var k, v, value = holder[key];
|
||||
if (value && typeof value === 'object') {
|
||||
for (k in value) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}
|
||||
|
||||
|
||||
// Parsing happens in four stages. In the first stage, we replace certain
|
||||
// Unicode characters with escape sequences. JavaScript handles many characters
|
||||
// incorrectly, either silently deleting them, or treating them as line endings.
|
||||
|
||||
text = String(text);
|
||||
cx.lastIndex = 0;
|
||||
if (cx.test(text)) {
|
||||
text = text.replace(cx, function (a) {
|
||||
return '\\u' +
|
||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
|
||||
// In the second stage, we run the text against regular expressions that look
|
||||
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
||||
// because they can cause invocation, and '=' because it can cause mutation.
|
||||
// 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
|
||||
// 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 all simple value tokens with ']' characters. Third, we delete all
|
||||
// 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
|
||||
// ',' 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, ''))) {
|
||||
|
||||
// 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
|
||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||
// in parens to eliminate the ambiguity.
|
||||
|
||||
j = eval('(' + text + ')');
|
||||
|
||||
// In the optional fourth stage, we recursively walk the new structure, passing
|
||||
// each name/value pair to a reviver function for possible transformation.
|
||||
|
||||
return typeof reviver === 'function' ?
|
||||
walk({'': j}, '') : j;
|
||||
}
|
||||
|
||||
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||
|
||||
throw new SyntaxError('JSON.parse');
|
||||
};
|
||||
}
|
||||
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||
throw new SyntaxError('JSON.parse');
|
||||
};
|
||||
}
|
||||
}());
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter
|
||||
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
|
||||
// %APPJET%: import("etherpad.admin.plugins");
|
||||
|
||||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
|
@ -22,32 +21,36 @@
|
|||
// requires: top
|
||||
// requires: plugins
|
||||
// requires: undefined
|
||||
|
||||
var linestylefilter = {};
|
||||
|
||||
linestylefilter.ATTRIB_CLASSES = {
|
||||
'bold':'tag:b',
|
||||
'italic':'tag:i',
|
||||
'underline':'tag:u',
|
||||
'strikethrough':'tag:s'
|
||||
'bold': 'tag:b',
|
||||
'italic': 'tag:i',
|
||||
'underline': 'tag:u',
|
||||
'strikethrough': 'tag:s'
|
||||
};
|
||||
|
||||
linestylefilter.getAuthorClassName = function(author) {
|
||||
return "author-"+author.replace(/[^a-y0-9]/g, function(c) {
|
||||
linestylefilter.getAuthorClassName = function(author)
|
||||
{
|
||||
return "author-" + author.replace(/[^a-y0-9]/g, function(c)
|
||||
{
|
||||
if (c == ".") return "-";
|
||||
return 'z'+c.charCodeAt(0)+'z';
|
||||
return 'z' + c.charCodeAt(0) + 'z';
|
||||
});
|
||||
};
|
||||
|
||||
// lineLength is without newline; aline includes newline,
|
||||
// but may be falsy if lineLength == 0
|
||||
linestylefilter.getLineStyleFilter = function(lineLength, aline,
|
||||
textAndClassFunc, apool) {
|
||||
linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool)
|
||||
{
|
||||
|
||||
var plugins_;
|
||||
if (typeof(plugins)!='undefined') {
|
||||
if (typeof(plugins) != 'undefined')
|
||||
{
|
||||
plugins_ = plugins;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
plugins_ = parent.parent.plugins;
|
||||
}
|
||||
|
||||
|
@ -55,109 +58,139 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline,
|
|||
|
||||
var nextAfterAuthorColors = textAndClassFunc;
|
||||
|
||||
var authorColorFunc = (function() {
|
||||
var authorColorFunc = (function()
|
||||
{
|
||||
var lineEnd = lineLength;
|
||||
var curIndex = 0;
|
||||
var extraClasses;
|
||||
var leftInAuthor;
|
||||
|
||||
function attribsToClasses(attribs) {
|
||||
function attribsToClasses(attribs)
|
||||
{
|
||||
var classes = '';
|
||||
Changeset.eachAttribNumber(attribs, function(n) {
|
||||
var key = apool.getAttribKey(n);
|
||||
if (key) {
|
||||
var value = apool.getAttribValue(n);
|
||||
if (value) {
|
||||
if (key == 'author') {
|
||||
classes += ' '+linestylefilter.getAuthorClassName(value);
|
||||
}
|
||||
else if (key == 'list') {
|
||||
classes += ' list:'+value;
|
||||
Changeset.eachAttribNumber(attribs, function(n)
|
||||
{
|
||||
var key = apool.getAttribKey(n);
|
||||
if (key)
|
||||
{
|
||||
var value = apool.getAttribValue(n);
|
||||
if (value)
|
||||
{
|
||||
if (key == 'author')
|
||||
{
|
||||
classes += ' ' + linestylefilter.getAuthorClassName(value);
|
||||
}
|
||||
else if (linestylefilter.ATTRIB_CLASSES[key]) {
|
||||
classes += ' '+linestylefilter.ATTRIB_CLASSES[key];
|
||||
} else {
|
||||
classes += plugins_.callHookStr("aceAttribsToClasses", {linestylefilter:linestylefilter, key:key, value:value}, " ", " ", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == 'list')
|
||||
{
|
||||
classes += ' list:' + value;
|
||||
}
|
||||
else if (linestylefilter.ATTRIB_CLASSES[key])
|
||||
{
|
||||
classes += ' ' + linestylefilter.ATTRIB_CLASSES[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
classes += plugins_.callHookStr("aceAttribsToClasses", {
|
||||
linestylefilter: linestylefilter,
|
||||
key: key,
|
||||
value: value
|
||||
}, " ", " ", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return classes.substring(1);
|
||||
}
|
||||
|
||||
var attributionIter = Changeset.opIterator(aline);
|
||||
var nextOp, nextOpClasses;
|
||||
function goNextOp() {
|
||||
|
||||
function goNextOp()
|
||||
{
|
||||
nextOp = attributionIter.next();
|
||||
nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
|
||||
}
|
||||
goNextOp();
|
||||
function nextClasses() {
|
||||
if (curIndex < lineEnd) {
|
||||
extraClasses = nextOpClasses;
|
||||
leftInAuthor = nextOp.chars;
|
||||
goNextOp();
|
||||
while (nextOp.opcode && nextOpClasses == extraClasses) {
|
||||
leftInAuthor += nextOp.chars;
|
||||
goNextOp();
|
||||
}
|
||||
|
||||
function nextClasses()
|
||||
{
|
||||
if (curIndex < lineEnd)
|
||||
{
|
||||
extraClasses = nextOpClasses;
|
||||
leftInAuthor = nextOp.chars;
|
||||
goNextOp();
|
||||
while (nextOp.opcode && nextOpClasses == extraClasses)
|
||||
{
|
||||
leftInAuthor += nextOp.chars;
|
||||
goNextOp();
|
||||
}
|
||||
}
|
||||
}
|
||||
nextClasses();
|
||||
|
||||
return function(txt, cls) {
|
||||
while (txt.length > 0) {
|
||||
if (leftInAuthor <= 0) {
|
||||
// prevent infinite loop if something funny's going on
|
||||
return nextAfterAuthorColors(txt, cls);
|
||||
}
|
||||
var spanSize = txt.length;
|
||||
if (spanSize > leftInAuthor) {
|
||||
spanSize = leftInAuthor;
|
||||
}
|
||||
var curTxt = txt.substring(0, spanSize);
|
||||
txt = txt.substring(spanSize);
|
||||
nextAfterAuthorColors(curTxt, (cls&&cls+" ")+extraClasses);
|
||||
curIndex += spanSize;
|
||||
leftInAuthor -= spanSize;
|
||||
if (leftInAuthor == 0) {
|
||||
nextClasses();
|
||||
}
|
||||
return function(txt, cls)
|
||||
{
|
||||
while (txt.length > 0)
|
||||
{
|
||||
if (leftInAuthor <= 0)
|
||||
{
|
||||
// prevent infinite loop if something funny's going on
|
||||
return nextAfterAuthorColors(txt, cls);
|
||||
}
|
||||
var spanSize = txt.length;
|
||||
if (spanSize > leftInAuthor)
|
||||
{
|
||||
spanSize = leftInAuthor;
|
||||
}
|
||||
var curTxt = txt.substring(0, spanSize);
|
||||
txt = txt.substring(spanSize);
|
||||
nextAfterAuthorColors(curTxt, (cls && cls + " ") + extraClasses);
|
||||
curIndex += spanSize;
|
||||
leftInAuthor -= spanSize;
|
||||
if (leftInAuthor == 0)
|
||||
{
|
||||
nextClasses();
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
return authorColorFunc;
|
||||
};
|
||||
|
||||
linestylefilter.getAtSignSplitterFilter = function(lineText,
|
||||
textAndClassFunc) {
|
||||
linestylefilter.getAtSignSplitterFilter = function(lineText, textAndClassFunc)
|
||||
{
|
||||
var at = /@/g;
|
||||
at.lastIndex = 0;
|
||||
var splitPoints = null;
|
||||
var execResult;
|
||||
while ((execResult = at.exec(lineText))) {
|
||||
if (! splitPoints) {
|
||||
while ((execResult = at.exec(lineText)))
|
||||
{
|
||||
if (!splitPoints)
|
||||
{
|
||||
splitPoints = [];
|
||||
}
|
||||
splitPoints.push(execResult.index);
|
||||
}
|
||||
|
||||
if (! splitPoints) return textAndClassFunc;
|
||||
if (!splitPoints) return textAndClassFunc;
|
||||
|
||||
return linestylefilter.textAndClassFuncSplitter(textAndClassFunc,
|
||||
splitPoints);
|
||||
return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints);
|
||||
};
|
||||
|
||||
linestylefilter.getRegexpFilter = function (regExp, tag) {
|
||||
return function (lineText, textAndClassFunc) {
|
||||
linestylefilter.getRegexpFilter = function(regExp, tag)
|
||||
{
|
||||
return function(lineText, textAndClassFunc)
|
||||
{
|
||||
regExp.lastIndex = 0;
|
||||
var regExpMatchs = null;
|
||||
var splitPoints = null;
|
||||
var execResult;
|
||||
while ((execResult = regExp.exec(lineText))) {
|
||||
if (! regExpMatchs) {
|
||||
regExpMatchs = [];
|
||||
splitPoints = [];
|
||||
while ((execResult = regExp.exec(lineText)))
|
||||
{
|
||||
if (!regExpMatchs)
|
||||
{
|
||||
regExpMatchs = [];
|
||||
splitPoints = [];
|
||||
}
|
||||
var startIndex = execResult.index;
|
||||
var regExpMatch = execResult[0];
|
||||
|
@ -165,126 +198,147 @@ linestylefilter.getRegexpFilter = function (regExp, tag) {
|
|||
splitPoints.push(startIndex, startIndex + regExpMatch.length);
|
||||
}
|
||||
|
||||
if (! regExpMatchs) return textAndClassFunc;
|
||||
if (!regExpMatchs) return textAndClassFunc;
|
||||
|
||||
function regExpMatchForIndex(idx) {
|
||||
for(var k=0; k<regExpMatchs.length; k++) {
|
||||
var u = regExpMatchs[k];
|
||||
if (idx >= u[0] && idx < u[0]+u[1].length) {
|
||||
return u[1];
|
||||
}
|
||||
function regExpMatchForIndex(idx)
|
||||
{
|
||||
for (var k = 0; k < regExpMatchs.length; k++)
|
||||
{
|
||||
var u = regExpMatchs[k];
|
||||
if (idx >= u[0] && idx < u[0] + u[1].length)
|
||||
{
|
||||
return u[1];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var handleRegExpMatchsAfterSplit = (function() {
|
||||
var handleRegExpMatchsAfterSplit = (function()
|
||||
{
|
||||
var curIndex = 0;
|
||||
return function(txt, cls) {
|
||||
var txtlen = txt.length;
|
||||
var newCls = cls;
|
||||
var regExpMatch = regExpMatchForIndex(curIndex);
|
||||
if (regExpMatch) {
|
||||
newCls += " "+tag+":"+regExpMatch;
|
||||
}
|
||||
textAndClassFunc(txt, newCls);
|
||||
curIndex += txtlen;
|
||||
return function(txt, cls)
|
||||
{
|
||||
var txtlen = txt.length;
|
||||
var newCls = cls;
|
||||
var regExpMatch = regExpMatchForIndex(curIndex);
|
||||
if (regExpMatch)
|
||||
{
|
||||
newCls += " " + tag + ":" + regExpMatch;
|
||||
}
|
||||
textAndClassFunc(txt, newCls);
|
||||
curIndex += txtlen;
|
||||
};
|
||||
})();
|
||||
|
||||
return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit,
|
||||
splitPoints);
|
||||
return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
linestylefilter.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]/;
|
||||
linestylefilter.REGEX_URLCHAR = new RegExp('('+/[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source+'|'+linestylefilter.REGEX_WORDCHAR.source+')');
|
||||
linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source+linestylefilter.REGEX_URLCHAR.source+'*(?![:.,;])'+linestylefilter.REGEX_URLCHAR.source, 'g');
|
||||
linestylefilter.REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + linestylefilter.REGEX_WORDCHAR.source + ')');
|
||||
linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g');
|
||||
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 idx = 0;
|
||||
|
||||
// don't split at 0
|
||||
while (splitPointsOpt &&
|
||||
nextPointIndex < splitPointsOpt.length &&
|
||||
splitPointsOpt[nextPointIndex] == 0) {
|
||||
while (splitPointsOpt && nextPointIndex < splitPointsOpt.length && splitPointsOpt[nextPointIndex] == 0)
|
||||
{
|
||||
nextPointIndex++;
|
||||
}
|
||||
|
||||
function spanHandler(txt, cls) {
|
||||
if ((! splitPointsOpt) || nextPointIndex >= splitPointsOpt.length) {
|
||||
function spanHandler(txt, cls)
|
||||
{
|
||||
if ((!splitPointsOpt) || nextPointIndex >= splitPointsOpt.length)
|
||||
{
|
||||
func(txt, cls);
|
||||
idx += txt.length;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
var splitPoints = splitPointsOpt;
|
||||
var pointLocInSpan = splitPoints[nextPointIndex] - idx;
|
||||
var txtlen = txt.length;
|
||||
if (pointLocInSpan >= txtlen) {
|
||||
func(txt, cls);
|
||||
idx += txt.length;
|
||||
if (pointLocInSpan == txtlen) {
|
||||
nextPointIndex++;
|
||||
}
|
||||
if (pointLocInSpan >= txtlen)
|
||||
{
|
||||
func(txt, cls);
|
||||
idx += txt.length;
|
||||
if (pointLocInSpan == txtlen)
|
||||
{
|
||||
nextPointIndex++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pointLocInSpan > 0) {
|
||||
func(txt.substring(0, pointLocInSpan), cls);
|
||||
idx += pointLocInSpan;
|
||||
}
|
||||
nextPointIndex++;
|
||||
// recurse
|
||||
spanHandler(txt.substring(pointLocInSpan), cls);
|
||||
else
|
||||
{
|
||||
if (pointLocInSpan > 0)
|
||||
{
|
||||
func(txt.substring(0, pointLocInSpan), cls);
|
||||
idx += pointLocInSpan;
|
||||
}
|
||||
nextPointIndex++;
|
||||
// recurse
|
||||
spanHandler(txt.substring(pointLocInSpan), cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
return spanHandler;
|
||||
};
|
||||
|
||||
linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser) {
|
||||
var func = linestylefilter.getURLFilter(lineText, textAndClassFunc);
|
||||
linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser)
|
||||
{
|
||||
var func = linestylefilter.getURLFilter(lineText, textAndClassFunc);
|
||||
|
||||
var plugins_;
|
||||
if (typeof(plugins)!='undefined') {
|
||||
if (typeof(plugins) != 'undefined')
|
||||
{
|
||||
plugins_ = plugins;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
plugins_ = parent.parent.plugins;
|
||||
}
|
||||
|
||||
var hookFilters = plugins_.callHook(
|
||||
"aceGetFilterStack", {linestylefilter:linestylefilter, browser:browser});
|
||||
hookFilters.map(function (hookFilter) {
|
||||
var hookFilters = plugins_.callHook("aceGetFilterStack", {
|
||||
linestylefilter: linestylefilter,
|
||||
browser: browser
|
||||
});
|
||||
hookFilters.map(function(hookFilter)
|
||||
{
|
||||
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.
|
||||
// We then normalize it back to text with no angle brackets. It's weird. So always
|
||||
// break spans at an "at" sign.
|
||||
func = linestylefilter.getAtSignSplitterFilter(
|
||||
lineText, func);
|
||||
lineText, func);
|
||||
}
|
||||
return func;
|
||||
};
|
||||
|
||||
// domLineObj is like that returned by domline.createDomLine
|
||||
linestylefilter.populateDomLine = function(textLine, aline, apool,
|
||||
domLineObj) {
|
||||
linestylefilter.populateDomLine = function(textLine, aline, apool, domLineObj)
|
||||
{
|
||||
// remove final newline from text if any
|
||||
var text = textLine;
|
||||
if (text.slice(-1) == '\n') {
|
||||
text = text.substring(0, text.length-1);
|
||||
if (text.slice(-1) == '\n')
|
||||
{
|
||||
text = text.substring(0, text.length - 1);
|
||||
}
|
||||
|
||||
function textAndClassFunc(tokenText, tokenClass) {
|
||||
function textAndClassFunc(tokenText, tokenClass)
|
||||
{
|
||||
domLineObj.appendSpan(tokenText, tokenClass);
|
||||
}
|
||||
|
||||
var func = linestylefilter.getFilterStack(text, textAndClassFunc);
|
||||
func = linestylefilter.getLineStyleFilter(text.length, aline,
|
||||
func, apool);
|
||||
func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool);
|
||||
func(text, '');
|
||||
};
|
||||
|
|
|
@ -28,9 +28,9 @@ linestylefilter.ATTRIB_CLASSES = {
|
|||
'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 "-";
|
||||
return 'z' + c.charCodeAt(0) + 'z';
|
||||
|
@ -39,11 +39,11 @@ linestylefilter.getAuthorClassName = function (author)
|
|||
|
||||
// lineLength is without newline; aline includes newline,
|
||||
// but may be falsy if lineLength == 0
|
||||
linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFunc, apool)
|
||||
linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool)
|
||||
{
|
||||
|
||||
var plugins_;
|
||||
if (typeof (plugins) != 'undefined')
|
||||
if (typeof(plugins) != 'undefined')
|
||||
{
|
||||
plugins_ = plugins;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFu
|
|||
|
||||
var nextAfterAuthorColors = textAndClassFunc;
|
||||
|
||||
var authorColorFunc = (function ()
|
||||
var authorColorFunc = (function()
|
||||
{
|
||||
var lineEnd = lineLength;
|
||||
var curIndex = 0;
|
||||
|
@ -66,7 +66,7 @@ linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFu
|
|||
function attribsToClasses(attribs)
|
||||
{
|
||||
var classes = '';
|
||||
Changeset.eachAttribNumber(attribs, function (n)
|
||||
Changeset.eachAttribNumber(attribs, function(n)
|
||||
{
|
||||
var key = apool.getAttribKey(n);
|
||||
if (key)
|
||||
|
@ -126,7 +126,7 @@ linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFu
|
|||
}
|
||||
nextClasses();
|
||||
|
||||
return function (txt, cls)
|
||||
return function(txt, cls)
|
||||
{
|
||||
while (txt.length > 0)
|
||||
{
|
||||
|
@ -155,7 +155,7 @@ linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFu
|
|||
return authorColorFunc;
|
||||
};
|
||||
|
||||
linestylefilter.getAtSignSplitterFilter = function (lineText, textAndClassFunc)
|
||||
linestylefilter.getAtSignSplitterFilter = function(lineText, textAndClassFunc)
|
||||
{
|
||||
var at = /@/g;
|
||||
at.lastIndex = 0;
|
||||
|
@ -175,9 +175,9 @@ linestylefilter.getAtSignSplitterFilter = function (lineText, textAndClassFunc)
|
|||
return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints);
|
||||
};
|
||||
|
||||
linestylefilter.getRegexpFilter = function (regExp, tag)
|
||||
linestylefilter.getRegexpFilter = function(regExp, tag)
|
||||
{
|
||||
return function (lineText, textAndClassFunc)
|
||||
return function(lineText, textAndClassFunc)
|
||||
{
|
||||
regExp.lastIndex = 0;
|
||||
var regExpMatchs = null;
|
||||
|
@ -211,10 +211,10 @@ linestylefilter.getRegexpFilter = function (regExp, tag)
|
|||
return false;
|
||||
}
|
||||
|
||||
var handleRegExpMatchsAfterSplit = (function ()
|
||||
var handleRegExpMatchsAfterSplit = (function()
|
||||
{
|
||||
var curIndex = 0;
|
||||
return function (txt, cls)
|
||||
return function(txt, cls)
|
||||
{
|
||||
var txtlen = txt.length;
|
||||
var newCls = cls;
|
||||
|
@ -239,7 +239,7 @@ linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs
|
|||
linestylefilter.getURLFilter = linestylefilter.getRegexpFilter(
|
||||
linestylefilter.REGEX_URL, 'url');
|
||||
|
||||
linestylefilter.textAndClassFuncSplitter = function (func, splitPointsOpt)
|
||||
linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt)
|
||||
{
|
||||
var nextPointIndex = 0;
|
||||
var idx = 0;
|
||||
|
@ -287,12 +287,12 @@ linestylefilter.textAndClassFuncSplitter = function (func, splitPointsOpt)
|
|||
return spanHandler;
|
||||
};
|
||||
|
||||
linestylefilter.getFilterStack = function (lineText, textAndClassFunc, browser)
|
||||
linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser)
|
||||
{
|
||||
var func = linestylefilter.getURLFilter(lineText, textAndClassFunc);
|
||||
|
||||
var plugins_;
|
||||
if (typeof (plugins) != 'undefined')
|
||||
if (typeof(plugins) != 'undefined')
|
||||
{
|
||||
plugins_ = plugins;
|
||||
}
|
||||
|
@ -305,7 +305,7 @@ linestylefilter.getFilterStack = function (lineText, textAndClassFunc, browser)
|
|||
linestylefilter: linestylefilter,
|
||||
browser: browser
|
||||
});
|
||||
hookFilters.map(function (hookFilter)
|
||||
hookFilters.map(function(hookFilter)
|
||||
{
|
||||
func = hookFilter(lineText, func);
|
||||
});
|
||||
|
@ -322,7 +322,7 @@ linestylefilter.getFilterStack = function (lineText, textAndClassFunc, browser)
|
|||
};
|
||||
|
||||
// domLineObj is like that returned by domline.createDomLine
|
||||
linestylefilter.populateDomLine = function (textLine, aline, apool, domLineObj)
|
||||
linestylefilter.populateDomLine = function(textLine, aline, apool, domLineObj)
|
||||
{
|
||||
// remove final newline from text if any
|
||||
var text = textLine;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -18,44 +18,52 @@
|
|||
|
||||
var socket;
|
||||
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function()
|
||||
{
|
||||
handshake();
|
||||
});
|
||||
|
||||
$(window).unload(function() {
|
||||
$(window).unload(function()
|
||||
{
|
||||
pad.dispose();
|
||||
});
|
||||
|
||||
function createCookie(name,value,days) {
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime()+(days*24*60*60*1000));
|
||||
var expires = "; expires="+date.toGMTString();
|
||||
}
|
||||
else var expires = "";
|
||||
document.cookie = name+"="+value+expires+"; path=/";
|
||||
function createCookie(name, value, days)
|
||||
{
|
||||
if (days)
|
||||
{
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
var expires = "; expires=" + date.toGMTString();
|
||||
}
|
||||
else var expires = "";
|
||||
document.cookie = name + "=" + value + expires + "; path=/";
|
||||
}
|
||||
|
||||
function readCookie(name) {
|
||||
var nameEQ = name + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for(var i=0;i < ca.length;i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0)==' ') c = c.substring(1,c.length);
|
||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
|
||||
}
|
||||
return null;
|
||||
function readCookie(name)
|
||||
{
|
||||
var nameEQ = name + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++)
|
||||
{
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function randomString() {
|
||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||
var string_length = 20;
|
||||
var randomstring = '';
|
||||
for (var i=0; i<string_length; i++) {
|
||||
var rnum = Math.floor(Math.random() * chars.length);
|
||||
randomstring += chars.substring(rnum,rnum+1);
|
||||
}
|
||||
return "t." + randomstring;
|
||||
function randomString()
|
||||
{
|
||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||
var string_length = 20;
|
||||
var randomstring = '';
|
||||
for (var i = 0; i < string_length; i++)
|
||||
{
|
||||
var rnum = Math.floor(Math.random() * chars.length);
|
||||
randomstring += chars.substring(rnum, rnum + 1);
|
||||
}
|
||||
return "t." + randomstring;
|
||||
}
|
||||
|
||||
function handshake()
|
||||
|
@ -67,67 +75,72 @@ function handshake()
|
|||
var url = loc.protocol + "//" + loc.hostname + ":" + port + "/";
|
||||
//find out in which subfolder we are
|
||||
console.log(loc.pathname);
|
||||
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);
|
||||
//connect
|
||||
socket = io.connect(url, {resource: resource});
|
||||
socket = io.connect(url, {
|
||||
resource: resource
|
||||
});
|
||||
|
||||
socket.on('connect', function(){
|
||||
var padId= document.URL.substring(document.URL.lastIndexOf("/")+1);
|
||||
|
||||
document.title = document.title + " | " + padId;
|
||||
|
||||
var token = readCookie("token");
|
||||
if(token == null)
|
||||
socket.on('connect', function()
|
||||
{
|
||||
var padId = document.URL.substring(document.URL.lastIndexOf("/") + 1);
|
||||
|
||||
document.title = document.title + " | " + padId;
|
||||
|
||||
var token = readCookie("token");
|
||||
if (token == null)
|
||||
{
|
||||
token = randomString();
|
||||
createCookie("token", token, 60);
|
||||
}
|
||||
|
||||
var msg = {
|
||||
"component": "pad",
|
||||
"type": "CLIENT_READY",
|
||||
"padId": padId,
|
||||
"token": token,
|
||||
"protocolVersion": 2
|
||||
};
|
||||
|
||||
socket.json.send(msg);
|
||||
});
|
||||
|
||||
var receivedClientVars = false;
|
||||
var initalized = false;
|
||||
|
||||
socket.on('message', function(obj)
|
||||
{
|
||||
//if we haven't recieved the clientVars yet, then this message should it be
|
||||
if (!receivedClientVars)
|
||||
{
|
||||
if (window.console) console.log(obj);
|
||||
|
||||
receivedClientVars = true;
|
||||
|
||||
clientVars = obj;
|
||||
clientVars.userAgent = navigator.userAgent;
|
||||
clientVars.collab_client_vars.clientAgent = navigator.userAgent;
|
||||
|
||||
pad.init();
|
||||
|
||||
initalized = true;
|
||||
}
|
||||
//This handles every Message after the clientVars
|
||||
else
|
||||
{
|
||||
if (obj.disconnect)
|
||||
{
|
||||
token = randomString();
|
||||
createCookie("token", token, 60);
|
||||
socket.disconnect();
|
||||
padconnectionstatus.disconnected("userdup");
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = { "component" : "pad",
|
||||
"type":"CLIENT_READY",
|
||||
"padId": padId,
|
||||
"token": token,
|
||||
"protocolVersion": 2};
|
||||
|
||||
socket.json.send(msg);
|
||||
});
|
||||
|
||||
var receivedClientVars=false;
|
||||
var initalized = false;
|
||||
|
||||
socket.on('message', function(obj){
|
||||
//if we haven't recieved the clientVars yet, then this message should it be
|
||||
if(!receivedClientVars)
|
||||
{
|
||||
if(window.console)
|
||||
console.log(obj);
|
||||
|
||||
receivedClientVars=true;
|
||||
|
||||
clientVars = obj;
|
||||
clientVars.userAgent=navigator.userAgent;
|
||||
clientVars.collab_client_vars.clientAgent=navigator.userAgent;
|
||||
|
||||
pad.init();
|
||||
|
||||
initalized=true;
|
||||
}
|
||||
//This handles every Message after the clientVars
|
||||
else
|
||||
{
|
||||
if(obj.disconnect)
|
||||
{
|
||||
socket.disconnect();
|
||||
padconnectionstatus.disconnected("userdup");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
pad.collabClient.handleMessageFromServer(obj);
|
||||
}
|
||||
pad.collabClient.handleMessageFromServer(obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var pad = {
|
||||
|
@ -142,43 +155,75 @@ var pad = {
|
|||
padOptions: {},
|
||||
|
||||
// these don't require init; clientVars should all go through here
|
||||
getPadId: function() { return clientVars.padId; },
|
||||
getClientIp: function() { return clientVars.clientIp; },
|
||||
getIsProPad: function() { return clientVars.isProPad; },
|
||||
getColorPalette: function() { return clientVars.colorPalette; },
|
||||
getDisplayUserAgent: function() {
|
||||
getPadId: function()
|
||||
{
|
||||
return clientVars.padId;
|
||||
},
|
||||
getClientIp: function()
|
||||
{
|
||||
return clientVars.clientIp;
|
||||
},
|
||||
getIsProPad: function()
|
||||
{
|
||||
return clientVars.isProPad;
|
||||
},
|
||||
getColorPalette: function()
|
||||
{
|
||||
return clientVars.colorPalette;
|
||||
},
|
||||
getDisplayUserAgent: function()
|
||||
{
|
||||
return padutils.uaDisplay(clientVars.userAgent);
|
||||
},
|
||||
getIsDebugEnabled: function() { return clientVars.debugEnabled; },
|
||||
getPrivilege: function(name) { return clientVars.accountPrivs[name]; },
|
||||
getUserIsGuest: function() { return clientVars.userIsGuest; },
|
||||
getIsDebugEnabled: function()
|
||||
{
|
||||
return clientVars.debugEnabled;
|
||||
},
|
||||
getPrivilege: function(name)
|
||||
{
|
||||
return clientVars.accountPrivs[name];
|
||||
},
|
||||
getUserIsGuest: function()
|
||||
{
|
||||
return clientVars.userIsGuest;
|
||||
},
|
||||
//
|
||||
|
||||
getUserId: function() { return pad.myUserInfo.userId; },
|
||||
getUserName: function() { return pad.myUserInfo.name; },
|
||||
sendClientMessage: function(msg) {
|
||||
getUserId: function()
|
||||
{
|
||||
return pad.myUserInfo.userId;
|
||||
},
|
||||
getUserName: function()
|
||||
{
|
||||
return pad.myUserInfo.name;
|
||||
},
|
||||
sendClientMessage: function(msg)
|
||||
{
|
||||
pad.collabClient.sendClientMessage(msg);
|
||||
},
|
||||
|
||||
init: function() {
|
||||
init: function()
|
||||
{
|
||||
pad.diagnosticInfo.uniqueId = padutils.uniqueId();
|
||||
pad.initTime = +(new Date());
|
||||
pad.padOptions = clientVars.initialOptions;
|
||||
|
||||
if ((! $.browser.msie) &&
|
||||
(! ($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0))) {
|
||||
if ((!$.browser.msie) && (!($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0)))
|
||||
{
|
||||
document.domain = document.domain; // for comet
|
||||
}
|
||||
|
||||
// for IE
|
||||
if ($.browser.msie) {
|
||||
try {
|
||||
if ($.browser.msie)
|
||||
{
|
||||
try
|
||||
{
|
||||
doc.execCommand("BackgroundImageCache", false, true);
|
||||
} catch (e) {}
|
||||
}
|
||||
catch (e)
|
||||
{}
|
||||
}
|
||||
|
||||
// order of inits is important here:
|
||||
|
||||
padcookie.init(clientVars.cookiePrefsToSet);
|
||||
|
||||
$("#widthprefcheck").click(pad.toggleWidthPref);
|
||||
|
@ -191,33 +236,34 @@ var pad = {
|
|||
colorId: clientVars.userColor,
|
||||
userAgent: pad.getDisplayUserAgent()
|
||||
};
|
||||
if (clientVars.specialKey) {
|
||||
if (clientVars.specialKey)
|
||||
{
|
||||
pad.myUserInfo.specialKey = clientVars.specialKey;
|
||||
if (clientVars.specialKeyTranslation) {
|
||||
$("#specialkeyarea").html("mode: "+
|
||||
String(clientVars.specialKeyTranslation).toUpperCase());
|
||||
if (clientVars.specialKeyTranslation)
|
||||
{
|
||||
$("#specialkeyarea").html("mode: " + String(clientVars.specialKeyTranslation).toUpperCase());
|
||||
}
|
||||
}
|
||||
paddocbar.init({isTitleEditable: pad.getIsProPad(),
|
||||
initialTitle:clientVars.initialTitle,
|
||||
initialPassword:clientVars.initialPassword,
|
||||
guestPolicy: pad.padOptions.guestPolicy
|
||||
});
|
||||
paddocbar.init(
|
||||
{
|
||||
isTitleEditable: pad.getIsProPad(),
|
||||
initialTitle: clientVars.initialTitle,
|
||||
initialPassword: clientVars.initialPassword,
|
||||
guestPolicy: pad.padOptions.guestPolicy
|
||||
});
|
||||
padimpexp.init();
|
||||
padsavedrevs.init(clientVars.initialRevisionList);
|
||||
|
||||
padeditor.init(postAceInit, pad.padOptions.view || {});
|
||||
|
||||
paduserlist.init(pad.myUserInfo);
|
||||
// padchat.init(clientVars.chatHistory, pad.myUserInfo);
|
||||
// padchat.init(clientVars.chatHistory, pad.myUserInfo);
|
||||
padconnectionstatus.init();
|
||||
padmodals.init();
|
||||
|
||||
pad.collabClient =
|
||||
getCollabClient(padeditor.ace,
|
||||
clientVars.collab_client_vars,
|
||||
pad.myUserInfo,
|
||||
{ colorPalette: pad.getColorPalette() });
|
||||
pad.collabClient = getCollabClient(padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo, {
|
||||
colorPalette: pad.getColorPalette()
|
||||
});
|
||||
pad.collabClient.setOnUserJoin(pad.handleUserJoin);
|
||||
pad.collabClient.setOnUpdateUserInfo(pad.handleUserUpdate);
|
||||
pad.collabClient.setOnUserLeave(pad.handleUserLeave);
|
||||
|
@ -226,180 +272,235 @@ var pad = {
|
|||
pad.collabClient.setOnChannelStateChange(pad.handleChannelStateChange);
|
||||
pad.collabClient.setOnInternalAction(pad.handleCollabAction);
|
||||
|
||||
function postAceInit() {
|
||||
function postAceInit()
|
||||
{
|
||||
padeditbar.init();
|
||||
setTimeout(function() { padeditor.ace.focus(); }, 0);
|
||||
setTimeout(function()
|
||||
{
|
||||
padeditor.ace.focus();
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
dispose: function() {
|
||||
dispose: function()
|
||||
{
|
||||
padeditor.dispose();
|
||||
},
|
||||
notifyChangeName: function(newName) {
|
||||
notifyChangeName: function(newName)
|
||||
{
|
||||
pad.myUserInfo.name = newName;
|
||||
pad.collabClient.updateUserInfo(pad.myUserInfo);
|
||||
//padchat.handleUserJoinOrUpdate(pad.myUserInfo);
|
||||
},
|
||||
notifyChangeColor: function(newColorId) {
|
||||
notifyChangeColor: function(newColorId)
|
||||
{
|
||||
pad.myUserInfo.colorId = newColorId;
|
||||
pad.collabClient.updateUserInfo(pad.myUserInfo);
|
||||
//padchat.handleUserJoinOrUpdate(pad.myUserInfo);
|
||||
},
|
||||
notifyChangeTitle: function(newTitle) {
|
||||
pad.collabClient.sendClientMessage({
|
||||
notifyChangeTitle: function(newTitle)
|
||||
{
|
||||
pad.collabClient.sendClientMessage(
|
||||
{
|
||||
type: 'padtitle',
|
||||
title: newTitle,
|
||||
changedBy: pad.myUserInfo.name || "unnamed"
|
||||
});
|
||||
},
|
||||
notifyChangePassword: function(newPass) {
|
||||
pad.collabClient.sendClientMessage({
|
||||
notifyChangePassword: function(newPass)
|
||||
{
|
||||
pad.collabClient.sendClientMessage(
|
||||
{
|
||||
type: 'padpassword',
|
||||
password: newPass,
|
||||
changedBy: pad.myUserInfo.name || "unnamed"
|
||||
});
|
||||
},
|
||||
changePadOption: function(key, value) {
|
||||
changePadOption: function(key, value)
|
||||
{
|
||||
var options = {};
|
||||
options[key] = value;
|
||||
pad.handleOptionsChange(options);
|
||||
pad.collabClient.sendClientMessage({
|
||||
pad.collabClient.sendClientMessage(
|
||||
{
|
||||
type: 'padoptions',
|
||||
options: options,
|
||||
changedBy: pad.myUserInfo.name || "unnamed"
|
||||
});
|
||||
},
|
||||
changeViewOption: function(key, value) {
|
||||
var options = {view: {}};
|
||||
changeViewOption: function(key, value)
|
||||
{
|
||||
var options = {
|
||||
view: {}
|
||||
};
|
||||
options.view[key] = value;
|
||||
pad.handleOptionsChange(options);
|
||||
pad.collabClient.sendClientMessage({
|
||||
pad.collabClient.sendClientMessage(
|
||||
{
|
||||
type: 'padoptions',
|
||||
options: options,
|
||||
changedBy: pad.myUserInfo.name || "unnamed"
|
||||
});
|
||||
},
|
||||
handleOptionsChange: function(opts) {
|
||||
handleOptionsChange: function(opts)
|
||||
{
|
||||
// opts object is a full set of options or just
|
||||
// some options to change
|
||||
if (opts.view) {
|
||||
if (! pad.padOptions.view) {
|
||||
if (opts.view)
|
||||
{
|
||||
if (!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];
|
||||
}
|
||||
padeditor.setViewOptions(pad.padOptions.view);
|
||||
}
|
||||
if (opts.guestPolicy) {
|
||||
if (opts.guestPolicy)
|
||||
{
|
||||
// order important here
|
||||
pad.padOptions.guestPolicy = opts.guestPolicy;
|
||||
paddocbar.setGuestPolicy(opts.guestPolicy);
|
||||
}
|
||||
},
|
||||
getPadOptions: function() {
|
||||
getPadOptions: function()
|
||||
{
|
||||
// caller shouldn't mutate the object
|
||||
return pad.padOptions;
|
||||
},
|
||||
isPadPublic: function() {
|
||||
return (! pad.getIsProPad()) || (pad.getPadOptions().guestPolicy == 'allow');
|
||||
isPadPublic: function()
|
||||
{
|
||||
return (!pad.getIsProPad()) || (pad.getPadOptions().guestPolicy == 'allow');
|
||||
},
|
||||
suggestUserName: function(userId, name) {
|
||||
pad.collabClient.sendClientMessage({
|
||||
suggestUserName: function(userId, name)
|
||||
{
|
||||
pad.collabClient.sendClientMessage(
|
||||
{
|
||||
type: 'suggestUserName',
|
||||
unnamedId: userId,
|
||||
newName: name
|
||||
});
|
||||
},
|
||||
handleUserJoin: function(userInfo) {
|
||||
handleUserJoin: function(userInfo)
|
||||
{
|
||||
paduserlist.userJoinOrUpdate(userInfo);
|
||||
//padchat.handleUserJoinOrUpdate(userInfo);
|
||||
},
|
||||
handleUserUpdate: function(userInfo) {
|
||||
handleUserUpdate: function(userInfo)
|
||||
{
|
||||
paduserlist.userJoinOrUpdate(userInfo);
|
||||
//padchat.handleUserJoinOrUpdate(userInfo);
|
||||
},
|
||||
handleUserLeave: function(userInfo) {
|
||||
handleUserLeave: function(userInfo)
|
||||
{
|
||||
paduserlist.userLeave(userInfo);
|
||||
//padchat.handleUserLeave(userInfo);
|
||||
},
|
||||
handleClientMessage: function(msg) {
|
||||
if (msg.type == 'suggestUserName') {
|
||||
if (msg.unnamedId == pad.myUserInfo.userId && msg.newName &&
|
||||
! pad.myUserInfo.name) {
|
||||
handleClientMessage: function(msg)
|
||||
{
|
||||
if (msg.type == 'suggestUserName')
|
||||
{
|
||||
if (msg.unnamedId == pad.myUserInfo.userId && msg.newName && !pad.myUserInfo.name)
|
||||
{
|
||||
pad.notifyChangeName(msg.newName);
|
||||
paduserlist.setMyUserInfo(pad.myUserInfo);
|
||||
}
|
||||
}
|
||||
else if (msg.type == 'chat') {
|
||||
else if (msg.type == 'chat')
|
||||
{
|
||||
//padchat.receiveChat(msg);
|
||||
}
|
||||
else if (msg.type == 'padtitle') {
|
||||
else if (msg.type == 'padtitle')
|
||||
{
|
||||
paddocbar.changeTitle(msg.title);
|
||||
}
|
||||
else if (msg.type == 'padpassword') {
|
||||
else if (msg.type == 'padpassword')
|
||||
{
|
||||
paddocbar.changePassword(msg.password);
|
||||
}
|
||||
else if (msg.type == 'newRevisionList') {
|
||||
else if (msg.type == 'newRevisionList')
|
||||
{
|
||||
padsavedrevs.newRevisionList(msg.revisionList);
|
||||
}
|
||||
else if (msg.type == 'revisionLabel') {
|
||||
else if (msg.type == 'revisionLabel')
|
||||
{
|
||||
padsavedrevs.newRevisionList(msg.revisionList);
|
||||
}
|
||||
else if (msg.type == 'padoptions') {
|
||||
else if (msg.type == 'padoptions')
|
||||
{
|
||||
var opts = msg.options;
|
||||
pad.handleOptionsChange(opts);
|
||||
}
|
||||
else if (msg.type == 'guestanswer') {
|
||||
else if (msg.type == 'guestanswer')
|
||||
{
|
||||
// someone answered a prompt, remove it
|
||||
paduserlist.removeGuestPrompt(msg.guestId);
|
||||
}
|
||||
},
|
||||
editbarClick: function(cmd) {
|
||||
if (padeditbar) {
|
||||
editbarClick: function(cmd)
|
||||
{
|
||||
if (padeditbar)
|
||||
{
|
||||
padeditbar.toolbarClick(cmd);
|
||||
}
|
||||
},
|
||||
dmesg: function(m) {
|
||||
if (pad.getIsDebugEnabled()) {
|
||||
dmesg: function(m)
|
||||
{
|
||||
if (pad.getIsDebugEnabled())
|
||||
{
|
||||
var djs = $('#djs').get(0);
|
||||
var wasAtBottom = (djs.scrollTop - (djs.scrollHeight - $(djs).height())
|
||||
>= -20);
|
||||
$('#djs').append('<p>'+m+'</p>');
|
||||
if (wasAtBottom) {
|
||||
var wasAtBottom = (djs.scrollTop - (djs.scrollHeight - $(djs).height()) >= -20);
|
||||
$('#djs').append('<p>' + m + '</p>');
|
||||
if (wasAtBottom)
|
||||
{
|
||||
djs.scrollTop = djs.scrollHeight;
|
||||
}
|
||||
}
|
||||
},
|
||||
handleServerMessage: function(m) {
|
||||
if (m.type == 'NOTICE') {
|
||||
if (m.text) {
|
||||
alertBar.displayMessage(function (abar) {
|
||||
abar.find("#servermsgdate").html(" ("+padutils.simpleDateTime(new Date)+")");
|
||||
handleServerMessage: function(m)
|
||||
{
|
||||
if (m.type == 'NOTICE')
|
||||
{
|
||||
if (m.text)
|
||||
{
|
||||
alertBar.displayMessage(function(abar)
|
||||
{
|
||||
abar.find("#servermsgdate").html(" (" + padutils.simpleDateTime(new Date) + ")");
|
||||
abar.find("#servermsgtext").html(m.text);
|
||||
});
|
||||
}
|
||||
if (m.js) {
|
||||
window['ev'+'al'](m.js);
|
||||
if (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);
|
||||
}
|
||||
},
|
||||
handleChannelStateChange: function(newState, message) {
|
||||
handleChannelStateChange: function(newState, message)
|
||||
{
|
||||
var oldFullyConnected = !! padconnectionstatus.isFullyConnected();
|
||||
var wasConnecting = (padconnectionstatus.getStatus().what == 'connecting');
|
||||
if (newState == "CONNECTED") {
|
||||
if (newState == "CONNECTED")
|
||||
{
|
||||
padconnectionstatus.connected();
|
||||
}
|
||||
else if (newState == "RECONNECTING") {
|
||||
else if (newState == "RECONNECTING")
|
||||
{
|
||||
padconnectionstatus.reconnecting();
|
||||
}
|
||||
else if (newState == "DISCONNECTED") {
|
||||
else if (newState == "DISCONNECTED")
|
||||
{
|
||||
pad.diagnosticInfo.disconnectedMessage = message;
|
||||
pad.diagnosticInfo.padInitTime = pad.initTime;
|
||||
pad.asyncSendDiagnosticInfo();
|
||||
if (typeof window.ajlog == "string") { window.ajlog += ("Disconnected: "+message+'\n'); }
|
||||
if (typeof window.ajlog == "string")
|
||||
{
|
||||
window.ajlog += ("Disconnected: " + message + '\n');
|
||||
}
|
||||
padeditor.disable();
|
||||
padeditbar.disable();
|
||||
paddocbar.disable();
|
||||
|
@ -408,16 +509,21 @@ var pad = {
|
|||
padconnectionstatus.disconnected(message);
|
||||
}
|
||||
var newFullyConnected = !! padconnectionstatus.isFullyConnected();
|
||||
if (newFullyConnected != oldFullyConnected) {
|
||||
if (newFullyConnected != oldFullyConnected)
|
||||
{
|
||||
pad.handleIsFullyConnected(newFullyConnected, wasConnecting);
|
||||
}
|
||||
},
|
||||
handleIsFullyConnected: function(isConnected, isInitialConnect) {
|
||||
handleIsFullyConnected: function(isConnected, isInitialConnect)
|
||||
{
|
||||
// load all images referenced from CSS, one at a time,
|
||||
// starting one second after connection is first established.
|
||||
if (isConnected && ! pad.preloadedImages) {
|
||||
window.setTimeout(function() {
|
||||
if (! pad.preloadedImages) {
|
||||
if (isConnected && !pad.preloadedImages)
|
||||
{
|
||||
window.setTimeout(function()
|
||||
{
|
||||
if (!pad.preloadedImages)
|
||||
{
|
||||
pad.preloadImages();
|
||||
pad.preloadedImages = true;
|
||||
}
|
||||
|
@ -426,158 +532,194 @@ var pad = {
|
|||
|
||||
padsavedrevs.handleIsFullyConnected(isConnected);
|
||||
|
||||
pad.determineSidebarVisibility(isConnected && ! isInitialConnect);
|
||||
pad.determineSidebarVisibility(isConnected && !isInitialConnect);
|
||||
},
|
||||
determineSidebarVisibility: function(asNowConnectedFeedback) {
|
||||
if (pad.isFullyConnected()) {
|
||||
var setSidebarVisibility =
|
||||
padutils.getCancellableAction(
|
||||
"set-sidebar-visibility",
|
||||
function() {
|
||||
$("body").toggleClass('hidesidebar',
|
||||
!! padcookie.getPref('hideSidebar'));
|
||||
});
|
||||
window.setTimeout(setSidebarVisibility,
|
||||
asNowConnectedFeedback ? 3000 : 0);
|
||||
determineSidebarVisibility: function(asNowConnectedFeedback)
|
||||
{
|
||||
if (pad.isFullyConnected())
|
||||
{
|
||||
var setSidebarVisibility = padutils.getCancellableAction("set-sidebar-visibility", function()
|
||||
{
|
||||
$("body").toggleClass('hidesidebar', !! padcookie.getPref('hideSidebar'));
|
||||
});
|
||||
window.setTimeout(setSidebarVisibility, asNowConnectedFeedback ? 3000 : 0);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
padutils.cancelActions("set-sidebar-visibility");
|
||||
$("body").removeClass('hidesidebar');
|
||||
}
|
||||
},
|
||||
handleCollabAction: function(action) {
|
||||
if (action == "commitPerformed") {
|
||||
handleCollabAction: function(action)
|
||||
{
|
||||
if (action == "commitPerformed")
|
||||
{
|
||||
padeditbar.setSyncStatus("syncing");
|
||||
}
|
||||
else if (action == "newlyIdle") {
|
||||
else if (action == "newlyIdle")
|
||||
{
|
||||
padeditbar.setSyncStatus("done");
|
||||
}
|
||||
},
|
||||
hideServerMessage: function() {
|
||||
hideServerMessage: function()
|
||||
{
|
||||
alertBar.hideMessage();
|
||||
},
|
||||
asyncSendDiagnosticInfo: function() {
|
||||
asyncSendDiagnosticInfo: function()
|
||||
{
|
||||
pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
|
||||
window.setTimeout(function() {
|
||||
$.ajax({
|
||||
window.setTimeout(function()
|
||||
{
|
||||
$.ajax(
|
||||
{
|
||||
type: 'post',
|
||||
url: '/ep/pad/connection-diagnostic-info',
|
||||
data: {padId: pad.getPadId(), diagnosticInfo: JSON.stringify(pad.diagnosticInfo)},
|
||||
success: function() {},
|
||||
error: function() {}
|
||||
data: {
|
||||
padId: pad.getPadId(),
|
||||
diagnosticInfo: JSON.stringify(pad.diagnosticInfo)
|
||||
},
|
||||
success: function()
|
||||
{},
|
||||
error: function()
|
||||
{}
|
||||
});
|
||||
}, 0);
|
||||
},
|
||||
forceReconnect: function() {
|
||||
forceReconnect: function()
|
||||
{
|
||||
$('form#reconnectform input.padId').val(pad.getPadId());
|
||||
pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
|
||||
$('form#reconnectform input.diagnosticInfo').val(JSON.stringify(pad.diagnosticInfo));
|
||||
$('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges()));
|
||||
$('form#reconnectform').submit();
|
||||
},
|
||||
toggleWidthPref: function() {
|
||||
var newValue = ! padcookie.getPref('fullWidth');
|
||||
toggleWidthPref: function()
|
||||
{
|
||||
var newValue = !padcookie.getPref('fullWidth');
|
||||
padcookie.setPref('fullWidth', newValue);
|
||||
$("#widthprefcheck").toggleClass('widthprefchecked', !!newValue).toggleClass(
|
||||
'widthprefunchecked', !newValue);
|
||||
$("#widthprefcheck").toggleClass('widthprefchecked', !! newValue).toggleClass('widthprefunchecked', !newValue);
|
||||
pad.handleWidthChange();
|
||||
},
|
||||
toggleSidebar: function() {
|
||||
var newValue = ! padcookie.getPref('hideSidebar');
|
||||
toggleSidebar: function()
|
||||
{
|
||||
var newValue = !padcookie.getPref('hideSidebar');
|
||||
padcookie.setPref('hideSidebar', newValue);
|
||||
$("#sidebarcheck").toggleClass('sidebarchecked', !newValue).toggleClass(
|
||||
'sidebarunchecked', !!newValue);
|
||||
$("#sidebarcheck").toggleClass('sidebarchecked', !newValue).toggleClass('sidebarunchecked', !! newValue);
|
||||
pad.determineSidebarVisibility();
|
||||
},
|
||||
handleWidthChange: function() {
|
||||
handleWidthChange: function()
|
||||
{
|
||||
var isFullWidth = padcookie.getPref('fullWidth');
|
||||
if (isFullWidth) {
|
||||
$("body").addClass('fullwidth').removeClass('limwidth').removeClass(
|
||||
'squish1width').removeClass('squish2width');
|
||||
if (isFullWidth)
|
||||
{
|
||||
$("body").addClass('fullwidth').removeClass('limwidth').removeClass('squish1width').removeClass('squish2width');
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$("body").addClass('limwidth').removeClass('fullwidth');
|
||||
|
||||
var pageWidth = $(window).width();
|
||||
$("body").toggleClass('squish1width', (pageWidth < 912 && pageWidth > 812)).toggleClass(
|
||||
'squish2width', (pageWidth <= 812));
|
||||
$("body").toggleClass('squish1width', (pageWidth < 912 && pageWidth > 812)).toggleClass('squish2width', (pageWidth <= 812));
|
||||
}
|
||||
},
|
||||
// this is called from code put into a frame from the server:
|
||||
handleImportExportFrameCall: function(callName, varargs) {
|
||||
padimpexp.handleFrameCall.call(padimpexp, callName,
|
||||
Array.prototype.slice.call(arguments, 1));
|
||||
handleImportExportFrameCall: function(callName, varargs)
|
||||
{
|
||||
padimpexp.handleFrameCall.call(padimpexp, callName, Array.prototype.slice.call(arguments, 1));
|
||||
},
|
||||
callWhenNotCommitting: function(f) {
|
||||
callWhenNotCommitting: function(f)
|
||||
{
|
||||
pad.collabClient.callWhenNotCommitting(f);
|
||||
},
|
||||
getCollabRevisionNumber: function() {
|
||||
getCollabRevisionNumber: function()
|
||||
{
|
||||
return pad.collabClient.getCurrentRevisionNumber();
|
||||
},
|
||||
isFullyConnected: function() {
|
||||
isFullyConnected: function()
|
||||
{
|
||||
return padconnectionstatus.isFullyConnected();
|
||||
},
|
||||
addHistoricalAuthors: function(data) {
|
||||
if (! pad.collabClient) {
|
||||
window.setTimeout(function() { pad.addHistoricalAuthors(data); },
|
||||
1000);
|
||||
addHistoricalAuthors: function(data)
|
||||
{
|
||||
if (!pad.collabClient)
|
||||
{
|
||||
window.setTimeout(function()
|
||||
{
|
||||
pad.addHistoricalAuthors(data);
|
||||
}, 1000);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
pad.collabClient.addHistoricalAuthors(data);
|
||||
}
|
||||
},
|
||||
preloadImages: function() {
|
||||
var images = [
|
||||
'../static/img/colorpicker.gif'
|
||||
];
|
||||
function loadNextImage() {
|
||||
if (images.length == 0) {
|
||||
preloadImages: function()
|
||||
{
|
||||
var images = ['../static/img/colorpicker.gif'];
|
||||
|
||||
function loadNextImage()
|
||||
{
|
||||
if (images.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var img = new Image();
|
||||
img.src = images.shift();
|
||||
if (img.complete) {
|
||||
if (img.complete)
|
||||
{
|
||||
scheduleLoadNextImage();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$(img).bind('error load onreadystatechange', scheduleLoadNextImage);
|
||||
}
|
||||
}
|
||||
function scheduleLoadNextImage() {
|
||||
|
||||
function scheduleLoadNextImage()
|
||||
{
|
||||
window.setTimeout(loadNextImage, 0);
|
||||
}
|
||||
scheduleLoadNextImage();
|
||||
}
|
||||
};
|
||||
|
||||
var alertBar = (function() {
|
||||
var alertBar = (function()
|
||||
{
|
||||
|
||||
var animator = padutils.makeShowHideAnimator(arriveAtAnimationState, false, 25, 400);
|
||||
|
||||
function arriveAtAnimationState(state) {
|
||||
if (state == -1) {
|
||||
function arriveAtAnimationState(state)
|
||||
{
|
||||
if (state == -1)
|
||||
{
|
||||
$("#alertbar").css('opacity', 0).css('display', 'block');
|
||||
}
|
||||
else if (state == 0) {
|
||||
else if (state == 0)
|
||||
{
|
||||
$("#alertbar").css('opacity', 1);
|
||||
}
|
||||
else if (state == 1) {
|
||||
else if (state == 1)
|
||||
{
|
||||
$("#alertbar").css('opacity', 0).css('display', 'none');
|
||||
}
|
||||
else if (state < 0) {
|
||||
$("#alertbar").css('opacity', state+1);
|
||||
else if (state < 0)
|
||||
{
|
||||
$("#alertbar").css('opacity', state + 1);
|
||||
}
|
||||
else if (state > 0) {
|
||||
else if (state > 0)
|
||||
{
|
||||
$("#alertbar").css('opacity', 1 - state);
|
||||
}
|
||||
}
|
||||
|
||||
var self = {
|
||||
displayMessage: function(setupFunc) {
|
||||
displayMessage: function(setupFunc)
|
||||
{
|
||||
animator.show();
|
||||
setupFunc($("#alertbar"));
|
||||
},
|
||||
hideMessage: function() {
|
||||
hideMessage: function()
|
||||
{
|
||||
animator.hide();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -14,44 +14,64 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var padconnectionstatus = (function() {
|
||||
var padconnectionstatus = (function()
|
||||
{
|
||||
|
||||
var status = {what: 'connecting'};
|
||||
var status = {
|
||||
what: 'connecting'
|
||||
};
|
||||
|
||||
var self = {
|
||||
init: function() {
|
||||
$('button#forcereconnect').click(function() {
|
||||
init: function()
|
||||
{
|
||||
$('button#forcereconnect').click(function()
|
||||
{
|
||||
window.location.reload();
|
||||
});
|
||||
},
|
||||
connected: function() {
|
||||
status = {what: 'connected'};
|
||||
connected: function()
|
||||
{
|
||||
status = {
|
||||
what: 'connected'
|
||||
};
|
||||
padmodals.hideModal(500);
|
||||
},
|
||||
reconnecting: function() {
|
||||
status = {what: 'reconnecting'};
|
||||
reconnecting: function()
|
||||
{
|
||||
status = {
|
||||
what: 'reconnecting'
|
||||
};
|
||||
$("#connectionbox").get(0).className = 'modaldialog cboxreconnecting';
|
||||
padmodals.showModal("#connectionbox", 500);
|
||||
},
|
||||
disconnected: function(msg) {
|
||||
status = {what: 'disconnected', why: msg};
|
||||
disconnected: function(msg)
|
||||
{
|
||||
status = {
|
||||
what: 'disconnected',
|
||||
why: msg
|
||||
};
|
||||
var k = String(msg).toLowerCase(); // known reason why
|
||||
if (!(k == 'userdup' || k == 'looping' || k == 'slowcommit' ||
|
||||
k == 'initsocketfail' || k == 'unauth')) {
|
||||
if (!(k == 'userdup' || k == 'looping' || k == 'slowcommit' || k == 'initsocketfail' || k == 'unauth'))
|
||||
{
|
||||
k = 'unknown';
|
||||
}
|
||||
var cls = 'modaldialog cboxdisconnected cboxdisconnected_'+k;
|
||||
var cls = 'modaldialog cboxdisconnected cboxdisconnected_' + k;
|
||||
$("#connectionbox").get(0).className = cls;
|
||||
padmodals.showModal("#connectionbox", 500);
|
||||
|
||||
$('button#forcereconnect').click(function() {
|
||||
|
||||
$('button#forcereconnect').click(function()
|
||||
{
|
||||
window.location.reload();
|
||||
});
|
||||
},
|
||||
isFullyConnected: function() {
|
||||
isFullyConnected: function()
|
||||
{
|
||||
return status.what == 'connected';
|
||||
},
|
||||
getStatus: function() { return status; }
|
||||
getStatus: function()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
};
|
||||
return self;
|
||||
}());
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,49 +15,61 @@
|
|||
*/
|
||||
|
||||
|
||||
var padcookie = (function(){
|
||||
function getRawCookie() {
|
||||
var padcookie = (function()
|
||||
{
|
||||
function getRawCookie()
|
||||
{
|
||||
// returns null if can't get cookie text
|
||||
if (! document.cookie) {
|
||||
if (!document.cookie)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// look for (start of string OR semicolon) followed by whitespace followed by prefs=(something);
|
||||
var regexResult = document.cookie.match(/(?:^|;)\s*prefs=([^;]*)(?:;|$)/);
|
||||
if ((! regexResult) || (! regexResult[1])) {
|
||||
if ((!regexResult) || (!regexResult[1]))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return regexResult[1];
|
||||
}
|
||||
function setRawCookie(safeText) {
|
||||
|
||||
function setRawCookie(safeText)
|
||||
{
|
||||
var expiresDate = new Date();
|
||||
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));
|
||||
return cookieData;
|
||||
}
|
||||
catch (e) {
|
||||
catch (e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function stringifyCookie(data) {
|
||||
|
||||
function stringifyCookie(data)
|
||||
{
|
||||
return escape(JSON.stringify(data));
|
||||
}
|
||||
function saveCookie() {
|
||||
if (! inited) {
|
||||
|
||||
function saveCookie()
|
||||
{
|
||||
if (!inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
setRawCookie(stringifyCookie(cookieData));
|
||||
|
||||
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"+
|
||||
" 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.");
|
||||
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" + " 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;
|
||||
}
|
||||
}
|
||||
|
@ -68,11 +80,14 @@ var padcookie = (function(){
|
|||
var inited = false;
|
||||
|
||||
var self = {
|
||||
init: function(prefsToSet) {
|
||||
init: function(prefsToSet)
|
||||
{
|
||||
var rawCookie = getRawCookie();
|
||||
if (rawCookie) {
|
||||
if (rawCookie)
|
||||
{
|
||||
var cookieObj = parseCookie(rawCookie);
|
||||
if (cookieObj) {
|
||||
if (cookieObj)
|
||||
{
|
||||
wasNoCookie = false; // there was a cookie
|
||||
delete cookieObj.userId;
|
||||
delete cookieObj.name;
|
||||
|
@ -81,21 +96,27 @@ var padcookie = (function(){
|
|||
}
|
||||
}
|
||||
|
||||
for(var k in prefsToSet) {
|
||||
for (var k in prefsToSet)
|
||||
{
|
||||
cookieData[k] = prefsToSet[k];
|
||||
}
|
||||
|
||||
inited = true;
|
||||
saveCookie();
|
||||
},
|
||||
wasNoCookie: function() { return wasNoCookie; },
|
||||
getPref: function(prefName) {
|
||||
wasNoCookie: function()
|
||||
{
|
||||
return wasNoCookie;
|
||||
},
|
||||
getPref: function(prefName)
|
||||
{
|
||||
return cookieData[prefName];
|
||||
},
|
||||
setPref: function(prefName, value) {
|
||||
setPref: function(prefName, value)
|
||||
{
|
||||
cookieData[prefName] = value;
|
||||
saveCookie();
|
||||
}
|
||||
};
|
||||
return self;
|
||||
}());
|
||||
}());
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,50 +15,66 @@
|
|||
*/
|
||||
|
||||
|
||||
var paddocbar = (function() {
|
||||
var paddocbar = (function()
|
||||
{
|
||||
var isTitleEditable = false;
|
||||
var isEditingTitle = false;
|
||||
var isEditingPassword = false;
|
||||
var enabled = false;
|
||||
|
||||
function getPanelOpenCloseAnimator(panelName, panelHeight) {
|
||||
var wrapper = $("#"+panelName+"-wrapper");
|
||||
var openingClass = "docbar"+panelName+"-opening";
|
||||
var openClass = "docbar"+panelName+"-open";
|
||||
var closingClass = "docbar"+panelName+"-closing";
|
||||
function setPanelState(action) {
|
||||
function getPanelOpenCloseAnimator(panelName, panelHeight)
|
||||
{
|
||||
var wrapper = $("#" + panelName + "-wrapper");
|
||||
var openingClass = "docbar" + panelName + "-opening";
|
||||
var openClass = "docbar" + panelName + "-open";
|
||||
var closingClass = "docbar" + panelName + "-closing";
|
||||
|
||||
function setPanelState(action)
|
||||
{
|
||||
$("#docbar").removeClass(openingClass).removeClass(openClass).
|
||||
removeClass(closingClass);
|
||||
if (action != "closed") {
|
||||
$("#docbar").addClass("docbar"+panelName+"-"+action);
|
||||
removeClass(closingClass);
|
||||
if (action != "closed")
|
||||
{
|
||||
$("#docbar").addClass("docbar" + panelName + "-" + action);
|
||||
}
|
||||
}
|
||||
|
||||
function openCloseAnimate(state) {
|
||||
function pow(x) { x = 1-x; x *= x*x; return 1-x; }
|
||||
function openCloseAnimate(state)
|
||||
{
|
||||
function pow(x)
|
||||
{
|
||||
x = 1 - x;
|
||||
x *= x * x;
|
||||
return 1 - x;
|
||||
}
|
||||
|
||||
if (state == -1) {
|
||||
if (state == -1)
|
||||
{
|
||||
// startng to open
|
||||
setPanelState("opening");
|
||||
wrapper.css('height', '0');
|
||||
}
|
||||
else if (state < 0) {
|
||||
else if (state < 0)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
else if (state == 0) {
|
||||
else if (state == 0)
|
||||
{
|
||||
// open
|
||||
setPanelState("open");
|
||||
wrapper.css('height', panelHeight-1);
|
||||
wrapper.css('height', panelHeight - 1);
|
||||
}
|
||||
else if (state < 1) {
|
||||
else if (state < 1)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
else if (state == 1) {
|
||||
else if (state == 1)
|
||||
{
|
||||
// closed
|
||||
setPanelState("closed");
|
||||
wrapper.css('height', '0');
|
||||
|
@ -70,16 +86,21 @@ var paddocbar = (function() {
|
|||
|
||||
|
||||
var currentPanel = null;
|
||||
function setCurrentPanel(newCurrentPanel) {
|
||||
if (currentPanel != newCurrentPanel) {
|
||||
|
||||
function setCurrentPanel(newCurrentPanel)
|
||||
{
|
||||
if (currentPanel != newCurrentPanel)
|
||||
{
|
||||
currentPanel = newCurrentPanel;
|
||||
padutils.cancelActions("hide-docbar-panel");
|
||||
}
|
||||
}
|
||||
var panels;
|
||||
|
||||
function changePassword(newPass) {
|
||||
if ((newPass || null) != (self.password || null)) {
|
||||
function changePassword(newPass)
|
||||
{
|
||||
if ((newPass || null) != (self.password || null))
|
||||
{
|
||||
self.password = (newPass || null);
|
||||
pad.notifyChangePassword(newPass);
|
||||
}
|
||||
|
@ -89,37 +110,72 @@ var paddocbar = (function() {
|
|||
var self = {
|
||||
title: null,
|
||||
password: null,
|
||||
init: function(opts) {
|
||||
init: function(opts)
|
||||
{
|
||||
panels = {
|
||||
impexp: { animator: getPanelOpenCloseAnimator("impexp", 160) },
|
||||
savedrevs: { animator: getPanelOpenCloseAnimator("savedrevs", 79) },
|
||||
options: { animator: getPanelOpenCloseAnimator(
|
||||
"options", 114) },
|
||||
security: { animator: getPanelOpenCloseAnimator("security", 130) }
|
||||
impexp: {
|
||||
animator: getPanelOpenCloseAnimator("impexp", 160)
|
||||
},
|
||||
savedrevs: {
|
||||
animator: getPanelOpenCloseAnimator("savedrevs", 79)
|
||||
},
|
||||
options: {
|
||||
animator: getPanelOpenCloseAnimator("options", 114)
|
||||
},
|
||||
security: {
|
||||
animator: getPanelOpenCloseAnimator("security", 130)
|
||||
}
|
||||
};
|
||||
|
||||
isTitleEditable = opts.isTitleEditable;
|
||||
self.title = opts.initialTitle;
|
||||
self.password = opts.initialPassword;
|
||||
|
||||
$("#docbarimpexp").click(function() {self.togglePanel("impexp");});
|
||||
$("#docbarsavedrevs").click(function() {self.togglePanel("savedrevs");});
|
||||
$("#docbaroptions").click(function() {self.togglePanel("options");});
|
||||
$("#docbarsecurity").click(function() {self.togglePanel("security");});
|
||||
$("#docbarimpexp").click(function()
|
||||
{
|
||||
self.togglePanel("impexp");
|
||||
});
|
||||
$("#docbarsavedrevs").click(function()
|
||||
{
|
||||
self.togglePanel("savedrevs");
|
||||
});
|
||||
$("#docbaroptions").click(function()
|
||||
{
|
||||
self.togglePanel("options");
|
||||
});
|
||||
$("#docbarsecurity").click(function()
|
||||
{
|
||||
self.togglePanel("security");
|
||||
});
|
||||
|
||||
$("#docbarrenamelink").click(self.editTitle);
|
||||
$("#padtitlesave").click(function() { self.closeTitleEdit(true); });
|
||||
$("#padtitlecancel").click(function() { self.closeTitleEdit(false); });
|
||||
padutils.bindEnterAndEscape($("#padtitleedit"),
|
||||
function() {
|
||||
$("#padtitlesave").trigger('click'); },
|
||||
function() {
|
||||
$("#padtitlecancel").trigger('click'); });
|
||||
$("#padtitlesave").click(function()
|
||||
{
|
||||
self.closeTitleEdit(true);
|
||||
});
|
||||
$("#padtitlecancel").click(function()
|
||||
{
|
||||
self.closeTitleEdit(false);
|
||||
});
|
||||
padutils.bindEnterAndEscape($("#padtitleedit"), function()
|
||||
{
|
||||
$("#padtitlesave").trigger('click');
|
||||
}, function()
|
||||
{
|
||||
$("#padtitlecancel").trigger('click');
|
||||
});
|
||||
|
||||
$("#options-close").click(function() {self.setShownPanel(null);});
|
||||
$("#security-close").click(function() {self.setShownPanel(null);});
|
||||
$("#options-close").click(function()
|
||||
{
|
||||
self.setShownPanel(null);
|
||||
});
|
||||
$("#security-close").click(function()
|
||||
{
|
||||
self.setShownPanel(null);
|
||||
});
|
||||
|
||||
if (pad.getIsProPad()) {
|
||||
if (pad.getIsProPad())
|
||||
{
|
||||
self.initPassword();
|
||||
}
|
||||
|
||||
|
@ -127,135 +183,171 @@ var paddocbar = (function() {
|
|||
self.render();
|
||||
|
||||
// public/private
|
||||
$("#security-access input").bind("change click", function(evt) {
|
||||
pad.changePadOption('guestPolicy',
|
||||
$("#security-access input[name='padaccess']:checked").val());
|
||||
$("#security-access input").bind("change click", function(evt)
|
||||
{
|
||||
pad.changePadOption('guestPolicy', $("#security-access input[name='padaccess']:checked").val());
|
||||
});
|
||||
self.setGuestPolicy(opts.guestPolicy);
|
||||
},
|
||||
setGuestPolicy: function(newPolicy) {
|
||||
$("#security-access input[value='"+newPolicy+"']").attr("checked",
|
||||
"checked");
|
||||
setGuestPolicy: function(newPolicy)
|
||||
{
|
||||
$("#security-access input[value='" + newPolicy + "']").attr("checked", "checked");
|
||||
self.render();
|
||||
},
|
||||
initPassword: function() {
|
||||
initPassword: function()
|
||||
{
|
||||
self.renderPassword();
|
||||
$("#password-clearlink").click(function() {
|
||||
$("#password-clearlink").click(function()
|
||||
{
|
||||
changePassword(null);
|
||||
});
|
||||
$("#password-setlink, #password-display").click(function() {
|
||||
$("#password-setlink, #password-display").click(function()
|
||||
{
|
||||
self.enterPassword();
|
||||
});
|
||||
$("#password-cancellink").click(function() {
|
||||
$("#password-cancellink").click(function()
|
||||
{
|
||||
self.exitPassword(false);
|
||||
});
|
||||
$("#password-savelink").click(function() {
|
||||
$("#password-savelink").click(function()
|
||||
{
|
||||
self.exitPassword(true);
|
||||
});
|
||||
padutils.bindEnterAndEscape($("#security-passwordedit"),
|
||||
function() {
|
||||
self.exitPassword(true);
|
||||
},
|
||||
function() {
|
||||
self.exitPassword(false);
|
||||
});
|
||||
padutils.bindEnterAndEscape($("#security-passwordedit"), function()
|
||||
{
|
||||
self.exitPassword(true);
|
||||
}, function()
|
||||
{
|
||||
self.exitPassword(false);
|
||||
});
|
||||
},
|
||||
enterPassword: function() {
|
||||
enterPassword: function()
|
||||
{
|
||||
isEditingPassword = true;
|
||||
$("#security-passwordedit").val(self.password || '');
|
||||
self.renderPassword();
|
||||
$("#security-passwordedit").focus().select();
|
||||
},
|
||||
exitPassword: function(accept) {
|
||||
exitPassword: function(accept)
|
||||
{
|
||||
isEditingPassword = false;
|
||||
if (accept) {
|
||||
if (accept)
|
||||
{
|
||||
changePassword($("#security-passwordedit").val());
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
self.renderPassword();
|
||||
}
|
||||
},
|
||||
renderPassword: function() {
|
||||
if (isEditingPassword) {
|
||||
renderPassword: function()
|
||||
{
|
||||
if (isEditingPassword)
|
||||
{
|
||||
$("#password-nonedit").hide();
|
||||
$("#password-inedit").show();
|
||||
}
|
||||
else {
|
||||
$("#password-nonedit").toggleClass('nopassword', ! self.password);
|
||||
else
|
||||
{
|
||||
$("#password-nonedit").toggleClass('nopassword', !self.password);
|
||||
$("#password-setlink").html(self.password ? "Change..." : "Set...");
|
||||
if (self.password) {
|
||||
if (self.password)
|
||||
{
|
||||
$("#password-display").html(self.password.replace(/./g, '•'));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$("#password-display").html("None");
|
||||
}
|
||||
$("#password-inedit").hide();
|
||||
$("#password-nonedit").show();
|
||||
}
|
||||
},
|
||||
togglePanel: function(panelName) {
|
||||
if (panelName in panels) {
|
||||
if (currentPanel == panelName) {
|
||||
togglePanel: function(panelName)
|
||||
{
|
||||
if (panelName in panels)
|
||||
{
|
||||
if (currentPanel == panelName)
|
||||
{
|
||||
self.setShownPanel(null);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
self.setShownPanel(panelName);
|
||||
}
|
||||
}
|
||||
},
|
||||
setShownPanel: function(panelName) {
|
||||
function animateHidePanel(panelName, next) {
|
||||
setShownPanel: function(panelName)
|
||||
{
|
||||
function animateHidePanel(panelName, next)
|
||||
{
|
||||
var delay = 0;
|
||||
if (panelName == 'options' && isEditingPassword) {
|
||||
if (panelName == 'options' && isEditingPassword)
|
||||
{
|
||||
// give user feedback that the password they've
|
||||
// typed in won't actually take effect
|
||||
self.exitPassword(false);
|
||||
delay = 500;
|
||||
}
|
||||
|
||||
window.setTimeout(function() {
|
||||
window.setTimeout(function()
|
||||
{
|
||||
panels[panelName].animator.hide();
|
||||
if (next) {
|
||||
if (next)
|
||||
{
|
||||
next();
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
if (! panelName) {
|
||||
if (currentPanel) {
|
||||
if (!panelName)
|
||||
{
|
||||
if (currentPanel)
|
||||
{
|
||||
animateHidePanel(currentPanel);
|
||||
setCurrentPanel(null);
|
||||
}
|
||||
}
|
||||
else if (panelName in panels) {
|
||||
if (currentPanel != panelName) {
|
||||
if (currentPanel) {
|
||||
animateHidePanel(currentPanel, function() {
|
||||
else if (panelName in panels)
|
||||
{
|
||||
if (currentPanel != panelName)
|
||||
{
|
||||
if (currentPanel)
|
||||
{
|
||||
animateHidePanel(currentPanel, function()
|
||||
{
|
||||
panels[panelName].animator.show();
|
||||
setCurrentPanel(panelName);
|
||||
});
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
panels[panelName].animator.show();
|
||||
setCurrentPanel(panelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
isPanelShown: function(panelName) {
|
||||
if (! panelName) {
|
||||
return ! currentPanel;
|
||||
isPanelShown: function(panelName)
|
||||
{
|
||||
if (!panelName)
|
||||
{
|
||||
return !currentPanel;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
return (panelName == currentPanel);
|
||||
}
|
||||
},
|
||||
changeTitle: function(newTitle) {
|
||||
changeTitle: function(newTitle)
|
||||
{
|
||||
self.title = newTitle;
|
||||
self.render();
|
||||
},
|
||||
editTitle: function() {
|
||||
if (! enabled) {
|
||||
editTitle: function()
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
$("#padtitleedit").val(self.title);
|
||||
|
@ -263,13 +355,17 @@ var paddocbar = (function() {
|
|||
self.render();
|
||||
$("#padtitleedit").focus().select();
|
||||
},
|
||||
closeTitleEdit: function(accept) {
|
||||
if (! enabled) {
|
||||
closeTitleEdit: function(accept)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (accept) {
|
||||
if (accept)
|
||||
{
|
||||
var newTitle = $("#padtitleedit").val();
|
||||
if (newTitle) {
|
||||
if (newTitle)
|
||||
{
|
||||
newTitle = newTitle.substring(0, 80);
|
||||
self.title = newTitle;
|
||||
|
||||
|
@ -280,67 +376,76 @@ var paddocbar = (function() {
|
|||
isEditingTitle = false;
|
||||
self.render();
|
||||
},
|
||||
changePassword: function(newPass) {
|
||||
if (newPass) {
|
||||
changePassword: function(newPass)
|
||||
{
|
||||
if (newPass)
|
||||
{
|
||||
self.password = newPass;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
self.password = null;
|
||||
}
|
||||
self.renderPassword();
|
||||
},
|
||||
render: function() {
|
||||
if (isEditingTitle) {
|
||||
render: function()
|
||||
{
|
||||
if (isEditingTitle)
|
||||
{
|
||||
$("#docbarpadtitle").hide();
|
||||
$("#docbarrenamelink").hide();
|
||||
$("#padtitleedit").show();
|
||||
$("#padtitlebuttons").show();
|
||||
if (! enabled) {
|
||||
if (!enabled)
|
||||
{
|
||||
$("#padtitleedit").attr('disabled', 'disabled');
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$("#padtitleedit").removeAttr('disabled');
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$("#padtitleedit").hide();
|
||||
$("#padtitlebuttons").hide();
|
||||
|
||||
var titleSpan = $("#docbarpadtitle span");
|
||||
titleSpan.html(padutils.escapeHtml(self.title));
|
||||
$("#docbarpadtitle").attr('title',
|
||||
(pad.isPadPublic() ? "Public Pad: " : "")+
|
||||
self.title);
|
||||
$("#docbarpadtitle").attr('title', (pad.isPadPublic() ? "Public Pad: " : "") + self.title);
|
||||
$("#docbarpadtitle").show();
|
||||
|
||||
if (isTitleEditable) {
|
||||
var titleRight = $("#docbarpadtitle").position().left +
|
||||
$("#docbarpadtitle span").position().left +
|
||||
Math.min($("#docbarpadtitle").width(),
|
||||
$("#docbarpadtitle span").width());
|
||||
if (isTitleEditable)
|
||||
{
|
||||
var titleRight = $("#docbarpadtitle").position().left + $("#docbarpadtitle span").position().left + Math.min($("#docbarpadtitle").width(), $("#docbarpadtitle span").width());
|
||||
$("#docbarrenamelink").css('left', titleRight + 10).show();
|
||||
}
|
||||
|
||||
if (pad.isPadPublic()) {
|
||||
if (pad.isPadPublic())
|
||||
{
|
||||
$("#docbar").addClass("docbar-public");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$("#docbar").removeClass("docbar-public");
|
||||
}
|
||||
}
|
||||
},
|
||||
disable: function() {
|
||||
disable: function()
|
||||
{
|
||||
enabled = false;
|
||||
self.render();
|
||||
},
|
||||
handleResizePage: function() {
|
||||
handleResizePage: function()
|
||||
{
|
||||
padsavedrevs.handleResizePage();
|
||||
},
|
||||
hideLaterIfNoOtherInteraction: function() {
|
||||
return padutils.getCancellableAction('hide-docbar-panel',
|
||||
function() {
|
||||
self.setShownPanel(null);
|
||||
});
|
||||
hideLaterIfNoOtherInteraction: function()
|
||||
{
|
||||
return padutils.getCancellableAction('hide-docbar-panel', function()
|
||||
{
|
||||
self.setShownPanel(null);
|
||||
});
|
||||
}
|
||||
};
|
||||
return self;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,50 +15,61 @@
|
|||
*/
|
||||
|
||||
|
||||
var padeditbar = (function(){
|
||||
var padeditbar = (function()
|
||||
{
|
||||
|
||||
var syncAnimation = (function() {
|
||||
var syncAnimation = (function()
|
||||
{
|
||||
var SYNCING = -100;
|
||||
var DONE = 100;
|
||||
var state = DONE;
|
||||
var fps = 25;
|
||||
var step = 1/fps;
|
||||
var step = 1 / fps;
|
||||
var T_START = -0.5;
|
||||
var T_FADE = 1.0;
|
||||
var T_GONE = 1.5;
|
||||
var animator = padutils.makeAnimationScheduler(function() {
|
||||
if (state == SYNCING || state == DONE) {
|
||||
var animator = padutils.makeAnimationScheduler(function()
|
||||
{
|
||||
if (state == SYNCING || state == DONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (state >= T_GONE) {
|
||||
else if (state >= T_GONE)
|
||||
{
|
||||
state = DONE;
|
||||
$("#syncstatussyncing").css('display', 'none');
|
||||
$("#syncstatusdone").css('display', 'none');
|
||||
return false;
|
||||
}
|
||||
else if (state < 0) {
|
||||
else if (state < 0)
|
||||
{
|
||||
state += step;
|
||||
if (state >= 0) {
|
||||
if (state >= 0)
|
||||
{
|
||||
$("#syncstatussyncing").css('display', 'none');
|
||||
$("#syncstatusdone").css('display', 'block').css('opacity', 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
state += step;
|
||||
if (state >= T_FADE) {
|
||||
if (state >= T_FADE)
|
||||
{
|
||||
$("#syncstatusdone").css('opacity', (T_GONE - state) / (T_GONE - T_FADE));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, step*1000);
|
||||
}, step * 1000);
|
||||
return {
|
||||
syncing: function() {
|
||||
syncing: function()
|
||||
{
|
||||
state = SYNCING;
|
||||
$("#syncstatussyncing").css('display', 'block');
|
||||
$("#syncstatusdone").css('display', 'none');
|
||||
},
|
||||
done: function() {
|
||||
done: function()
|
||||
{
|
||||
state = T_START;
|
||||
animator.scheduleAnimation();
|
||||
}
|
||||
|
@ -66,23 +77,30 @@ var padeditbar = (function(){
|
|||
}());
|
||||
|
||||
var self = {
|
||||
init: function() {
|
||||
init: function()
|
||||
{
|
||||
$("#editbar .editbarbutton").attr("unselectable", "on"); // for IE
|
||||
$("#editbar").removeClass("disabledtoolbar").addClass("enabledtoolbar");
|
||||
},
|
||||
isEnabled: function() {
|
||||
return ! $("#editbar").hasClass('disabledtoolbar');
|
||||
isEnabled: function()
|
||||
{
|
||||
return !$("#editbar").hasClass('disabledtoolbar');
|
||||
},
|
||||
disable: function() {
|
||||
disable: function()
|
||||
{
|
||||
$("#editbar").addClass('disabledtoolbar').removeClass("enabledtoolbar");
|
||||
},
|
||||
toolbarClick: function(cmd) {
|
||||
if (self.isEnabled()) {
|
||||
if (cmd == 'showusers') {
|
||||
// show users shows the current users on teh pad
|
||||
// get current height
|
||||
var editbarheight = $('#users').css('display');
|
||||
if (editbarheight == "none"){
|
||||
toolbarClick: function(cmd)
|
||||
{
|
||||
if (self.isEnabled())
|
||||
{
|
||||
if (cmd == 'showusers')
|
||||
{
|
||||
// show users shows the current users on teh pad
|
||||
// get current height
|
||||
var editbarheight = $('#users').css('display');
|
||||
if (editbarheight == "none")
|
||||
{
|
||||
// increase the size of the editbar
|
||||
//$('#editbar').animate({height:'72px'});
|
||||
//$('#editorcontainerbox').animate({top:'72px'});
|
||||
|
@ -97,18 +115,23 @@ var padeditbar = (function(){
|
|||
$('#users').slideUp("fast");
|
||||
}
|
||||
}
|
||||
if (cmd == 'embed') {
|
||||
// embed shows the embed link
|
||||
// get current height
|
||||
var editbarheight = $('#embed').css('display');
|
||||
if (editbarheight == "none"){
|
||||
if (cmd == 'embed')
|
||||
{
|
||||
// embed shows the embed link
|
||||
// get current height
|
||||
var editbarheight = $('#embed').css('display');
|
||||
if (editbarheight == "none")
|
||||
{
|
||||
// increase the size of the editbar
|
||||
//$('#editbar').animate({height:'72px'});
|
||||
$('#editorcontainerbox').animate({top:'72px'});
|
||||
$('#editorcontainerbox').animate(
|
||||
{
|
||||
top: '72px'
|
||||
});
|
||||
// get the pad url
|
||||
padurl = document.location;
|
||||
// change the div contents to include the pad url in an input box
|
||||
$('#embed').html('<div id="embedcode">Embed code:<input id="embedinput" type="text" value="<iframe src="'+padurl+'" width=500 height=400>"</iframe></div>');
|
||||
$('#embed').html('<div id="embedcode">Embed code:<input id="embedinput" type="text" value="<iframe src="' + padurl + '" width=500 height=400>"</iframe></div>');
|
||||
$('#users').slideUp("fast");
|
||||
$('#embed').slideDown("fast");
|
||||
}
|
||||
|
@ -116,46 +139,64 @@ var padeditbar = (function(){
|
|||
{
|
||||
// increase the size of the editbar
|
||||
//$('#editbar').animate({height:'36px'});
|
||||
$('#editorcontainerbox').animate({top:'36px'});
|
||||
$('#editorcontainerbox').animate(
|
||||
{
|
||||
top: '36px'
|
||||
});
|
||||
$('#embed').hide();
|
||||
}
|
||||
}
|
||||
if (cmd == 'save') {
|
||||
if (cmd == 'save')
|
||||
{
|
||||
padsavedrevs.saveNow();
|
||||
} else {
|
||||
padeditor.ace.callWithAce(function (ace) {
|
||||
if (cmd == 'bold' || cmd == 'italic' || cmd == 'underline' || cmd == 'strikethrough')
|
||||
ace.ace_toggleAttributeOnSelection(cmd);
|
||||
else if (cmd == 'undo' || cmd == 'redo')
|
||||
ace.ace_doUndoRedo(cmd);
|
||||
else if (cmd == 'insertunorderedlist')
|
||||
ace.ace_doInsertUnorderedList();
|
||||
else if (cmd == 'indent') {
|
||||
if (! ace.ace_doIndentOutdent(false)) {
|
||||
ace.ace_doInsertUnorderedList();
|
||||
}
|
||||
} else if (cmd == 'outdent') {
|
||||
}
|
||||
else
|
||||
{
|
||||
padeditor.ace.callWithAce(function(ace)
|
||||
{
|
||||
if (cmd == 'bold' || cmd == 'italic' || cmd == 'underline' || cmd == 'strikethrough') ace.ace_toggleAttributeOnSelection(cmd);
|
||||
else if (cmd == 'undo' || cmd == 'redo') ace.ace_doUndoRedo(cmd);
|
||||
else if (cmd == 'insertunorderedlist') ace.ace_doInsertUnorderedList();
|
||||
else if (cmd == 'indent')
|
||||
{
|
||||
if (!ace.ace_doIndentOutdent(false))
|
||||
{
|
||||
ace.ace_doInsertUnorderedList();
|
||||
}
|
||||
}
|
||||
else if (cmd == 'outdent')
|
||||
{
|
||||
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 {
|
||||
ace.ace_setAttributeOnSelection('author', '');
|
||||
}
|
||||
}
|
||||
}, cmd, 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
|
||||
{
|
||||
ace.ace_setAttributeOnSelection('author', '');
|
||||
}
|
||||
}
|
||||
}, cmd, true);
|
||||
}
|
||||
}
|
||||
padeditor.ace.focus();
|
||||
},
|
||||
setSyncStatus: function(status) {
|
||||
if (status == "syncing") {
|
||||
setSyncStatus: function(status)
|
||||
{
|
||||
if (status == "syncing")
|
||||
{
|
||||
syncAnimation.syncing();
|
||||
}
|
||||
else if (status == "done") {
|
||||
else if (status == "done")
|
||||
{
|
||||
syncAnimation.done();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,15 +15,20 @@
|
|||
*/
|
||||
|
||||
|
||||
var padeditor = (function(){
|
||||
var padeditor = (function()
|
||||
{
|
||||
var self = {
|
||||
ace: null, // this is accessed directly from other files
|
||||
ace: null,
|
||||
// this is accessed directly from other files
|
||||
viewZoom: 100,
|
||||
init: function(readyFunc, initialViewOptions) {
|
||||
init: function(readyFunc, initialViewOptions)
|
||||
{
|
||||
|
||||
function aceReady() {
|
||||
function aceReady()
|
||||
{
|
||||
$("#editorloadingbox").hide();
|
||||
if (readyFunc) {
|
||||
if (readyFunc)
|
||||
{
|
||||
readyFunc();
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +36,8 @@ var padeditor = (function(){
|
|||
self.ace = new Ace2Editor();
|
||||
self.ace.init("editorcontainer", "", aceReady);
|
||||
self.ace.setProperty("wraps", true);
|
||||
if (pad.getIsDebugEnabled()) {
|
||||
if (pad.getIsDebugEnabled())
|
||||
{
|
||||
self.ace.setProperty("dmesg", pad.dmesg);
|
||||
}
|
||||
self.initViewOptions();
|
||||
|
@ -41,22 +47,25 @@ var padeditor = (function(){
|
|||
self.initViewZoom();
|
||||
$("#viewbarcontents").show();
|
||||
},
|
||||
initViewOptions: function() {
|
||||
padutils.bindCheckboxChange($("#options-linenoscheck"), function() {
|
||||
pad.changeViewOption('showLineNumbers',
|
||||
padutils.getCheckbox($("#options-linenoscheck")));
|
||||
initViewOptions: function()
|
||||
{
|
||||
padutils.bindCheckboxChange($("#options-linenoscheck"), function()
|
||||
{
|
||||
pad.changeViewOption('showLineNumbers', padutils.getCheckbox($("#options-linenoscheck")));
|
||||
});
|
||||
padutils.bindCheckboxChange($("#options-colorscheck"), function() {
|
||||
pad.changeViewOption('showAuthorColors',
|
||||
padutils.getCheckbox("#options-colorscheck"));
|
||||
padutils.bindCheckboxChange($("#options-colorscheck"), function()
|
||||
{
|
||||
pad.changeViewOption('showAuthorColors', padutils.getCheckbox("#options-colorscheck"));
|
||||
});
|
||||
$("#viewfontmenu").change(function() {
|
||||
pad.changeViewOption('useMonospaceFont',
|
||||
$("#viewfontmenu").val() == 'monospace');
|
||||
$("#viewfontmenu").change(function()
|
||||
{
|
||||
pad.changeViewOption('useMonospaceFont', $("#viewfontmenu").val() == 'monospace');
|
||||
});
|
||||
},
|
||||
setViewOptions: function(newOptions) {
|
||||
function getOption(key, defaultValue) {
|
||||
setViewOptions: function(newOptions)
|
||||
{
|
||||
function getOption(key, defaultValue)
|
||||
{
|
||||
var value = String(newOptions[key]);
|
||||
if (value == "true") return true;
|
||||
if (value == "false") return false;
|
||||
|
@ -73,52 +82,59 @@ var padeditor = (function(){
|
|||
padutils.setCheckbox($("#options-colorscheck"), v);
|
||||
|
||||
v = getOption('useMonospaceFont', false);
|
||||
self.ace.setProperty("textface",
|
||||
(v ? "monospace" : "Arial, sans-serif"));
|
||||
self.ace.setProperty("textface", (v ? "monospace" : "Arial, sans-serif"));
|
||||
$("#viewfontmenu").val(v ? "monospace" : "normal");
|
||||
},
|
||||
initViewZoom: function() {
|
||||
initViewZoom: function()
|
||||
{
|
||||
var viewZoom = Number(padcookie.getPref('viewZoom'));
|
||||
if ((! viewZoom) || isNaN(viewZoom)) {
|
||||
if ((!viewZoom) || isNaN(viewZoom))
|
||||
{
|
||||
viewZoom = 100;
|
||||
}
|
||||
self.setViewZoom(viewZoom);
|
||||
$("#viewzoommenu").change(function(evt) {
|
||||
$("#viewzoommenu").change(function(evt)
|
||||
{
|
||||
// strip initial 'z' from val
|
||||
self.setViewZoom(Number($("#viewzoommenu").val().substring(1)));
|
||||
});
|
||||
},
|
||||
setViewZoom: function(percent) {
|
||||
if (! (percent >= 50 && percent <= 1000)) {
|
||||
setViewZoom: function(percent)
|
||||
{
|
||||
if (!(percent >= 50 && percent <= 1000))
|
||||
{
|
||||
// percent is out of sane range or NaN (which fails comparisons)
|
||||
return;
|
||||
}
|
||||
|
||||
self.viewZoom = percent;
|
||||
$("#viewzoommenu").val('z'+percent);
|
||||
$("#viewzoommenu").val('z' + percent);
|
||||
|
||||
var baseSize = 13;
|
||||
self.ace.setProperty('textsize',
|
||||
Math.round(baseSize * self.viewZoom / 100));
|
||||
self.ace.setProperty('textsize', Math.round(baseSize * self.viewZoom / 100));
|
||||
|
||||
padcookie.setPref('viewZoom', percent);
|
||||
},
|
||||
dispose: function() {
|
||||
if (self.ace) {
|
||||
dispose: function()
|
||||
{
|
||||
if (self.ace)
|
||||
{
|
||||
self.ace.destroy();
|
||||
}
|
||||
},
|
||||
disable: function() {
|
||||
if (self.ace) {
|
||||
disable: function()
|
||||
{
|
||||
if (self.ace)
|
||||
{
|
||||
self.ace.setProperty("grayedOut", true);
|
||||
self.ace.setEditable(false);
|
||||
}
|
||||
},
|
||||
restoreRevisionText: function(dataFromServer) {
|
||||
restoreRevisionText: function(dataFromServer)
|
||||
{
|
||||
pad.addHistoricalAuthors(dataFromServer.historicalAuthorData);
|
||||
self.ace.importAText(dataFromServer.atext, dataFromServer.apool, true);
|
||||
}
|
||||
};
|
||||
return self;
|
||||
}());
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,121 +15,182 @@
|
|||
*/
|
||||
|
||||
|
||||
var padimpexp = (function() {
|
||||
var padimpexp = (function()
|
||||
{
|
||||
|
||||
///// import
|
||||
|
||||
var currentImportTimer = null;
|
||||
var hidePanelCall = null;
|
||||
|
||||
function addImportFrames() {
|
||||
function addImportFrames()
|
||||
{
|
||||
$("#impexp-import .importframe").remove();
|
||||
$('#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');
|
||||
$('#importsubmitinput').removeAttr('disabled');
|
||||
$('#importmessagefail').fadeOut("fast");
|
||||
$('#importarrow').show();
|
||||
$('#importarrow').animate({paddingLeft:"0px"}, 500)
|
||||
.animate({paddingLeft:"10px"}, 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');
|
||||
$('#importarrow').animate(
|
||||
{
|
||||
paddingLeft: "0px"
|
||||
}, 500).animate(
|
||||
{
|
||||
paddingLeft: "10px"
|
||||
}, 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");
|
||||
var ret = window.confirm(
|
||||
"Importing a file will overwrite the current text of the pad."+
|
||||
" Are you sure you want to proceed?");
|
||||
if (ret) {
|
||||
var ret = window.confirm("Importing a file will overwrite the current text of the pad." + " Are you sure you want to proceed?");
|
||||
if (ret)
|
||||
{
|
||||
hidePanelCall = paddocbar.hideLaterIfNoOtherInteraction();
|
||||
currentImportTimer = window.setTimeout(function() {
|
||||
if (! currentImportTimer) {
|
||||
currentImportTimer = window.setTimeout(function()
|
||||
{
|
||||
if (!currentImportTimer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
currentImportTimer = null;
|
||||
importFailed("Request timed out.");
|
||||
}, 25000); // time out after some number of seconds
|
||||
$('#importsubmitinput').attr({disabled: true}).val("Importing...");
|
||||
window.setTimeout(function() {
|
||||
$('#importfileinput').attr({disabled: true}); }, 0);
|
||||
$('#importsubmitinput').attr(
|
||||
{
|
||||
disabled: true
|
||||
}).val("Importing...");
|
||||
window.setTimeout(function()
|
||||
{
|
||||
$('#importfileinput').attr(
|
||||
{
|
||||
disabled: true
|
||||
});
|
||||
}, 0);
|
||||
$('#importarrow').stop(true, true).hide();
|
||||
$('#importstatusball').show();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function importFailed(msg) {
|
||||
|
||||
function importFailed(msg)
|
||||
{
|
||||
importErrorMessage(msg);
|
||||
importDone();
|
||||
addImportFrames();
|
||||
}
|
||||
function importDone() {
|
||||
|
||||
function importDone()
|
||||
{
|
||||
$('#importsubmitinput').removeAttr('disabled').val("Import Now");
|
||||
window.setTimeout(function() {
|
||||
$('#importfileinput').removeAttr('disabled'); }, 0);
|
||||
window.setTimeout(function()
|
||||
{
|
||||
$('#importfileinput').removeAttr('disabled');
|
||||
}, 0);
|
||||
$('#importstatusball').hide();
|
||||
importClearTimeout();
|
||||
}
|
||||
function importClearTimeout() {
|
||||
if (currentImportTimer) {
|
||||
|
||||
function importClearTimeout()
|
||||
{
|
||||
if (currentImportTimer)
|
||||
{
|
||||
window.clearTimeout(currentImportTimer);
|
||||
currentImportTimer = null;
|
||||
}
|
||||
}
|
||||
function importErrorMessage(msg) {
|
||||
function showError(fade) {
|
||||
$('#importmessagefail').html(
|
||||
'<strong style="color: red">Import failed:</strong> '+
|
||||
(msg || 'Please try a different file.'))[(fade?"fadeIn":"show")]();
|
||||
|
||||
function importErrorMessage(msg)
|
||||
{
|
||||
function showError(fade)
|
||||
{
|
||||
$('#importmessagefail').html('<strong style="color: red">Import failed:</strong> ' + (msg || 'Please try a different file.'))[(fade ? "fadeIn" : "show")]();
|
||||
}
|
||||
|
||||
if ($('#importexport .importmessage').is(':visible')) {
|
||||
$('#importmessagesuccess').fadeOut("fast");
|
||||
$('#importmessagefail').fadeOut("fast", function() {
|
||||
showError(true); });
|
||||
} else {
|
||||
if ($('#importexport .importmessage').is(':visible'))
|
||||
{
|
||||
$('#importmessagesuccess').fadeOut("fast");
|
||||
$('#importmessagefail').fadeOut("fast", function()
|
||||
{
|
||||
showError(true);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
showError();
|
||||
}
|
||||
}
|
||||
function importSuccessful(token) {
|
||||
$.ajax({
|
||||
|
||||
function importSuccessful(token)
|
||||
{
|
||||
$.ajax(
|
||||
{
|
||||
type: 'post',
|
||||
url: '/ep/pad/impexp/import2',
|
||||
data: {token: token, padId: pad.getPadId()},
|
||||
data: {
|
||||
token: token,
|
||||
padId: pad.getPadId()
|
||||
},
|
||||
success: importApplicationSuccessful,
|
||||
error: importApplicationFailed,
|
||||
timeout: 25000
|
||||
});
|
||||
addImportFrames();
|
||||
}
|
||||
function importApplicationFailed(xhr, textStatus, errorThrown) {
|
||||
|
||||
function importApplicationFailed(xhr, textStatus, errorThrown)
|
||||
{
|
||||
importErrorMessage("Error during conversion.");
|
||||
importDone();
|
||||
}
|
||||
function importApplicationSuccessful(data, textStatus) {
|
||||
if (data.substr(0, 2) == "ok") {
|
||||
if ($('#importexport .importmessage').is(':visible')) {
|
||||
|
||||
function importApplicationSuccessful(data, textStatus)
|
||||
{
|
||||
if (data.substr(0, 2) == "ok")
|
||||
{
|
||||
if ($('#importexport .importmessage').is(':visible'))
|
||||
{
|
||||
$('#importexport .importmessage').hide();
|
||||
}
|
||||
$('#importmessagesuccess').html(
|
||||
'<strong style="color: green">Import successful!</strong>').show();
|
||||
$('#importmessagesuccess').html('<strong style="color: green">Import successful!</strong>').show();
|
||||
$('#importformfilediv').hide();
|
||||
window.setTimeout(function() {
|
||||
$('#importmessagesuccess').fadeOut("slow", function() {
|
||||
window.setTimeout(function()
|
||||
{
|
||||
$('#importmessagesuccess').fadeOut("slow", function()
|
||||
{
|
||||
$('#importformfilediv').show();
|
||||
});
|
||||
if (hidePanelCall) {
|
||||
if (hidePanelCall)
|
||||
{
|
||||
hidePanelCall();
|
||||
}
|
||||
}, 3000);
|
||||
} else if (data.substr(0, 4) == "fail") {
|
||||
importErrorMessage(
|
||||
"Couldn't update pad contents. This can happen if your web browser has \"cookies\" disabled.");
|
||||
} else if (data.substr(0, 4) == "msg:") {
|
||||
}
|
||||
else if (data.substr(0, 4) == "fail")
|
||||
{
|
||||
importErrorMessage("Couldn't update pad contents. This can happen if your web browser has \"cookies\" disabled.");
|
||||
}
|
||||
else if (data.substr(0, 4) == "msg:")
|
||||
{
|
||||
importErrorMessage(data.substr(4));
|
||||
}
|
||||
importDone();
|
||||
|
@ -137,47 +198,62 @@ var padimpexp = (function() {
|
|||
|
||||
///// export
|
||||
|
||||
function cantExport() {
|
||||
function cantExport()
|
||||
{
|
||||
var type = $(this);
|
||||
if (type.hasClass("exporthrefpdf")) {
|
||||
if (type.hasClass("exporthrefpdf"))
|
||||
{
|
||||
type = "PDF";
|
||||
} else if (type.hasClass("exporthrefdoc")) {
|
||||
}
|
||||
else if (type.hasClass("exporthrefdoc"))
|
||||
{
|
||||
type = "Microsoft Word";
|
||||
} else if (type.hasClass("exporthrefodt")) {
|
||||
}
|
||||
else if (type.hasClass("exporthrefodt"))
|
||||
{
|
||||
type = "OpenDocument";
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
type = "this file";
|
||||
}
|
||||
alert("Exporting as "+type+" format is disabled. Please contact your"+
|
||||
" system administrator for details.");
|
||||
alert("Exporting as " + type + " format is disabled. Please contact your" + " system administrator for details.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/////
|
||||
|
||||
var self = {
|
||||
init: function() {
|
||||
$("#impexp-close").click(function() {paddocbar.setShownPanel(null);});
|
||||
init: function()
|
||||
{
|
||||
$("#impexp-close").click(function()
|
||||
{
|
||||
paddocbar.setShownPanel(null);
|
||||
});
|
||||
|
||||
addImportFrames();
|
||||
$("#importfileinput").change(fileInputUpdated);
|
||||
$('#importform').submit(fileInputSubmit);
|
||||
$('.disabledexport').click(cantExport);
|
||||
},
|
||||
handleFrameCall: function(callName, argsArray) {
|
||||
if (callName == 'importFailed') {
|
||||
handleFrameCall: function(callName, argsArray)
|
||||
{
|
||||
if (callName == 'importFailed')
|
||||
{
|
||||
importFailed(argsArray[0]);
|
||||
}
|
||||
else if (callName == 'importSuccessful') {
|
||||
else if (callName == 'importSuccessful')
|
||||
{
|
||||
importSuccessful(argsArray[0]);
|
||||
}
|
||||
},
|
||||
disable: function() {
|
||||
disable: function()
|
||||
{
|
||||
$("#impexp-disabled-clickcatcher").show();
|
||||
$("#impexp-import").css('opacity', 0.5);
|
||||
$("#impexp-export").css('opacity', 0.5);
|
||||
},
|
||||
enable: function() {
|
||||
enable: function()
|
||||
{
|
||||
$("#impexp-disabled-clickcatcher").hide();
|
||||
$("#impexp-import").css('opacity', 1);
|
||||
$("#impexp-export").css('opacity', 1);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -14,9 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var padmodals = (function() {
|
||||
var padmodals = (function()
|
||||
{
|
||||
|
||||
/*var clearFeedbackEmail = function() {};
|
||||
/*var clearFeedbackEmail = function() {};
|
||||
function clearFeedback() {
|
||||
clearFeedbackEmail();
|
||||
$("#feedbackbox-message").val('');
|
||||
|
@ -37,140 +38,187 @@ var padmodals = (function() {
|
|||
}*/
|
||||
|
||||
var sendingInvite = false;
|
||||
function setSendingInvite(v) {
|
||||
|
||||
function setSendingInvite(v)
|
||||
{
|
||||
v = !! v;
|
||||
if (sendingInvite != v) {
|
||||
if (sendingInvite != v)
|
||||
{
|
||||
sendingInvite = v;
|
||||
if (v) {
|
||||
if (v)
|
||||
{
|
||||
$(".sharebox-send").css('opacity', 0.75);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$("#sharebox-send").css('opacity', 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var clearShareBoxTo = function() {};
|
||||
function clearShareBox() {
|
||||
var clearShareBoxTo = function()
|
||||
{};
|
||||
|
||||
function clearShareBox()
|
||||
{
|
||||
clearShareBoxTo();
|
||||
}
|
||||
|
||||
var self = {
|
||||
init: function() {
|
||||
init: function()
|
||||
{
|
||||
self.initFeedback();
|
||||
self.initShareBox();
|
||||
},
|
||||
initFeedback: function() {
|
||||
/*var emailField = $("#feedbackbox-email");
|
||||
initFeedback: function()
|
||||
{
|
||||
/*var emailField = $("#feedbackbox-email");
|
||||
clearFeedbackEmail =
|
||||
padutils.makeFieldLabeledWhenEmpty(emailField, '(your email address)').clear;
|
||||
clearFeedback();*/
|
||||
|
||||
$("#feedbackbox-hide").click(function() {
|
||||
$("#feedbackbox-hide").click(function()
|
||||
{
|
||||
self.hideModal();
|
||||
});
|
||||
/*$("#feedbackbox-send").click(function() {
|
||||
/*$("#feedbackbox-send").click(function() {
|
||||
self.sendFeedbackEmail();
|
||||
});*/
|
||||
|
||||
$("#feedbackbutton").click(function() {
|
||||
$("#feedbackbutton").click(function()
|
||||
{
|
||||
self.showFeedback();
|
||||
});
|
||||
},
|
||||
initShareBox: function() {
|
||||
$("#sharebutton").click(function() {
|
||||
initShareBox: function()
|
||||
{
|
||||
$("#sharebutton").click(function()
|
||||
{
|
||||
self.showShareBox();
|
||||
});
|
||||
$("#sharebox-hide").click(function() {
|
||||
$("#sharebox-hide").click(function()
|
||||
{
|
||||
self.hideModal();
|
||||
});
|
||||
$("#sharebox-send").click(function() {
|
||||
$("#sharebox-send").click(function()
|
||||
{
|
||||
self.sendInvite();
|
||||
});
|
||||
|
||||
$("#sharebox-url").click(function() {
|
||||
$("#sharebox-url").click(function()
|
||||
{
|
||||
$("#sharebox-url").focus().select();
|
||||
});
|
||||
|
||||
clearShareBoxTo =
|
||||
padutils.makeFieldLabeledWhenEmpty($("#sharebox-to"),
|
||||
"(email addresses)").clear;
|
||||
clearShareBoxTo = padutils.makeFieldLabeledWhenEmpty($("#sharebox-to"), "(email addresses)").clear;
|
||||
clearShareBox();
|
||||
|
||||
$("#sharebox-subject").val(self.getDefaultShareBoxSubjectForName(pad.getUserName()));
|
||||
$("#sharebox-message").val(self.getDefaultShareBoxMessageForName(pad.getUserName()));
|
||||
|
||||
$("#sharebox-stripe .setsecurity").click(function() {
|
||||
$("#sharebox-stripe .setsecurity").click(function()
|
||||
{
|
||||
self.hideModal();
|
||||
paddocbar.setShownPanel('security');
|
||||
});
|
||||
},
|
||||
getDefaultShareBoxMessageForName: function(name) {
|
||||
return (name || "Somebody")+" has shared an EtherPad document with you."+
|
||||
"\n\n"+"View it here:\n\n"+
|
||||
padutils.escapeHtml($(".sharebox-url").val()+"\n");
|
||||
getDefaultShareBoxMessageForName: function(name)
|
||||
{
|
||||
return (name || "Somebody") + " has shared an EtherPad document with you." + "\n\n" + "View it here:\n\n" + padutils.escapeHtml($(".sharebox-url").val() + "\n");
|
||||
},
|
||||
getDefaultShareBoxSubjectForName: function(name) {
|
||||
return (name || "Somebody")+" invited you to an EtherPad document";
|
||||
getDefaultShareBoxSubjectForName: function(name)
|
||||
{
|
||||
return (name || "Somebody") + " invited you to an EtherPad document";
|
||||
},
|
||||
relayoutWithBottom: function(px) {
|
||||
relayoutWithBottom: function(px)
|
||||
{
|
||||
$("#modaloverlay").height(px);
|
||||
$("#sharebox").css('left',
|
||||
Math.floor(($(window).width() -
|
||||
$("#sharebox").outerWidth())/2));
|
||||
$("#feedbackbox").css('left',
|
||||
Math.floor(($(window).width() -
|
||||
$("#feedbackbox").outerWidth())/2));
|
||||
$("#sharebox").css('left', Math.floor(($(window).width() - $("#sharebox").outerWidth()) / 2));
|
||||
$("#feedbackbox").css('left', Math.floor(($(window).width() - $("#feedbackbox").outerWidth()) / 2));
|
||||
},
|
||||
showFeedback: function() {
|
||||
showFeedback: function()
|
||||
{
|
||||
self.showModal("#feedbackbox");
|
||||
},
|
||||
showShareBox: function() {
|
||||
showShareBox: function()
|
||||
{
|
||||
// when showing the dialog, if it still says "Somebody" invited you
|
||||
// then we fill in the updated username if there is one;
|
||||
// otherwise, we don't touch it, perhaps the user is happy with it
|
||||
var msgbox = $("#sharebox-message");
|
||||
if (msgbox.val() == self.getDefaultShareBoxMessageForName(null)) {
|
||||
if (msgbox.val() == self.getDefaultShareBoxMessageForName(null))
|
||||
{
|
||||
msgbox.val(self.getDefaultShareBoxMessageForName(pad.getUserName()));
|
||||
}
|
||||
var subjBox = $("#sharebox-subject");
|
||||
if (subjBox.val() == self.getDefaultShareBoxSubjectForName(null)) {
|
||||
if (subjBox.val() == self.getDefaultShareBoxSubjectForName(null))
|
||||
{
|
||||
subjBox.val(self.getDefaultShareBoxSubjectForName(pad.getUserName()));
|
||||
}
|
||||
|
||||
if (pad.isPadPublic()) {
|
||||
if (pad.isPadPublic())
|
||||
{
|
||||
$("#sharebox-stripe").get(0).className = 'sharebox-stripe-public';
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$("#sharebox-stripe").get(0).className = 'sharebox-stripe-private';
|
||||
}
|
||||
|
||||
self.showModal("#sharebox", 500);
|
||||
$("#sharebox-url").focus().select();
|
||||
},
|
||||
showModal: function(modalId, duration) {
|
||||
showModal: function(modalId, duration)
|
||||
{
|
||||
$(".modaldialog").hide();
|
||||
$(modalId).show().css({'opacity': 0}).animate({'opacity': 1}, duration);
|
||||
$("#modaloverlay").show().css({'opacity': 0}).animate({'opacity': 1}, duration);
|
||||
$(modalId).show().css(
|
||||
{
|
||||
'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-sharebox');
|
||||
$("#sharebox-response").hide();
|
||||
$(".modaldialog").animate({'opacity': 0}, duration, function () { $("#modaloverlay").hide(); });
|
||||
$("#modaloverlay").animate({'opacity': 0}, duration, function () { $("#modaloverlay").hide(); });
|
||||
$(".modaldialog").animate(
|
||||
{
|
||||
'opacity': 0
|
||||
}, duration, function()
|
||||
{
|
||||
$("#modaloverlay").hide();
|
||||
});
|
||||
$("#modaloverlay").animate(
|
||||
{
|
||||
'opacity': 0
|
||||
}, duration, function()
|
||||
{
|
||||
$("#modaloverlay").hide();
|
||||
});
|
||||
},
|
||||
hideFeedbackLaterIfNoOtherInteraction: function() {
|
||||
return padutils.getCancellableAction('hide-feedbackbox',
|
||||
function() {
|
||||
self.hideModal();
|
||||
});
|
||||
hideFeedbackLaterIfNoOtherInteraction: function()
|
||||
{
|
||||
return padutils.getCancellableAction('hide-feedbackbox', function()
|
||||
{
|
||||
self.hideModal();
|
||||
});
|
||||
},
|
||||
hideShareboxLaterIfNoOtherInteraction: function() {
|
||||
return padutils.getCancellableAction('hide-sharebox',
|
||||
function() {
|
||||
self.hideModal();
|
||||
});
|
||||
hideShareboxLaterIfNoOtherInteraction: function()
|
||||
{
|
||||
return padutils.getCancellableAction('hide-sharebox', function()
|
||||
{
|
||||
self.hideModal();
|
||||
});
|
||||
},
|
||||
/* sendFeedbackEmail: function() {
|
||||
if (sendingFeedback) {
|
||||
|
@ -217,38 +265,44 @@ var padmodals = (function() {
|
|||
$("#feedbackbox-response").show();
|
||||
}
|
||||
},*/
|
||||
sendInvite: function() {
|
||||
if (sendingInvite) {
|
||||
sendInvite: function()
|
||||
{
|
||||
if (sendingInvite)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (! pad.isFullyConnected()) {
|
||||
if (!pad.isFullyConnected())
|
||||
{
|
||||
displayErrorMessage("Error: Connection to the server is down or flaky.");
|
||||
return;
|
||||
}
|
||||
var message = $("#sharebox-message").val();
|
||||
if (! message) {
|
||||
if (!message)
|
||||
{
|
||||
displayErrorMessage("Please enter a message body before sending.");
|
||||
return;
|
||||
}
|
||||
var emails = ($("#sharebox-to").hasClass('editempty') ? '' :
|
||||
$("#sharebox-to").val()) || '';
|
||||
var emails = ($("#sharebox-to").hasClass('editempty') ? '' : $("#sharebox-to").val()) || '';
|
||||
// find runs of characters that aren't obviously non-email punctuation
|
||||
var emailArray = emails.match(/[^\s,:;<>\"\'\/\(\)\[\]{}]+/g) || [];
|
||||
if (emailArray.length == 0) {
|
||||
if (emailArray.length == 0)
|
||||
{
|
||||
displayErrorMessage('Please enter at least one "To:" address.');
|
||||
$("#sharebox-to").focus().select();
|
||||
return;
|
||||
}
|
||||
for(var i=0;i<emailArray.length;i++) {
|
||||
for (var i = 0; i < emailArray.length; i++)
|
||||
{
|
||||
var addr = emailArray[i];
|
||||
if (! addr.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/)) {
|
||||
displayErrorMessage('"'+padutils.escapeHtml(addr) +
|
||||
'" does not appear to be a valid email address.');
|
||||
if (!addr.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/))
|
||||
{
|
||||
displayErrorMessage('"' + padutils.escapeHtml(addr) + '" does not appear to be a valid email address.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
var subject = $("#sharebox-subject").val();
|
||||
if (! subject) {
|
||||
if (!subject)
|
||||
{
|
||||
subject = self.getDefaultShareBoxSubjectForName(pad.getUserName());
|
||||
$("#sharebox-subject").val(subject); // force the default subject
|
||||
}
|
||||
|
@ -258,7 +312,8 @@ var padmodals = (function() {
|
|||
setSendingInvite(true);
|
||||
$("#sharebox-response").html("Sending...").get(0).className = '';
|
||||
$("#sharebox-response").show();
|
||||
$.ajax({
|
||||
$.ajax(
|
||||
{
|
||||
type: 'post',
|
||||
url: '/ep/pad/emailinvite',
|
||||
data: {
|
||||
|
@ -272,22 +327,30 @@ var padmodals = (function() {
|
|||
error: error
|
||||
});
|
||||
var hideCall = self.hideShareboxLaterIfNoOtherInteraction();
|
||||
function success(msg) {
|
||||
|
||||
function success(msg)
|
||||
{
|
||||
setSendingInvite(false);
|
||||
$("#sharebox-response").html("Email invitation sent!").get(0).className = 'goodresponse';
|
||||
$("#sharebox-response").show();
|
||||
window.setTimeout(function() {
|
||||
$("#sharebox-response").fadeOut('slow', function() {
|
||||
window.setTimeout(function()
|
||||
{
|
||||
$("#sharebox-response").fadeOut('slow', function()
|
||||
{
|
||||
hideCall();
|
||||
});
|
||||
}, 1500);
|
||||
}
|
||||
function error(e) {
|
||||
|
||||
function error(e)
|
||||
{
|
||||
setSendingFeedback(false);
|
||||
$("#sharebox-response").html("An error occurred; no email was sent.").get(0).className = 'badresponse';
|
||||
$("#sharebox-response").show();
|
||||
}
|
||||
function displayErrorMessage(msgHtml) {
|
||||
|
||||
function displayErrorMessage(msgHtml)
|
||||
{
|
||||
$("#sharebox-response").html(msgHtml).get(0).className = 'badresponse';
|
||||
$("#sharebox-response").show();
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,46 +15,51 @@
|
|||
*/
|
||||
|
||||
|
||||
var padsavedrevs = (function() {
|
||||
var padsavedrevs = (function()
|
||||
{
|
||||
|
||||
function reversedCopy(L) {
|
||||
function reversedCopy(L)
|
||||
{
|
||||
var L2 = L.slice();
|
||||
L2.reverse();
|
||||
return L2;
|
||||
}
|
||||
|
||||
function makeRevisionBox(revisionInfo, rnum) {
|
||||
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>');
|
||||
function makeRevisionBox(revisionInfo, rnum)
|
||||
{
|
||||
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>');
|
||||
setBoxLabel(box, revisionInfo.label);
|
||||
setBoxTimestamp(box, revisionInfo.timestamp);
|
||||
box.find(".srauthor").html("by "+padutils.escapeHtml(revisionInfo.savedBy));
|
||||
var viewLink = '/ep/pad/view/'+pad.getPadId()+'/'+revisionInfo.id;
|
||||
box.find(".srauthor").html("by " + padutils.escapeHtml(revisionInfo.savedBy));
|
||||
var viewLink = '/ep/pad/view/' + pad.getPadId() + '/' + revisionInfo.id;
|
||||
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(".srname").click(function(evt) {
|
||||
box.find(".srname").click(function(evt)
|
||||
{
|
||||
editRevisionLabel(rnum, box);
|
||||
});
|
||||
return box;
|
||||
}
|
||||
function setBoxLabel(box, label) {
|
||||
|
||||
function setBoxLabel(box, 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(
|
||||
padutils.timediff(new Date(timestamp))));
|
||||
padutils.timediff(new Date(timestamp))));
|
||||
}
|
||||
function getNthBox(n) {
|
||||
|
||||
function getNthBox(n)
|
||||
{
|
||||
return $("#savedrevisions .srouterbox").eq(n);
|
||||
}
|
||||
function editRevisionLabel(rnum, box) {
|
||||
|
||||
function editRevisionLabel(rnum, box)
|
||||
{
|
||||
var input = $('<input type="text" class="srnameedit"/>');
|
||||
box.find(".srnameedit").remove(); // just in case
|
||||
var label = box.find(".srname");
|
||||
|
@ -64,195 +69,256 @@ var padsavedrevs = (function() {
|
|||
input.css('left', label.position().left);
|
||||
label.after(input);
|
||||
label.css('opacity', 0);
|
||||
function endEdit() {
|
||||
|
||||
function endEdit()
|
||||
{
|
||||
input.remove();
|
||||
label.css('opacity', 1);
|
||||
}
|
||||
var rev = currentRevisionList[rnum];
|
||||
var oldLabel = rev.label;
|
||||
input.blur(function() {
|
||||
input.blur(function()
|
||||
{
|
||||
var newLabel = input.val();
|
||||
if (newLabel && newLabel != oldLabel) {
|
||||
if (newLabel && newLabel != oldLabel)
|
||||
{
|
||||
relabelRevision(rnum, newLabel);
|
||||
}
|
||||
endEdit();
|
||||
});
|
||||
input.val(rev.label).focus().select();
|
||||
padutils.bindEnterAndEscape(input, function onEnter() {
|
||||
padutils.bindEnterAndEscape(input, function onEnter()
|
||||
{
|
||||
input.blur();
|
||||
}, function onEscape() {
|
||||
}, function onEscape()
|
||||
{
|
||||
input.val('').blur();
|
||||
});
|
||||
}
|
||||
function relabelRevision(rnum, newLabel) {
|
||||
|
||||
function relabelRevision(rnum, newLabel)
|
||||
{
|
||||
var rev = currentRevisionList[rnum];
|
||||
$.ajax({
|
||||
$.ajax(
|
||||
{
|
||||
type: 'post',
|
||||
url: '/ep/pad/saverevisionlabel',
|
||||
data: {userId: pad.getUserId(),
|
||||
padId: pad.getPadId(),
|
||||
revId: rev.id,
|
||||
newLabel: newLabel},
|
||||
data: {
|
||||
userId: pad.getUserId(),
|
||||
padId: pad.getPadId(),
|
||||
revId: rev.id,
|
||||
newLabel: newLabel
|
||||
},
|
||||
success: success,
|
||||
error: error
|
||||
});
|
||||
function success(text) {
|
||||
|
||||
function success(text)
|
||||
{
|
||||
var newRevisionList = JSON.parse(text);
|
||||
self.newRevisionList(newRevisionList);
|
||||
pad.sendClientMessage({
|
||||
pad.sendClientMessage(
|
||||
{
|
||||
type: 'revisionLabel',
|
||||
revisionList: reversedCopy(currentRevisionList),
|
||||
savedBy: pad.getUserName(),
|
||||
newLabel: newLabel
|
||||
});
|
||||
}
|
||||
function error(e) {
|
||||
|
||||
function error(e)
|
||||
{
|
||||
alert("Oops! There was an error saving that revision label. Please try again later.");
|
||||
}
|
||||
}
|
||||
|
||||
var currentRevisionList = [];
|
||||
function setRevisionList(newRevisionList, noAnimation) {
|
||||
|
||||
function setRevisionList(newRevisionList, noAnimation)
|
||||
{
|
||||
// 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 b = newRevisionList[i];
|
||||
if (b.label != a.label) {
|
||||
if (b.label != a.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);
|
||||
$("#savedrevs-scrollinner").append(newBox);
|
||||
newBox.css('left', j * REVISION_BOX_WIDTH);
|
||||
}
|
||||
var newOnes = (newRevisionList.length > currentRevisionList.length);
|
||||
currentRevisionList = newRevisionList;
|
||||
if (newOnes) {
|
||||
if (newOnes)
|
||||
{
|
||||
setDesiredScroll(getMaxScroll());
|
||||
if (noAnimation) {
|
||||
if (noAnimation)
|
||||
{
|
||||
setScroll(desiredScroll);
|
||||
}
|
||||
|
||||
if (! noAnimation) {
|
||||
var nameOfLast = currentRevisionList[currentRevisionList.length-1].label;
|
||||
if (!noAnimation)
|
||||
{
|
||||
var nameOfLast = currentRevisionList[currentRevisionList.length - 1].label;
|
||||
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 box = getNthBox(i);
|
||||
setBoxTimestamp(box, r.timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
var savedTipAnimator = padutils.makeShowHideAnimator(function(state) {
|
||||
if (state == -1) {
|
||||
var savedTipAnimator = padutils.makeShowHideAnimator(function(state)
|
||||
{
|
||||
if (state == -1)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 0).css('display', 'block');
|
||||
}
|
||||
else if (state == 0) {
|
||||
else if (state == 0)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 1);
|
||||
}
|
||||
else if (state == 1) {
|
||||
else if (state == 1)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 0).css('display', 'none');
|
||||
}
|
||||
else if (state < 0) {
|
||||
else if (state < 0)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 1);
|
||||
}
|
||||
else if (state > 0) {
|
||||
else if (state > 0)
|
||||
{
|
||||
$("#revision-notifier").css('opacity', 1 - state);
|
||||
}
|
||||
}, false, 25, 300);
|
||||
|
||||
function displaySavedTip(text) {
|
||||
function displaySavedTip(text)
|
||||
{
|
||||
$("#revision-notifier .name").html(padutils.escapeHtml(text));
|
||||
savedTipAnimator.show();
|
||||
padutils.cancelActions("hide-revision-notifier");
|
||||
var hideLater = padutils.getCancellableAction("hide-revision-notifier",
|
||||
function() {
|
||||
savedTipAnimator.hide();
|
||||
});
|
||||
var hideLater = padutils.getCancellableAction("hide-revision-notifier", function()
|
||||
{
|
||||
savedTipAnimator.hide();
|
||||
});
|
||||
window.setTimeout(hideLater, 3000);
|
||||
}
|
||||
|
||||
var REVISION_BOX_WIDTH = 120;
|
||||
var curScroll = 0; // distance between left of revisions and right of view
|
||||
var desiredScroll = 0;
|
||||
function getScrollWidth() {
|
||||
|
||||
function getScrollWidth()
|
||||
{
|
||||
return REVISION_BOX_WIDTH * currentRevisionList.length;
|
||||
}
|
||||
function getViewportWidth() {
|
||||
|
||||
function getViewportWidth()
|
||||
{
|
||||
return $("#savedrevs-scrollouter").width();
|
||||
}
|
||||
function getMinScroll() {
|
||||
|
||||
function getMinScroll()
|
||||
{
|
||||
return Math.min(getViewportWidth(), getScrollWidth());
|
||||
}
|
||||
function getMaxScroll() {
|
||||
|
||||
function getMaxScroll()
|
||||
{
|
||||
return getScrollWidth();
|
||||
}
|
||||
function setScroll(newScroll) {
|
||||
|
||||
function setScroll(newScroll)
|
||||
{
|
||||
curScroll = newScroll;
|
||||
$("#savedrevs-scrollinner").css('right', newScroll);
|
||||
updateScrollArrows();
|
||||
}
|
||||
function setDesiredScroll(newDesiredScroll, dontUpdate) {
|
||||
desiredScroll = Math.min(getMaxScroll(), Math.max(getMinScroll(),
|
||||
newDesiredScroll));
|
||||
if (! dontUpdate) {
|
||||
|
||||
function setDesiredScroll(newDesiredScroll, dontUpdate)
|
||||
{
|
||||
desiredScroll = Math.min(getMaxScroll(), Math.max(getMinScroll(), newDesiredScroll));
|
||||
if (!dontUpdate)
|
||||
{
|
||||
updateScroll();
|
||||
}
|
||||
}
|
||||
function updateScroll() {
|
||||
|
||||
function updateScroll()
|
||||
{
|
||||
updateScrollArrows();
|
||||
scrollAnimator.scheduleAnimation();
|
||||
}
|
||||
function updateScrollArrows() {
|
||||
$("#savedrevs-scrollleft").toggleClass("disabledscrollleft",
|
||||
desiredScroll <= getMinScroll());
|
||||
$("#savedrevs-scrollright").toggleClass("disabledscrollright",
|
||||
desiredScroll >= getMaxScroll());
|
||||
|
||||
function updateScrollArrows()
|
||||
{
|
||||
$("#savedrevs-scrollleft").toggleClass("disabledscrollleft", desiredScroll <= getMinScroll());
|
||||
$("#savedrevs-scrollright").toggleClass("disabledscrollright", desiredScroll >= getMaxScroll());
|
||||
}
|
||||
var scrollAnimator = padutils.makeAnimationScheduler(function() {
|
||||
var scrollAnimator = padutils.makeAnimationScheduler(function()
|
||||
{
|
||||
setDesiredScroll(desiredScroll, true); // re-clamp
|
||||
if (Math.abs(desiredScroll - curScroll) < 1) {
|
||||
if (Math.abs(desiredScroll - curScroll) < 1)
|
||||
{
|
||||
setScroll(desiredScroll);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
setScroll(curScroll + (desiredScroll - curScroll)*0.5);
|
||||
else
|
||||
{
|
||||
setScroll(curScroll + (desiredScroll - curScroll) * 0.5);
|
||||
return true;
|
||||
}
|
||||
}, 50, 2);
|
||||
|
||||
var isSaving = false;
|
||||
function setIsSaving(v) {
|
||||
|
||||
function setIsSaving(v)
|
||||
{
|
||||
isSaving = v;
|
||||
rerenderButton();
|
||||
}
|
||||
|
||||
function haveReachedRevLimit() {
|
||||
function haveReachedRevLimit()
|
||||
{
|
||||
var mv = pad.getPrivilege('maxRevisions');
|
||||
return (!(mv < 0 || mv > currentRevisionList.length));
|
||||
}
|
||||
function rerenderButton() {
|
||||
if (isSaving || (! pad.isFullyConnected()) ||
|
||||
haveReachedRevLimit()) {
|
||||
|
||||
function rerenderButton()
|
||||
{
|
||||
if (isSaving || (!pad.isFullyConnected()) || haveReachedRevLimit())
|
||||
{
|
||||
$("#savedrevs-savenow").css('opacity', 0.75);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$("#savedrevs-savenow").css('opacity', 1);
|
||||
}
|
||||
}
|
||||
|
||||
var scrollRepeatTimer = null;
|
||||
var scrollStartTime = 0;
|
||||
function setScrollRepeatTimer(dir) {
|
||||
|
||||
function setScrollRepeatTimer(dir)
|
||||
{
|
||||
clearScrollRepeatTimer();
|
||||
scrollStartTime = +new Date;
|
||||
scrollRepeatTimer = window.setTimeout(function f() {
|
||||
if (! scrollRepeatTimer) {
|
||||
scrollRepeatTimer = window.setTimeout(function f()
|
||||
{
|
||||
if (!scrollRepeatTimer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.scroll(dir);
|
||||
|
@ -262,8 +328,11 @@ var padsavedrevs = (function() {
|
|||
}, 300);
|
||||
$(document).bind('mouseup', clearScrollRepeatTimer);
|
||||
}
|
||||
function clearScrollRepeatTimer() {
|
||||
if (scrollRepeatTimer) {
|
||||
|
||||
function clearScrollRepeatTimer()
|
||||
{
|
||||
if (scrollRepeatTimer)
|
||||
{
|
||||
window.clearTimeout(scrollRepeatTimer);
|
||||
scrollRepeatTimer = null;
|
||||
}
|
||||
|
@ -271,78 +340,103 @@ var padsavedrevs = (function() {
|
|||
}
|
||||
|
||||
var self = {
|
||||
init: function(initialRevisions) {
|
||||
init: function(initialRevisions)
|
||||
{
|
||||
self.newRevisionList(initialRevisions, true);
|
||||
|
||||
$("#savedrevs-savenow").click(function() { self.saveNow(); });
|
||||
$("#savedrevs-scrollleft").mousedown(function() {
|
||||
$("#savedrevs-savenow").click(function()
|
||||
{
|
||||
self.saveNow();
|
||||
});
|
||||
$("#savedrevs-scrollleft").mousedown(function()
|
||||
{
|
||||
self.scroll('left');
|
||||
setScrollRepeatTimer('left');
|
||||
});
|
||||
$("#savedrevs-scrollright").mousedown(function() {
|
||||
$("#savedrevs-scrollright").mousedown(function()
|
||||
{
|
||||
self.scroll('right');
|
||||
setScrollRepeatTimer('right');
|
||||
});
|
||||
$("#savedrevs-close").click(function() {paddocbar.setShownPanel(null);});
|
||||
$("#savedrevs-close").click(function()
|
||||
{
|
||||
paddocbar.setShownPanel(null);
|
||||
});
|
||||
|
||||
// update "saved n minutes ago" times
|
||||
window.setInterval(function() {
|
||||
window.setInterval(function()
|
||||
{
|
||||
refreshRevisionList();
|
||||
}, 60*1000);
|
||||
}, 60 * 1000);
|
||||
},
|
||||
restoreRevision: function(rnum) {
|
||||
restoreRevision: function(rnum)
|
||||
{
|
||||
var rev = currentRevisionList[rnum];
|
||||
var warning = ("Restoring this revision will overwrite the current"
|
||||
+ " text of the pad. "+
|
||||
"Are you sure you want to continue?");
|
||||
var warning = ("Restoring this revision will overwrite the current" + " text of the pad. " + "Are you sure you want to continue?");
|
||||
var hidePanel = paddocbar.hideLaterIfNoOtherInteraction();
|
||||
var box = getNthBox(rnum);
|
||||
if (confirm(warning)) {
|
||||
if (confirm(warning))
|
||||
{
|
||||
box.find(".srtwirly").show();
|
||||
$.ajax({
|
||||
$.ajax(
|
||||
{
|
||||
type: 'get',
|
||||
url: '/ep/pad/getrevisionatext',
|
||||
data: {padId: pad.getPadId(), revId: rev.id},
|
||||
data: {
|
||||
padId: pad.getPadId(),
|
||||
revId: rev.id
|
||||
},
|
||||
success: success,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
function success(resultJson) {
|
||||
|
||||
function success(resultJson)
|
||||
{
|
||||
untwirl();
|
||||
var result = JSON.parse(resultJson);
|
||||
padeditor.restoreRevisionText(result);
|
||||
window.setTimeout(function() {
|
||||
window.setTimeout(function()
|
||||
{
|
||||
hidePanel();
|
||||
}, 0);
|
||||
}
|
||||
function error(e) {
|
||||
|
||||
function error(e)
|
||||
{
|
||||
untwirl();
|
||||
alert("Oops! There was an error retreiving the text (revNum= "+
|
||||
rev.revNum+"; padId="+pad.getPadId());
|
||||
alert("Oops! There was an error retreiving the text (revNum= " + rev.revNum + "; padId=" + pad.getPadId());
|
||||
}
|
||||
function untwirl() {
|
||||
|
||||
function untwirl()
|
||||
{
|
||||
box.find(".srtwirly").hide();
|
||||
}
|
||||
},
|
||||
showReachedLimit: function() {
|
||||
alert("Sorry, you do not have privileges to save more than "+
|
||||
pad.getPrivilege('maxRevisions')+" revisions.");
|
||||
showReachedLimit: function()
|
||||
{
|
||||
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;
|
||||
// we want chronological order
|
||||
var L = reversedCopy(lst);
|
||||
setRevisionList(L, noAnimation);
|
||||
rerenderButton();
|
||||
},
|
||||
saveNow: function() {
|
||||
if (isSaving) {
|
||||
saveNow: function()
|
||||
{
|
||||
if (isSaving)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (! pad.isFullyConnected()) {
|
||||
if (!pad.isFullyConnected())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (haveReachedRevLimit()) {
|
||||
if (haveReachedRevLimit())
|
||||
{
|
||||
self.showReachedLimit();
|
||||
return;
|
||||
}
|
||||
|
@ -350,59 +444,71 @@ var padsavedrevs = (function() {
|
|||
var savedBy = pad.getUserName() || "unnamed";
|
||||
pad.callWhenNotCommitting(submitSave);
|
||||
|
||||
function submitSave() {
|
||||
$.ajax({
|
||||
function submitSave()
|
||||
{
|
||||
$.ajax(
|
||||
{
|
||||
type: 'post',
|
||||
url: '/ep/pad/saverevision',
|
||||
data: {
|
||||
padId: pad.getPadId(),
|
||||
savedBy: savedBy,
|
||||
savedById: pad.getUserId(),
|
||||
revNum: pad.getCollabRevisionNumber()
|
||||
padId: pad.getPadId(),
|
||||
savedBy: savedBy,
|
||||
savedById: pad.getUserId(),
|
||||
revNum: pad.getCollabRevisionNumber()
|
||||
},
|
||||
success: success,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
function success(text) {
|
||||
|
||||
function success(text)
|
||||
{
|
||||
setIsSaving(false);
|
||||
var newRevisionList = JSON.parse(text);
|
||||
self.newRevisionList(newRevisionList);
|
||||
pad.sendClientMessage({
|
||||
pad.sendClientMessage(
|
||||
{
|
||||
type: 'newRevisionList',
|
||||
revisionList: newRevisionList,
|
||||
savedBy: savedBy
|
||||
});
|
||||
}
|
||||
function error(e) {
|
||||
|
||||
function error(e)
|
||||
{
|
||||
setIsSaving(false);
|
||||
alert("Oops! The server failed to save the revision. Please try again later.");
|
||||
}
|
||||
},
|
||||
handleResizePage: function() {
|
||||
handleResizePage: function()
|
||||
{
|
||||
updateScrollArrows();
|
||||
},
|
||||
handleIsFullyConnected: function(isConnected) {
|
||||
handleIsFullyConnected: function(isConnected)
|
||||
{
|
||||
rerenderButton();
|
||||
},
|
||||
scroll: function(dir) {
|
||||
scroll: function(dir)
|
||||
{
|
||||
var minScroll = getMinScroll();
|
||||
var maxScroll = getMaxScroll();
|
||||
if (dir == 'left') {
|
||||
if (desiredScroll > minScroll) {
|
||||
var n = Math.floor((desiredScroll - 1 - minScroll) /
|
||||
REVISION_BOX_WIDTH);
|
||||
setDesiredScroll(Math.max(0, n)*REVISION_BOX_WIDTH + minScroll);
|
||||
if (dir == 'left')
|
||||
{
|
||||
if (desiredScroll > minScroll)
|
||||
{
|
||||
var n = Math.floor((desiredScroll - 1 - minScroll) / REVISION_BOX_WIDTH);
|
||||
setDesiredScroll(Math.max(0, n) * REVISION_BOX_WIDTH + minScroll);
|
||||
}
|
||||
}
|
||||
else if (dir == 'right') {
|
||||
if (desiredScroll < maxScroll) {
|
||||
var n = Math.floor((maxScroll - desiredScroll - 1) /
|
||||
REVISION_BOX_WIDTH);
|
||||
setDesiredScroll(maxScroll - Math.max(0, n)*REVISION_BOX_WIDTH);
|
||||
else if (dir == 'right')
|
||||
{
|
||||
if (desiredScroll < maxScroll)
|
||||
{
|
||||
var n = Math.floor((maxScroll - desiredScroll - 1) / REVISION_BOX_WIDTH);
|
||||
setDesiredScroll(maxScroll - Math.max(0, n) * REVISION_BOX_WIDTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return self;
|
||||
}());
|
||||
}());
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -21,117 +21,150 @@ var colorPickerSetup = false;
|
|||
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
|
||||
// their insertion, removal, and reordering. It manipulates TD height
|
||||
// and TD opacity.
|
||||
|
||||
function nextRowId() {
|
||||
return "usertr"+(nextRowId.counter++);
|
||||
function nextRowId()
|
||||
{
|
||||
return "usertr" + (nextRowId.counter++);
|
||||
}
|
||||
nextRowId.counter = 1;
|
||||
// objects are shared; fields are "domId","data","animationStep"
|
||||
var rowsFadingOut = []; // unordered set
|
||||
var rowsFadingIn = []; // unordered set
|
||||
var rowsPresent = []; // in order
|
||||
|
||||
var ANIMATION_START = -12; // just starting to fade in
|
||||
var ANIMATION_END = 12; // just finishing fading out
|
||||
function getAnimationHeight(step, power) {
|
||||
var a = Math.abs(step/12);
|
||||
if (power == 2) a = a*a;
|
||||
else if (power == 3) a = a*a*a;
|
||||
else if (power == 4) a = a*a*a*a;
|
||||
else if (power >= 5) a = a*a*a*a*a;
|
||||
return Math.round(26*(1-a));
|
||||
|
||||
|
||||
function getAnimationHeight(step, power)
|
||||
{
|
||||
var a = Math.abs(step / 12);
|
||||
if (power == 2) a = a * a;
|
||||
else if (power == 3) a = a * a * a;
|
||||
else if (power == 4) a = a * a * a * a;
|
||||
else if (power >= 5) a = a * a * a * a * a;
|
||||
return Math.round(26 * (1 - a));
|
||||
}
|
||||
var OPACITY_STEPS = 6;
|
||||
|
||||
var ANIMATION_STEP_TIME = 20;
|
||||
var LOWER_FRAMERATE_FACTOR = 2;
|
||||
var scheduleAnimation = padutils.makeAnimationScheduler(animateStep, ANIMATION_STEP_TIME,
|
||||
LOWER_FRAMERATE_FACTOR).scheduleAnimation;
|
||||
var scheduleAnimation = padutils.makeAnimationScheduler(animateStep, ANIMATION_STEP_TIME, LOWER_FRAMERATE_FACTOR).scheduleAnimation;
|
||||
|
||||
var NUMCOLS = 4;
|
||||
|
||||
// we do lots of manipulation of table rows and stuff that JQuery makes ok, despite
|
||||
// IE's poor handling when manipulating the DOM directly.
|
||||
|
||||
function getEmptyRowHtml(height) {
|
||||
return '<td colspan="'+NUMCOLS+'" style="border:0;height:'+height+'px"><!-- --></td>';
|
||||
function getEmptyRowHtml(height)
|
||||
{
|
||||
return '<td colspan="' + NUMCOLS + '" style="border:0;height:' + height + 'px"><!-- --></td>';
|
||||
}
|
||||
function isNameEditable(data) {
|
||||
return (! data.name) && (data.status != 'Disconnected');
|
||||
|
||||
function isNameEditable(data)
|
||||
{
|
||||
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);
|
||||
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
|
||||
for(var i=0; i<tds.length; i++) {
|
||||
for (var i = 0; i < tds.length; i++)
|
||||
{
|
||||
var oldTd = $(tr.find("td").get(i));
|
||||
if (! oldTd.hasClass('usertdname')) {
|
||||
if (!oldTd.hasClass('usertdname'))
|
||||
{
|
||||
oldTd.replaceWith(tds[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
tr.html(tds.join(''));
|
||||
}
|
||||
return tr;
|
||||
}
|
||||
function getUserRowHtml(height, data) {
|
||||
|
||||
function getUserRowHtml(height, data)
|
||||
{
|
||||
var nameHtml;
|
||||
var isGuest = (data.id.charAt(0) != 'p');
|
||||
if (data.name) {
|
||||
if (data.name)
|
||||
{
|
||||
nameHtml = padutils.escapeHtml(data.name);
|
||||
if (isGuest && pad.getIsProPad()) {
|
||||
if (isGuest && pad.getIsProPad())
|
||||
{
|
||||
nameHtml += ' (Guest)';
|
||||
}
|
||||
}
|
||||
else {
|
||||
nameHtml = '<input type="text" class="editempty newinput" value="unnamed" '+
|
||||
(isNameEditable(data) ? '' : 'disabled="disabled" ')+
|
||||
'/>';
|
||||
else
|
||||
{
|
||||
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>',
|
||||
'<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('');
|
||||
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('');
|
||||
}
|
||||
function getRowHtml(id, innerHtml) {
|
||||
return '<tr id="'+id+'">'+innerHtml+'</tr>';
|
||||
|
||||
function getRowHtml(id, innerHtml)
|
||||
{
|
||||
return '<tr id="' + id + '">' + innerHtml + '</tr>';
|
||||
}
|
||||
function rowNode(row) {
|
||||
return $("#"+row.domId);
|
||||
|
||||
function rowNode(row)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
delete row.opacity;
|
||||
}
|
||||
}
|
||||
function handleRowNode(tr, data) {
|
||||
if (data.titleText) {
|
||||
|
||||
function handleRowNode(tr, data)
|
||||
{
|
||||
if (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');
|
||||
}
|
||||
}
|
||||
function handleOtherUserInputs() {
|
||||
|
||||
function handleOtherUserInputs()
|
||||
{
|
||||
// handle 'INPUT' elements for naming other unnamed users
|
||||
$("#otheruserstable input.newinput").each(function() {
|
||||
$("#otheruserstable input.newinput").each(function()
|
||||
{
|
||||
var input = $(this);
|
||||
var tr = input.closest("tr");
|
||||
if (tr.length > 0) {
|
||||
if (tr.length > 0)
|
||||
{
|
||||
var index = tr.parent().children().index(tr);
|
||||
if (index >= 0) {
|
||||
if (index >= 0)
|
||||
{
|
||||
var userId = rowsPresent[index].data.id;
|
||||
rowManagerMakeNameEditor($(this), userId);
|
||||
}
|
||||
|
@ -140,33 +173,45 @@ var paduserlist = (function() {
|
|||
}
|
||||
|
||||
// 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));
|
||||
animationPower = (animationPower === undefined ? 4 : animationPower);
|
||||
|
||||
var domId = nextRowId();
|
||||
var row = {data: data, animationStep: ANIMATION_START, domId: domId,
|
||||
animationPower: animationPower};
|
||||
var row = {
|
||||
data: data,
|
||||
animationStep: ANIMATION_START,
|
||||
domId: domId,
|
||||
animationPower: animationPower
|
||||
};
|
||||
handleRowData(row);
|
||||
rowsPresent.splice(position, 0, row);
|
||||
var tr;
|
||||
if (animationPower == 0) {
|
||||
if (animationPower == 0)
|
||||
{
|
||||
tr = $(getRowHtml(domId, getUserRowHtml(getAnimationHeight(0), data)));
|
||||
row.animationStep = 0;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
rowsFadingIn.push(row);
|
||||
tr = $(getRowHtml(domId, getEmptyRowHtml(getAnimationHeight(ANIMATION_START))));
|
||||
}
|
||||
handleRowNode(tr, data);
|
||||
if (position == 0) {
|
||||
if (position == 0)
|
||||
{
|
||||
$("table#otheruserstable").prepend(tr);
|
||||
}
|
||||
else {
|
||||
rowNode(rowsPresent[position-1]).after(tr);
|
||||
else
|
||||
{
|
||||
rowNode(rowsPresent[position - 1]).after(tr);
|
||||
}
|
||||
|
||||
if (animationPower != 0) {
|
||||
if (animationPower != 0)
|
||||
{
|
||||
scheduleAnimation();
|
||||
}
|
||||
|
||||
|
@ -175,32 +220,38 @@ var paduserlist = (function() {
|
|||
return row;
|
||||
}
|
||||
|
||||
function updateRow(position, data) {
|
||||
function updateRow(position, data)
|
||||
{
|
||||
var row = rowsPresent[position];
|
||||
if (row) {
|
||||
if (row)
|
||||
{
|
||||
row.data = data;
|
||||
handleRowData(row);
|
||||
if (row.animationStep == 0) {
|
||||
if (row.animationStep == 0)
|
||||
{
|
||||
// not currently animating
|
||||
var tr = rowNode(row);
|
||||
replaceUserRowContents(tr, getAnimationHeight(0), row.data).find(
|
||||
"td").css('opacity', (row.opacity === undefined ? 1 : row.opacity));
|
||||
replaceUserRowContents(tr, getAnimationHeight(0), row.data).find("td").css('opacity', (row.opacity === undefined ? 1 : row.opacity));
|
||||
handleRowNode(tr, data);
|
||||
handleOtherUserInputs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeRow(position, animationPower) {
|
||||
function removeRow(position, animationPower)
|
||||
{
|
||||
animationPower = (animationPower === undefined ? 4 : animationPower);
|
||||
var row = rowsPresent[position];
|
||||
if (row) {
|
||||
if (row)
|
||||
{
|
||||
rowsPresent.splice(position, 1); // remove
|
||||
if (animationPower == 0) {
|
||||
if (animationPower == 0)
|
||||
{
|
||||
rowNode(row).remove();
|
||||
}
|
||||
else {
|
||||
row.animationStep = - row.animationStep; // use symmetry
|
||||
else
|
||||
{
|
||||
row.animationStep = -row.animationStep; // use symmetry
|
||||
row.animationPower = animationPower;
|
||||
rowsFadingOut.push(row);
|
||||
scheduleAnimation();
|
||||
|
@ -209,59 +260,72 @@ var paduserlist = (function() {
|
|||
}
|
||||
|
||||
// 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
|
||||
var row = rowsPresent[oldPosition];
|
||||
if (row && oldPosition != newPosition) {
|
||||
if (row && oldPosition != newPosition)
|
||||
{
|
||||
var rowData = row.data;
|
||||
removeRow(oldPosition, animationPower);
|
||||
insertRow(newPosition, rowData, animationPower);
|
||||
}
|
||||
}
|
||||
|
||||
function animateStep() {
|
||||
function animateStep()
|
||||
{
|
||||
// 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 step = ++row.animationStep;
|
||||
var animHeight = getAnimationHeight(step, row.animationPower);
|
||||
var node = rowNode(row);
|
||||
var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
|
||||
if (step <= -OPACITY_STEPS) {
|
||||
if (step <= -OPACITY_STEPS)
|
||||
{
|
||||
node.find("td").height(animHeight);
|
||||
}
|
||||
else if (step == -OPACITY_STEPS+1) {
|
||||
node.html(getUserRowHtml(animHeight, row.data)).find("td").css(
|
||||
'opacity', baseOpacity*1/OPACITY_STEPS);
|
||||
else if (step == -OPACITY_STEPS + 1)
|
||||
{
|
||||
node.html(getUserRowHtml(animHeight, row.data)).find("td").css('opacity', baseOpacity * 1 / OPACITY_STEPS);
|
||||
handleRowNode(node, row.data);
|
||||
}
|
||||
else if (step < 0) {
|
||||
node.find("td").css('opacity', baseOpacity*(OPACITY_STEPS-(-step))/OPACITY_STEPS).height(animHeight);
|
||||
else if (step < 0)
|
||||
{
|
||||
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
|
||||
node.html(getUserRowHtml(animHeight, row.data)).find("td").css(
|
||||
'opacity', baseOpacity*1).height(animHeight);
|
||||
node.html(getUserRowHtml(animHeight, row.data)).find("td").css('opacity', baseOpacity * 1).height(animHeight);
|
||||
handleRowNode(node, row.data);
|
||||
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 step = ++row.animationStep;
|
||||
var node = rowNode(row);
|
||||
var animHeight = getAnimationHeight(step, row.animationPower);
|
||||
var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
|
||||
if (step < OPACITY_STEPS) {
|
||||
node.find("td").css('opacity', baseOpacity*(OPACITY_STEPS - step)/OPACITY_STEPS).height(animHeight);
|
||||
if (step < OPACITY_STEPS)
|
||||
{
|
||||
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));
|
||||
}
|
||||
else if (step <= ANIMATION_END) {
|
||||
else if (step <= ANIMATION_END)
|
||||
{
|
||||
node.find("td").height(animHeight);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
rowsFadingOut.splice(i, 1); // remove from set
|
||||
node.remove();
|
||||
}
|
||||
|
@ -280,35 +344,44 @@ var paduserlist = (function() {
|
|||
};
|
||||
return self;
|
||||
}()); ////////// rowManager
|
||||
|
||||
var otherUsersInfo = [];
|
||||
var otherUsersData = [];
|
||||
|
||||
function rowManagerMakeNameEditor(jnode, userId) {
|
||||
setUpEditable(jnode, function() {
|
||||
function rowManagerMakeNameEditor(jnode, userId)
|
||||
{
|
||||
setUpEditable(jnode, function()
|
||||
{
|
||||
var existingIndex = findExistingIndex(userId);
|
||||
if (existingIndex >= 0) {
|
||||
if (existingIndex >= 0)
|
||||
{
|
||||
return otherUsersInfo[existingIndex].name || '';
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}, function(newName) {
|
||||
if (! newName) {
|
||||
}, function(newName)
|
||||
{
|
||||
if (!newName)
|
||||
{
|
||||
jnode.addClass("editempty");
|
||||
jnode.val("unnamed");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
jnode.attr('disabled', 'disabled');
|
||||
pad.suggestUserName(userId, newName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function findExistingIndex(userId) {
|
||||
function findExistingIndex(userId)
|
||||
{
|
||||
var existingIndex = -1;
|
||||
for(var i=0;i<otherUsersInfo.length;i++) {
|
||||
if (otherUsersInfo[i].userId == userId) {
|
||||
for (var i = 0; i < otherUsersInfo.length; i++)
|
||||
{
|
||||
if (otherUsersInfo[i].userId == userId)
|
||||
{
|
||||
existingIndex = i;
|
||||
break;
|
||||
}
|
||||
|
@ -316,32 +389,41 @@ var paduserlist = (function() {
|
|||
return existingIndex;
|
||||
}
|
||||
|
||||
function setUpEditable(jqueryNode, valueGetter, valueSetter) {
|
||||
jqueryNode.bind('focus', function(evt) {
|
||||
function setUpEditable(jqueryNode, valueGetter, valueSetter)
|
||||
{
|
||||
jqueryNode.bind('focus', function(evt)
|
||||
{
|
||||
var oldValue = valueGetter();
|
||||
if (jqueryNode.val() !== oldValue) {
|
||||
if (jqueryNode.val() !== oldValue)
|
||||
{
|
||||
jqueryNode.val(oldValue);
|
||||
}
|
||||
jqueryNode.addClass("editactive").removeClass("editempty");
|
||||
});
|
||||
jqueryNode.bind('blur', function(evt) {
|
||||
jqueryNode.bind('blur', function(evt)
|
||||
{
|
||||
var newValue = jqueryNode.removeClass("editactive").val();
|
||||
valueSetter(newValue);
|
||||
});
|
||||
padutils.bindEnterAndEscape(jqueryNode, function onEnter() {
|
||||
padutils.bindEnterAndEscape(jqueryNode, function onEnter()
|
||||
{
|
||||
jqueryNode.blur();
|
||||
}, function onEscape() {
|
||||
}, function onEscape()
|
||||
{
|
||||
jqueryNode.val(valueGetter()).blur();
|
||||
});
|
||||
jqueryNode.removeAttr('disabled').addClass('editable');
|
||||
}
|
||||
|
||||
function updateInviteNotice() {
|
||||
if (otherUsersInfo.length == 0) {
|
||||
function updateInviteNotice()
|
||||
{
|
||||
if (otherUsersInfo.length == 0)
|
||||
{
|
||||
$("#otheruserstable").hide();
|
||||
$("#nootherusers").show();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$("#nootherusers").hide();
|
||||
$("#otheruserstable").show();
|
||||
}
|
||||
|
@ -350,68 +432,82 @@ var paduserlist = (function() {
|
|||
var knocksToIgnore = {};
|
||||
var guestPromptFlashState = 0;
|
||||
var guestPromptFlash = padutils.makeAnimationScheduler(
|
||||
function () {
|
||||
var prompts = $("#guestprompts .guestprompt");
|
||||
if (prompts.length == 0) {
|
||||
return false; // no more to do
|
||||
}
|
||||
|
||||
guestPromptFlashState = 1 - guestPromptFlashState;
|
||||
if (guestPromptFlashState) {
|
||||
prompts.css('background', '#ffa');
|
||||
}
|
||||
else {
|
||||
prompts.css('background', '#ffe');
|
||||
}
|
||||
function()
|
||||
{
|
||||
var prompts = $("#guestprompts .guestprompt");
|
||||
if (prompts.length == 0)
|
||||
{
|
||||
return false; // no more to do
|
||||
}
|
||||
|
||||
return true;
|
||||
}, 1000);
|
||||
guestPromptFlashState = 1 - guestPromptFlashState;
|
||||
if (guestPromptFlashState)
|
||||
{
|
||||
prompts.css('background', '#ffa');
|
||||
}
|
||||
else
|
||||
{
|
||||
prompts.css('background', '#ffe');
|
||||
}
|
||||
|
||||
return true;
|
||||
}, 1000);
|
||||
|
||||
var self = {
|
||||
init: function(myInitialUserInfo) {
|
||||
init: function(myInitialUserInfo)
|
||||
{
|
||||
self.setMyUserInfo(myInitialUserInfo);
|
||||
|
||||
$("#otheruserstable tr").remove();
|
||||
|
||||
if (pad.getUserIsGuest()) {
|
||||
if (pad.getUserIsGuest())
|
||||
{
|
||||
$("#myusernameedit").addClass('myusernameedithoverable');
|
||||
setUpEditable($("#myusernameedit"),
|
||||
function() {
|
||||
return myUserInfo.name || '';
|
||||
},
|
||||
function(newValue) {
|
||||
myUserInfo.name = newValue;
|
||||
pad.notifyChangeName(newValue);
|
||||
// wrap with setTimeout to do later because we get
|
||||
// a double "blur" fire in IE...
|
||||
window.setTimeout(function() {
|
||||
self.renderMyUserInfo();
|
||||
}, 0);
|
||||
});
|
||||
setUpEditable($("#myusernameedit"), function()
|
||||
{
|
||||
return myUserInfo.name || '';
|
||||
}, function(newValue)
|
||||
{
|
||||
myUserInfo.name = newValue;
|
||||
pad.notifyChangeName(newValue);
|
||||
// wrap with setTimeout to do later because we get
|
||||
// a double "blur" fire in IE...
|
||||
window.setTimeout(function()
|
||||
{
|
||||
self.renderMyUserInfo();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
// color picker
|
||||
$("#myswatchbox").click(showColorPicker);
|
||||
$("#mycolorpicker .pickerswatchouter").click(function() {
|
||||
$("#mycolorpicker .pickerswatchouter").click(function()
|
||||
{
|
||||
$("#mycolorpicker .pickerswatchouter").removeClass('picked');
|
||||
$(this).addClass('picked');
|
||||
});
|
||||
$("#mycolorpickersave").click(function() {
|
||||
$("#mycolorpickersave").click(function()
|
||||
{
|
||||
closeColorPicker(true);
|
||||
});
|
||||
$("#mycolorpickercancel").click(function() {
|
||||
$("#mycolorpickercancel").click(function()
|
||||
{
|
||||
closeColorPicker(false);
|
||||
});
|
||||
//
|
||||
|
||||
},
|
||||
setMyUserInfo: function(info) {
|
||||
myUserInfo = $.extend({}, info);
|
||||
setMyUserInfo: function(info)
|
||||
{
|
||||
myUserInfo = $.extend(
|
||||
{}, info);
|
||||
|
||||
self.renderMyUserInfo();
|
||||
},
|
||||
userJoinOrUpdate: function(info) {
|
||||
if ((! info.userId) || (info.userId == myUserInfo.userId)) {
|
||||
userJoinOrUpdate: function(info)
|
||||
{
|
||||
if ((!info.userId) || (info.userId == myUserInfo.userId))
|
||||
{
|
||||
// not sure how this would happen
|
||||
return;
|
||||
}
|
||||
|
@ -423,36 +519,41 @@ var paduserlist = (function() {
|
|||
userData.activity = '';
|
||||
userData.id = info.userId;
|
||||
// Firefox ignores \n in title text; Safari does a linebreak
|
||||
userData.titleText = [info.userAgent||'', info.ip||''].join(' \n');
|
||||
userData.titleText = [info.userAgent || '', info.ip || ''].join(' \n');
|
||||
|
||||
var existingIndex = findExistingIndex(info.userId);
|
||||
|
||||
var numUsersBesides = otherUsersInfo.length;
|
||||
if (existingIndex >= 0) {
|
||||
if (existingIndex >= 0)
|
||||
{
|
||||
numUsersBesides--;
|
||||
}
|
||||
var newIndex = padutils.binarySearch(numUsersBesides, function(n) {
|
||||
if (existingIndex >= 0 && n >= existingIndex) {
|
||||
var newIndex = padutils.binarySearch(numUsersBesides, function(n)
|
||||
{
|
||||
if (existingIndex >= 0 && n >= existingIndex)
|
||||
{
|
||||
// pretend existingIndex isn't there
|
||||
n++;
|
||||
}
|
||||
var infoN = otherUsersInfo[n];
|
||||
var nameN = (infoN.name||'').toLowerCase();
|
||||
var nameThis = (info.name||'').toLowerCase();
|
||||
var nameN = (infoN.name || '').toLowerCase();
|
||||
var nameThis = (info.name || '').toLowerCase();
|
||||
var idN = infoN.userId;
|
||||
var idThis = info.userId;
|
||||
return (nameN > nameThis) || (nameN == nameThis &&
|
||||
idN > idThis);
|
||||
return (nameN > nameThis) || (nameN == nameThis && idN > idThis);
|
||||
});
|
||||
|
||||
if (existingIndex >= 0) {
|
||||
if (existingIndex >= 0)
|
||||
{
|
||||
// update
|
||||
if (existingIndex == newIndex) {
|
||||
if (existingIndex == newIndex)
|
||||
{
|
||||
otherUsersInfo[existingIndex] = info;
|
||||
otherUsersData[existingIndex] = userData;
|
||||
rowManager.updateRow(existingIndex, userData);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
otherUsersInfo.splice(existingIndex, 1);
|
||||
otherUsersData.splice(existingIndex, 1);
|
||||
otherUsersInfo.splice(newIndex, 0, info);
|
||||
|
@ -461,47 +562,55 @@ var paduserlist = (function() {
|
|||
rowManager.moveRow(existingIndex, newIndex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
otherUsersInfo.splice(newIndex, 0, info);
|
||||
otherUsersData.splice(newIndex, 0, userData);
|
||||
rowManager.insertRow(newIndex, userData);
|
||||
}
|
||||
|
||||
updateInviteNotice();
|
||||
|
||||
updateInviteNotice();
|
||||
|
||||
self.updateNumberOfOnlineUsers();
|
||||
},
|
||||
updateNumberOfOnlineUsers: function(){
|
||||
updateNumberOfOnlineUsers: function()
|
||||
{
|
||||
var online = 1; // you are always online!
|
||||
for(var i=0;i<otherUsersData.length;i++) {
|
||||
if(otherUsersData[i].status == "")
|
||||
for (var i = 0; i < otherUsersData.length; i++)
|
||||
{
|
||||
if (otherUsersData[i].status == "")
|
||||
{
|
||||
online++;
|
||||
}
|
||||
}
|
||||
$("#online_count").text(online);
|
||||
|
||||
|
||||
return online;
|
||||
},
|
||||
userLeave: function(info) {
|
||||
userLeave: function(info)
|
||||
{
|
||||
var existingIndex = findExistingIndex(info.userId);
|
||||
if (existingIndex >= 0) {
|
||||
if (existingIndex >= 0)
|
||||
{
|
||||
var userData = otherUsersData[existingIndex];
|
||||
userData.status = 'Disconnected';
|
||||
rowManager.updateRow(existingIndex, userData);
|
||||
if (userData.leaveTimer) {
|
||||
if (userData.leaveTimer)
|
||||
{
|
||||
window.clearTimeout(userData.leaveTimer);
|
||||
}
|
||||
// set up a timer that will only fire if no leaves,
|
||||
// joins, or updates happen for this user in the
|
||||
// next N seconds, to remove the user from the list.
|
||||
var thisUserId = info.userId;
|
||||
var thisLeaveTimer = window.setTimeout(function() {
|
||||
var thisLeaveTimer = window.setTimeout(function()
|
||||
{
|
||||
var newExistingIndex = findExistingIndex(thisUserId);
|
||||
if (newExistingIndex >= 0) {
|
||||
if (newExistingIndex >= 0)
|
||||
{
|
||||
var newUserData = otherUsersData[newExistingIndex];
|
||||
if (newUserData.status == 'Disconnected' &&
|
||||
newUserData.leaveTimer == thisLeaveTimer) {
|
||||
if (newUserData.status == 'Disconnected' && newUserData.leaveTimer == thisLeaveTimer)
|
||||
{
|
||||
otherUsersInfo.splice(newExistingIndex, 1);
|
||||
otherUsersData.splice(newExistingIndex, 1);
|
||||
rowManager.removeRow(newExistingIndex);
|
||||
|
@ -511,50 +620,58 @@ var paduserlist = (function() {
|
|||
}, 8000); // how long to wait
|
||||
userData.leaveTimer = thisLeaveTimer;
|
||||
}
|
||||
updateInviteNotice();
|
||||
|
||||
self.updateNumberOfOnlineUsers();
|
||||
updateInviteNotice();
|
||||
|
||||
self.updateNumberOfOnlineUsers();
|
||||
},
|
||||
showGuestPrompt: function(userId, displayName) {
|
||||
if (knocksToIgnore[userId]) {
|
||||
showGuestPrompt: function(userId, displayName)
|
||||
{
|
||||
if (knocksToIgnore[userId])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var encodedUserId = padutils.encodeUserId(userId);
|
||||
|
||||
var actionName = 'hide-guest-prompt-'+encodedUserId;
|
||||
var actionName = 'hide-guest-prompt-' + encodedUserId;
|
||||
padutils.cancelActions(actionName);
|
||||
|
||||
var box = $("#guestprompt-"+encodedUserId);
|
||||
if (box.length == 0) {
|
||||
var box = $("#guestprompt-" + encodedUserId);
|
||||
if (box.length == 0)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
});
|
||||
window.setTimeout(hideLater, 15000); // time-out with no knock
|
||||
|
||||
guestPromptFlash.scheduleAnimation();
|
||||
},
|
||||
removeGuestPrompt: function(userId) {
|
||||
var box = $("#guestprompt-"+padutils.encodeUserId(userId));
|
||||
removeGuestPrompt: function(userId)
|
||||
{
|
||||
var box = $("#guestprompt-" + padutils.encodeUserId(userId));
|
||||
// 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();
|
||||
});
|
||||
|
||||
knocksToIgnore[userId] = true;
|
||||
window.setTimeout(function() {
|
||||
window.setTimeout(function()
|
||||
{
|
||||
delete knocksToIgnore[userId];
|
||||
}, 5000);
|
||||
},
|
||||
answerGuestPrompt: function(encodedUserId, approve) {
|
||||
answerGuestPrompt: function(encodedUserId, approve)
|
||||
{
|
||||
var guestId = padutils.decodeUserId(encodedUserId);
|
||||
|
||||
var msg = {
|
||||
|
@ -567,22 +684,24 @@ var paduserlist = (function() {
|
|||
|
||||
self.removeGuestPrompt(guestId);
|
||||
},
|
||||
renderMyUserInfo: function() {
|
||||
if (myUserInfo.name) {
|
||||
renderMyUserInfo: function()
|
||||
{
|
||||
if (myUserInfo.name)
|
||||
{
|
||||
$("#myusernameedit").removeClass("editempty").val(
|
||||
myUserInfo.name);
|
||||
myUserInfo.name);
|
||||
}
|
||||
else {
|
||||
$("#myusernameedit").addClass("editempty").val(
|
||||
"Enter your name");
|
||||
else
|
||||
{
|
||||
$("#myusernameedit").addClass("editempty").val("Enter your name");
|
||||
}
|
||||
if (colorPickerOpen) {
|
||||
$("#myswatchbox").addClass('myswatchboxunhoverable').removeClass(
|
||||
'myswatchboxhoverable');
|
||||
if (colorPickerOpen)
|
||||
{
|
||||
$("#myswatchbox").addClass('myswatchboxunhoverable').removeClass('myswatchboxhoverable');
|
||||
}
|
||||
else {
|
||||
$("#myswatchbox").addClass('myswatchboxhoverable').removeClass(
|
||||
'myswatchboxunhoverable');
|
||||
else
|
||||
{
|
||||
$("#myswatchbox").addClass('myswatchboxhoverable').removeClass('myswatchboxunhoverable');
|
||||
}
|
||||
$("#myswatch").css('background', pad.getColorPalette()[myUserInfo.colorId]);
|
||||
}
|
||||
|
@ -590,61 +709,73 @@ var paduserlist = (function() {
|
|||
return self;
|
||||
}());
|
||||
|
||||
function getColorPickerSwatchIndex(jnode) {
|
||||
// return Number(jnode.get(0).className.match(/\bn([0-9]+)\b/)[1])-1;
|
||||
function getColorPickerSwatchIndex(jnode)
|
||||
{
|
||||
// return Number(jnode.get(0).className.match(/\bn([0-9]+)\b/)[1])-1;
|
||||
return $("#colorpickerswatches li").index(jnode);
|
||||
}
|
||||
function closeColorPicker(accept) {
|
||||
if (accept) {
|
||||
|
||||
function closeColorPicker(accept)
|
||||
{
|
||||
if (accept)
|
||||
{
|
||||
var newColorId = getColorPickerSwatchIndex($("#colorpickerswatches .picked"));
|
||||
if (newColorId >= 0) { // fails on NaN
|
||||
if (newColorId >= 0)
|
||||
{ // fails on NaN
|
||||
myUserInfo.colorId = newColorId;
|
||||
pad.notifyChangeColor(newColorId);
|
||||
}
|
||||
|
||||
|
||||
paduserlist.renderMyUserInfo();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
pad.notifyChangeColor(previousColorId);
|
||||
paduserlist.renderMyUserInfo();
|
||||
}
|
||||
|
||||
|
||||
colorPickerOpen = false;
|
||||
$("#mycolorpicker").fadeOut("fast");
|
||||
}
|
||||
|
||||
function showColorPicker() {
|
||||
previousColorId = myUserInfo.colorId ;
|
||||
function showColorPicker()
|
||||
{
|
||||
previousColorId = myUserInfo.colorId;
|
||||
|
||||
if (! colorPickerOpen) {
|
||||
if (!colorPickerOpen)
|
||||
{
|
||||
var palette = pad.getColorPalette();
|
||||
|
||||
if(!colorPickerSetup) {
|
||||
if (!colorPickerSetup)
|
||||
{
|
||||
var colorsList = $("#colorpickerswatches")
|
||||
for(var i=0;i<palette.length;i++) {
|
||||
|
||||
for (var i = 0; i < palette.length; i++)
|
||||
{
|
||||
|
||||
var li = $('<li>', {
|
||||
style: 'background: '+palette[i]+';'
|
||||
style: 'background: ' + palette[i] + ';'
|
||||
});
|
||||
|
||||
|
||||
li.appendTo(colorsList);
|
||||
|
||||
li.bind('click', function(event){
|
||||
|
||||
li.bind('click', function(event)
|
||||
{
|
||||
$("#colorpickerswatches li").removeClass('picked');
|
||||
$(event.target).addClass("picked");
|
||||
|
||||
|
||||
var newColorId = getColorPickerSwatchIndex($("#colorpickerswatches .picked"));
|
||||
pad.notifyChangeColor(newColorId);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
colorPickerSetup = true;
|
||||
}
|
||||
|
||||
|
||||
$("#mycolorpicker").fadeIn();
|
||||
colorPickerOpen = true;
|
||||
|
||||
|
||||
$("#colorpickerswatches li").removeClass('picked');
|
||||
$($("#colorpickerswatches li")[myUserInfo.colorId]).addClass("picked"); //seems weird
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,65 +15,81 @@
|
|||
*/
|
||||
|
||||
var padutils = {
|
||||
escapeHtml: function(x) {
|
||||
escapeHtml: function(x)
|
||||
{
|
||||
return String(x).replace(/\</g, '<').replace(/\>/g, '>');
|
||||
},
|
||||
uniqueId: function() {
|
||||
function encodeNum(n, width) {
|
||||
uniqueId: function()
|
||||
{
|
||||
function encodeNum(n, width)
|
||||
{
|
||||
// returns string that is exactly 'width' chars, padding with zeros
|
||||
// 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(),
|
||||
encodeNum(+new Date, 7),
|
||||
encodeNum(Math.floor(Math.random()*1e9), 4)].join('.');
|
||||
return [pad.getClientIp(), encodeNum(+new Date, 7), encodeNum(Math.floor(Math.random() * 1e9), 4)].join('.');
|
||||
},
|
||||
uaDisplay: function(ua) {
|
||||
uaDisplay: function(ua)
|
||||
{
|
||||
var m;
|
||||
|
||||
function clean(a) {
|
||||
function clean(a)
|
||||
{
|
||||
var maxlen = 16;
|
||||
a = a.replace(/[^a-zA-Z0-9\.]/g, '');
|
||||
if (a.length > maxlen) {
|
||||
a = a.substr(0,maxlen);
|
||||
if (a.length > maxlen)
|
||||
{
|
||||
a = a.substr(0, maxlen);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function checkver(name) {
|
||||
function checkver(name)
|
||||
{
|
||||
var m = ua.match(RegExp(name + '\\/([\\d\\.]+)'));
|
||||
if (m && m.length > 1) {
|
||||
return clean(name+m[1]);
|
||||
if (m && m.length > 1)
|
||||
{
|
||||
return clean(name + m[1]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// firefox
|
||||
if (checkver('Firefox')) { return checkver('Firefox'); }
|
||||
if (checkver('Firefox'))
|
||||
{
|
||||
return checkver('Firefox');
|
||||
}
|
||||
|
||||
// misc browsers, including IE
|
||||
m = ua.match(/compatible; ([^;]+);/);
|
||||
if (m && m.length > 1) {
|
||||
if (m && m.length > 1)
|
||||
{
|
||||
return clean(m[1]);
|
||||
}
|
||||
|
||||
// iphone
|
||||
if (ua.match(/\(iPhone;/)) {
|
||||
if (ua.match(/\(iPhone;/))
|
||||
{
|
||||
return 'iPhone';
|
||||
}
|
||||
|
||||
// chrome
|
||||
if (checkver('Chrome')) { return checkver('Chrome'); }
|
||||
if (checkver('Chrome'))
|
||||
{
|
||||
return checkver('Chrome');
|
||||
}
|
||||
|
||||
// safari
|
||||
m = ua.match(/Safari\/[\d\.]+/);
|
||||
if (m) {
|
||||
if (m)
|
||||
{
|
||||
var v = '?';
|
||||
m = ua.match(/Version\/([\d\.]+)/);
|
||||
if (m && m.length > 1) {
|
||||
if (m && m.length > 1)
|
||||
{
|
||||
v = m[1];
|
||||
}
|
||||
return clean('Safari'+v);
|
||||
return clean('Safari' + v);
|
||||
}
|
||||
|
||||
// everything else
|
||||
|
@ -83,41 +99,49 @@ var padutils = {
|
|||
// "func" is a function over 0..(numItems-1) that is monotonically
|
||||
// "increasing" with index (false, then true). Finds the boundary
|
||||
// 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 (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 high = numItems-1; // func(high) is always true
|
||||
while ((high - low) > 1) {
|
||||
var x = Math.floor((low+high)/2); // x != low, x != high
|
||||
var high = numItems - 1; // func(high) is always true
|
||||
while ((high - low) > 1)
|
||||
{
|
||||
var x = Math.floor((low + high) / 2); // x != low, x != high
|
||||
if (func(x)) high = x;
|
||||
else low = x;
|
||||
}
|
||||
return high;
|
||||
},
|
||||
// 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 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 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 dayOfMonth = d.getDate();
|
||||
var year = d.getFullYear();
|
||||
var hourmin = d.getHours()+":"+("0"+d.getMinutes()).slice(-2);
|
||||
return dayOfWeek+' '+month+' '+dayOfMonth+' '+year+' '+hourmin;
|
||||
var hourmin = d.getHours() + ":" + ("0" + d.getMinutes()).slice(-2);
|
||||
return dayOfWeek + ' ' + month + ' ' + dayOfMonth + ' ' + year + ' ' + hourmin;
|
||||
},
|
||||
findURLs: function(text) {
|
||||
findURLs: function(text)
|
||||
{
|
||||
// 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_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_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');
|
||||
|
||||
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
||||
function _findURLs(text) {
|
||||
|
||||
|
||||
function _findURLs(text)
|
||||
{
|
||||
_REGEX_URL.lastIndex = 0;
|
||||
var urls = null;
|
||||
var execResult;
|
||||
while ((execResult = _REGEX_URL.exec(text))) {
|
||||
while ((execResult = _REGEX_URL.exec(text)))
|
||||
{
|
||||
urls = (urls || []);
|
||||
var startIndex = execResult.index;
|
||||
var url = execResult[0];
|
||||
|
@ -129,23 +153,28 @@ var padutils = {
|
|||
|
||||
return _findURLs(text);
|
||||
},
|
||||
escapeHtmlWithClickableLinks: function(text, target) {
|
||||
escapeHtmlWithClickableLinks: function(text, target)
|
||||
{
|
||||
var idx = 0;
|
||||
var pieces = [];
|
||||
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)));
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
if (urls) {
|
||||
for(var j=0;j<urls.length;j++) {
|
||||
if (urls)
|
||||
{
|
||||
for (var j = 0; j < urls.length; j++)
|
||||
{
|
||||
var startIndex = urls[j][0];
|
||||
var href = urls[j][1];
|
||||
advanceTo(startIndex);
|
||||
pieces.push('<a ', (target?'target="'+target+'" ':''),
|
||||
'href="', href.replace(/\"/g, '"'), '">');
|
||||
pieces.push('<a ', (target ? 'target="' + target + '" ' : ''), 'href="', href.replace(/\"/g, '"'), '">');
|
||||
advanceTo(startIndex + href.length);
|
||||
pieces.push('</a>');
|
||||
}
|
||||
|
@ -153,209 +182,280 @@ var padutils = {
|
|||
advanceTo(text.length);
|
||||
return pieces.join('');
|
||||
},
|
||||
bindEnterAndEscape: function(node, onEnter, onEscape) {
|
||||
bindEnterAndEscape: function(node, onEnter, onEscape)
|
||||
{
|
||||
|
||||
// Use keypress instead of keyup in bindEnterAndEscape
|
||||
// Keyup event is fired on enter in IME (Input Method Editor), But
|
||||
// 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).
|
||||
|
||||
if (onEnter) {
|
||||
node.keypress( function(evt) {
|
||||
if (evt.which == 13) {
|
||||
if (onEnter)
|
||||
{
|
||||
node.keypress(function(evt)
|
||||
{
|
||||
if (evt.which == 13)
|
||||
{
|
||||
onEnter(evt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (onEscape) {
|
||||
node.keydown( function(evt) {
|
||||
if (evt.which == 27) {
|
||||
if (onEscape)
|
||||
{
|
||||
node.keydown(function(evt)
|
||||
{
|
||||
if (evt.which == 27)
|
||||
{
|
||||
onEscape(evt);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
timediff: function(d) {
|
||||
function format(n, word) {
|
||||
timediff: function(d)
|
||||
{
|
||||
function format(n, word)
|
||||
{
|
||||
n = Math.round(n);
|
||||
return ('' + n + ' ' + word + (n != 1 ? 's' : '') + ' ago');
|
||||
}
|
||||
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;
|
||||
if (d < 60) { return format(d, 'minute'); }
|
||||
if (d < 60)
|
||||
{
|
||||
return format(d, 'minute');
|
||||
}
|
||||
d /= 60;
|
||||
if (d < 24) { return format(d, 'hour'); }
|
||||
if (d < 24)
|
||||
{
|
||||
return format(d, 'hour');
|
||||
}
|
||||
d /= 24;
|
||||
return format(d, 'day');
|
||||
},
|
||||
makeAnimationScheduler: function(funcToAnimateOneStep, stepTime, stepsAtOnce) {
|
||||
if (stepsAtOnce === undefined) {
|
||||
makeAnimationScheduler: function(funcToAnimateOneStep, stepTime, stepsAtOnce)
|
||||
{
|
||||
if (stepsAtOnce === undefined)
|
||||
{
|
||||
stepsAtOnce = 1;
|
||||
}
|
||||
|
||||
var animationTimer = null;
|
||||
|
||||
function scheduleAnimation() {
|
||||
if (! animationTimer) {
|
||||
animationTimer = window.setTimeout(function() {
|
||||
function scheduleAnimation()
|
||||
{
|
||||
if (!animationTimer)
|
||||
{
|
||||
animationTimer = window.setTimeout(function()
|
||||
{
|
||||
animationTimer = null;
|
||||
var n = stepsAtOnce;
|
||||
var moreToDo = true;
|
||||
while (moreToDo && n > 0) {
|
||||
while (moreToDo && n > 0)
|
||||
{
|
||||
moreToDo = funcToAnimateOneStep();
|
||||
n--;
|
||||
}
|
||||
if (moreToDo) {
|
||||
if (moreToDo)
|
||||
{
|
||||
// more to do
|
||||
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 animationFrameDelay = 1000 / fps;
|
||||
var animationStep = animationFrameDelay / totalMs;
|
||||
|
||||
var scheduleAnimation =
|
||||
padutils.makeAnimationScheduler(animateOneStep, animationFrameDelay).scheduleAnimation;
|
||||
var scheduleAnimation = padutils.makeAnimationScheduler(animateOneStep, animationFrameDelay).scheduleAnimation;
|
||||
|
||||
function doShow() {
|
||||
function doShow()
|
||||
{
|
||||
animationState = -1;
|
||||
funcToArriveAtState(animationState);
|
||||
scheduleAnimation();
|
||||
}
|
||||
|
||||
function doQuickShow() { // start showing without losing any fade-in progress
|
||||
if (animationState < -1) {
|
||||
function doQuickShow()
|
||||
{ // start showing without losing any fade-in progress
|
||||
if (animationState < -1)
|
||||
{
|
||||
animationState = -1;
|
||||
}
|
||||
else if (animationState <= 0) {
|
||||
else if (animationState <= 0)
|
||||
{
|
||||
animationState = animationState;
|
||||
}
|
||||
else {
|
||||
animationState = Math.max(-1, Math.min(0, - animationState));
|
||||
else
|
||||
{
|
||||
animationState = Math.max(-1, Math.min(0, -animationState));
|
||||
}
|
||||
funcToArriveAtState(animationState);
|
||||
scheduleAnimation();
|
||||
}
|
||||
|
||||
function doHide() {
|
||||
if (animationState >= -1 && animationState <= 0) {
|
||||
function doHide()
|
||||
{
|
||||
if (animationState >= -1 && animationState <= 0)
|
||||
{
|
||||
animationState = 1e-6;
|
||||
scheduleAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
function animateOneStep() {
|
||||
if (animationState < -1 || animationState == 0) {
|
||||
function animateOneStep()
|
||||
{
|
||||
if (animationState < -1 || animationState == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (animationState < 0) {
|
||||
else if (animationState < 0)
|
||||
{
|
||||
// animate show
|
||||
animationState += animationStep;
|
||||
if (animationState >= 0) {
|
||||
if (animationState >= 0)
|
||||
{
|
||||
animationState = 0;
|
||||
funcToArriveAtState(animationState);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
funcToArriveAtState(animationState);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (animationState > 0) {
|
||||
else if (animationState > 0)
|
||||
{
|
||||
// animate hide
|
||||
animationState += animationStep;
|
||||
if (animationState >= 1) {
|
||||
if (animationState >= 1)
|
||||
{
|
||||
animationState = 1;
|
||||
funcToArriveAtState(animationState);
|
||||
animationState = -2;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
funcToArriveAtState(animationState);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {show: doShow, hide: doHide, quickShow: doQuickShow};
|
||||
return {
|
||||
show: doShow,
|
||||
hide: doHide,
|
||||
quickShow: doQuickShow
|
||||
};
|
||||
},
|
||||
_nextActionId: 1,
|
||||
uncanceledActions: {},
|
||||
getCancellableAction: function(actionType, actionFunc) {
|
||||
getCancellableAction: function(actionType, actionFunc)
|
||||
{
|
||||
var o = padutils.uncanceledActions[actionType];
|
||||
if (! o) {
|
||||
if (!o)
|
||||
{
|
||||
o = {};
|
||||
padutils.uncanceledActions[actionType] = o;
|
||||
}
|
||||
var actionId = (padutils._nextActionId++);
|
||||
o[actionId] = true;
|
||||
return function() {
|
||||
return function()
|
||||
{
|
||||
var p = padutils.uncanceledActions[actionType];
|
||||
if (p && p[actionId]) {
|
||||
if (p && p[actionId])
|
||||
{
|
||||
actionFunc();
|
||||
}
|
||||
};
|
||||
},
|
||||
cancelActions: function(actionType) {
|
||||
cancelActions: function(actionType)
|
||||
{
|
||||
var o = padutils.uncanceledActions[actionType];
|
||||
if (o) {
|
||||
if (o)
|
||||
{
|
||||
// clear it
|
||||
delete padutils.uncanceledActions[actionType];
|
||||
}
|
||||
},
|
||||
makeFieldLabeledWhenEmpty: function(field, labelText) {
|
||||
makeFieldLabeledWhenEmpty: function(field, labelText)
|
||||
{
|
||||
field = $(field);
|
||||
function clear() {
|
||||
|
||||
function clear()
|
||||
{
|
||||
field.addClass('editempty');
|
||||
field.val(labelText);
|
||||
}
|
||||
field.focus(function() {
|
||||
if (field.hasClass('editempty')) {
|
||||
field.focus(function()
|
||||
{
|
||||
if (field.hasClass('editempty'))
|
||||
{
|
||||
field.val('');
|
||||
}
|
||||
field.removeClass('editempty');
|
||||
});
|
||||
field.blur(function() {
|
||||
if (! field.val()) {
|
||||
field.blur(function()
|
||||
{
|
||||
if (!field.val())
|
||||
{
|
||||
clear();
|
||||
}
|
||||
});
|
||||
return {clear:clear};
|
||||
return {
|
||||
clear: clear
|
||||
};
|
||||
},
|
||||
getCheckbox: function(node) {
|
||||
getCheckbox: function(node)
|
||||
{
|
||||
return $(node).is(':checked');
|
||||
},
|
||||
setCheckbox: function(node, value) {
|
||||
if (value) {
|
||||
setCheckbox: function(node, value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
$(node).attr('checked', 'checked');
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$(node).removeAttr('checked');
|
||||
}
|
||||
},
|
||||
bindCheckboxChange: function(node, func) {
|
||||
bindCheckboxChange: function(node, func)
|
||||
{
|
||||
$(node).bind("click change", func);
|
||||
},
|
||||
encodeUserId: function(userId) {
|
||||
return userId.replace(/[^a-y0-9]/g, function(c) {
|
||||
encodeUserId: function(userId)
|
||||
{
|
||||
return userId.replace(/[^a-y0-9]/g, function(c)
|
||||
{
|
||||
if (c == ".") return "-";
|
||||
return 'z'+c.charCodeAt(0)+'z';
|
||||
return 'z' + c.charCodeAt(0) + 'z';
|
||||
});
|
||||
},
|
||||
decodeUserId: function(encodedUserId) {
|
||||
return encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, function(cc) {
|
||||
decodeUserId: function(encodedUserId)
|
||||
{
|
||||
return encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, function(cc)
|
||||
{
|
||||
if (cc == '-') return '.';
|
||||
else if (cc.charAt(0) == 'z') {
|
||||
return String.fromCharCode(Number(cc.slice(1,-1)));
|
||||
else if (cc.charAt(0) == 'z')
|
||||
{
|
||||
return String.fromCharCode(Number(cc.slice(1, -1)));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
return cc;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,22 +1,26 @@
|
|||
plugins = {
|
||||
callHook: function (hookName, args) {
|
||||
callHook: function(hookName, args)
|
||||
{
|
||||
var hook = clientVars.hooks[hookName];
|
||||
if (hook === undefined)
|
||||
return [];
|
||||
if (hook === undefined) return [];
|
||||
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 pluginRes = eval(plugin.plugin)[plugin.original || hookName](args);
|
||||
if (pluginRes != undefined && pluginRes != null)
|
||||
res = res.concat(pluginRes);
|
||||
if (pluginRes != undefined && pluginRes != null) res = res.concat(pluginRes);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
callHookStr: function (hookName, args, sep, pre, post) {
|
||||
callHookStr: function(hookName, args, sep, pre, post)
|
||||
{
|
||||
if (sep == undefined) sep = '';
|
||||
if (pre == undefined) pre = '';
|
||||
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 || "");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -18,16 +18,43 @@
|
|||
|
||||
|
||||
|
||||
function newSkipList() {
|
||||
function newSkipList()
|
||||
{
|
||||
var PROFILER = window.PROFILER;
|
||||
if (!PROFILER) {
|
||||
PROFILER = function() { return {start:noop, mark:noop, literal:noop, end:noop, cancel:noop}; };
|
||||
if (!PROFILER)
|
||||
{
|
||||
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
|
||||
var start = {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 start = {
|
||||
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 totalWidth = 0;
|
||||
var keyToNodeMap = {};
|
||||
|
@ -38,43 +65,61 @@ function newSkipList() {
|
|||
// 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
|
||||
// this point.
|
||||
function _getPoint(targetLoc) {
|
||||
|
||||
|
||||
function _getPoint(targetLoc)
|
||||
{
|
||||
var numLevels = start.levels;
|
||||
var lvl = numLevels-1;
|
||||
var i = -1, ws = 0;
|
||||
var lvl = numLevels - 1;
|
||||
var i = -1,
|
||||
ws = 0;
|
||||
var nodes = new Array(numLevels);
|
||||
var idxs = new Array(numLevels);
|
||||
var widthSkips = new Array(numLevels);
|
||||
nodes[lvl] = start;
|
||||
idxs[lvl] = -1;
|
||||
widthSkips[lvl] = 0;
|
||||
while (lvl >= 0) {
|
||||
while (lvl >= 0)
|
||||
{
|
||||
var n = nodes[lvl];
|
||||
while (n.downPtrs[lvl] &&
|
||||
(i + n.downSkips[lvl] < targetLoc)) {
|
||||
i += n.downSkips[lvl];
|
||||
ws += n.downSkipWidths[lvl];
|
||||
n = n.downPtrs[lvl];
|
||||
while (n.downPtrs[lvl] && (i + n.downSkips[lvl] < targetLoc))
|
||||
{
|
||||
i += n.downSkips[lvl];
|
||||
ws += n.downSkipWidths[lvl];
|
||||
n = n.downPtrs[lvl];
|
||||
}
|
||||
nodes[lvl] = n;
|
||||
idxs[lvl] = i;
|
||||
widthSkips[lvl] = ws;
|
||||
lvl--;
|
||||
if (lvl >= 0) {
|
||||
nodes[lvl] = n;
|
||||
if (lvl >= 0)
|
||||
{
|
||||
nodes[lvl] = n;
|
||||
}
|
||||
}
|
||||
return {nodes:nodes, idxs:idxs, loc:targetLoc, widthSkips:widthSkips, toString: function() {
|
||||
return "getPoint("+targetLoc+")"; } };
|
||||
return {
|
||||
nodes: nodes,
|
||||
idxs: idxs,
|
||||
loc: targetLoc,
|
||||
widthSkips: widthSkips,
|
||||
toString: function()
|
||||
{
|
||||
return "getPoint(" + targetLoc + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
function _getNodeAtOffset(targetOffset) {
|
||||
|
||||
function _getNodeAtOffset(targetOffset)
|
||||
{
|
||||
var i = 0;
|
||||
var n = start;
|
||||
var lvl = start.levels-1;
|
||||
while (lvl >= 0 && n.downPtrs[lvl]) {
|
||||
while (n.downPtrs[lvl] && (i + n.downSkipWidths[lvl] <= targetOffset)) {
|
||||
i += n.downSkipWidths[lvl];
|
||||
n = n.downPtrs[lvl];
|
||||
var lvl = start.levels - 1;
|
||||
while (lvl >= 0 && n.downPtrs[lvl])
|
||||
{
|
||||
while (n.downPtrs[lvl] && (i + n.downSkipWidths[lvl] <= targetOffset))
|
||||
{
|
||||
i += n.downSkipWidths[lvl];
|
||||
n = n.downPtrs[lvl];
|
||||
}
|
||||
lvl--;
|
||||
}
|
||||
|
@ -82,10 +127,23 @@ function newSkipList() {
|
|||
else if (n === end) return (targetOffset == totalWidth ? (end.upPtrs[0] || null) : null);
|
||||
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 newNode = {key:newKey, levels: 0, upPtrs:[], downPtrs:[], downSkips:[], downSkipWidths:[]};
|
||||
var newNode = {
|
||||
key: newKey,
|
||||
levels: 0,
|
||||
upPtrs: [],
|
||||
downPtrs: [],
|
||||
downSkips: [],
|
||||
downSkipWidths: []
|
||||
};
|
||||
p.mark("donealloc");
|
||||
var pNodes = point.nodes;
|
||||
var pIdxs = point.idxs;
|
||||
|
@ -93,21 +151,23 @@ function newSkipList() {
|
|||
var widthLoc = point.widthSkips[0] + point.nodes[0].downSkipWidths[0];
|
||||
var newWidth = _entryWidth(entry);
|
||||
p.mark("loop1");
|
||||
while (newNode.levels == 0 || Math.random() < 0.01) {
|
||||
while (newNode.levels == 0 || Math.random() < 0.01)
|
||||
{
|
||||
var lvl = newNode.levels;
|
||||
newNode.levels++;
|
||||
if (lvl == pNodes.length) {
|
||||
// assume we have just passed the end of point.nodes, and reached one level greater
|
||||
// than the skiplist currently supports
|
||||
pNodes[lvl] = start;
|
||||
pIdxs[lvl] = -1;
|
||||
start.levels++;
|
||||
end.levels++;
|
||||
start.downPtrs[lvl] = end;
|
||||
end.upPtrs[lvl] = start;
|
||||
start.downSkips[lvl] = numNodes+1;
|
||||
start.downSkipWidths[lvl] = totalWidth;
|
||||
point.widthSkips[lvl] = 0;
|
||||
if (lvl == pNodes.length)
|
||||
{
|
||||
// assume we have just passed the end of point.nodes, and reached one level greater
|
||||
// than the skiplist currently supports
|
||||
pNodes[lvl] = start;
|
||||
pIdxs[lvl] = -1;
|
||||
start.levels++;
|
||||
end.levels++;
|
||||
start.downPtrs[lvl] = end;
|
||||
end.upPtrs[lvl] = start;
|
||||
start.downSkips[lvl] = numNodes + 1;
|
||||
start.downSkipWidths[lvl] = totalWidth;
|
||||
point.widthSkips[lvl] = 0;
|
||||
}
|
||||
var me = newNode;
|
||||
var up = pNodes[lvl];
|
||||
|
@ -127,82 +187,101 @@ function newSkipList() {
|
|||
}
|
||||
p.mark("loop2");
|
||||
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];
|
||||
up.downSkips[lvl]++;
|
||||
up.downSkipWidths[lvl] += newWidth;
|
||||
}
|
||||
p.mark("map");
|
||||
keyToNodeMap['$KEY$'+newKey] = newNode;
|
||||
keyToNodeMap['$KEY$' + newKey] = newNode;
|
||||
numNodes++;
|
||||
totalWidth += newWidth;
|
||||
p.end();
|
||||
}
|
||||
function _getNodeAtPoint(point) {
|
||||
|
||||
function _getNodeAtPoint(point)
|
||||
{
|
||||
return point.nodes[0].downPtrs[0];
|
||||
}
|
||||
function _incrementPoint(point) {
|
||||
|
||||
function _incrementPoint(point)
|
||||
{
|
||||
point.loc++;
|
||||
for(var i=0;i<point.nodes.length;i++) {
|
||||
if (point.idxs[i] + point.nodes[i].downSkips[i] < point.loc) {
|
||||
point.idxs[i] += point.nodes[i].downSkips[i];
|
||||
point.widthSkips[i] += point.nodes[i].downSkipWidths[i];
|
||||
point.nodes[i] = point.nodes[i].downPtrs[i];
|
||||
for (var i = 0; i < point.nodes.length; i++)
|
||||
{
|
||||
if (point.idxs[i] + point.nodes[i].downSkips[i] < point.loc)
|
||||
{
|
||||
point.idxs[i] += point.nodes[i].downSkips[i];
|
||||
point.widthSkips[i] += point.nodes[i].downSkipWidths[i];
|
||||
point.nodes[i] = point.nodes[i].downPtrs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
function _deleteKeyAtPoint(point) {
|
||||
|
||||
function _deleteKeyAtPoint(point)
|
||||
{
|
||||
var elem = point.nodes[0].downPtrs[0];
|
||||
var elemWidth = _entryWidth(elem.entry);
|
||||
for(var i=0;i<point.nodes.length;i++) {
|
||||
if (i < elem.levels) {
|
||||
var up = elem.upPtrs[i];
|
||||
var down = elem.downPtrs[i];
|
||||
var totalSkip = up.downSkips[i] + elem.downSkips[i] - 1;
|
||||
up.downPtrs[i] = down;
|
||||
down.upPtrs[i] = up;
|
||||
up.downSkips[i] = totalSkip;
|
||||
var totalWidthSkip = up.downSkipWidths[i] + elem.downSkipWidths[i] - elemWidth;
|
||||
up.downSkipWidths[i] = totalWidthSkip;
|
||||
for (var i = 0; i < point.nodes.length; i++)
|
||||
{
|
||||
if (i < elem.levels)
|
||||
{
|
||||
var up = elem.upPtrs[i];
|
||||
var down = elem.downPtrs[i];
|
||||
var totalSkip = up.downSkips[i] + elem.downSkips[i] - 1;
|
||||
up.downPtrs[i] = down;
|
||||
down.upPtrs[i] = up;
|
||||
up.downSkips[i] = totalSkip;
|
||||
var totalWidthSkip = up.downSkipWidths[i] + elem.downSkipWidths[i] - elemWidth;
|
||||
up.downSkipWidths[i] = totalWidthSkip;
|
||||
}
|
||||
else {
|
||||
var up = point.nodes[i];
|
||||
var down = up.downPtrs[i];
|
||||
up.downSkips[i]--;
|
||||
up.downSkipWidths[i] -= elemWidth;
|
||||
else
|
||||
{
|
||||
var up = point.nodes[i];
|
||||
var down = up.downPtrs[i];
|
||||
up.downSkips[i]--;
|
||||
up.downSkipWidths[i] -= elemWidth;
|
||||
}
|
||||
}
|
||||
delete keyToNodeMap['$KEY$'+elem.key];
|
||||
delete keyToNodeMap['$KEY$' + elem.key];
|
||||
numNodes--;
|
||||
totalWidth -= elemWidth;
|
||||
}
|
||||
function _propagateWidthChange(node) {
|
||||
|
||||
function _propagateWidthChange(node)
|
||||
{
|
||||
var oldWidth = node.downSkipWidths[0];
|
||||
var newWidth = _entryWidth(node.entry);
|
||||
var widthChange = newWidth - oldWidth;
|
||||
var n = node;
|
||||
var lvl = 0;
|
||||
while (lvl < n.levels) {
|
||||
while (lvl < n.levels)
|
||||
{
|
||||
n.downSkipWidths[lvl] += widthChange;
|
||||
lvl++;
|
||||
while (lvl >= n.levels && n.upPtrs[lvl-1]) {
|
||||
n = n.upPtrs[lvl-1];
|
||||
while (lvl >= n.levels && n.upPtrs[lvl - 1])
|
||||
{
|
||||
n = n.upPtrs[lvl - 1];
|
||||
}
|
||||
}
|
||||
totalWidth += widthChange;
|
||||
}
|
||||
function _getNodeIndex(node, byWidth) {
|
||||
|
||||
function _getNodeIndex(node, byWidth)
|
||||
{
|
||||
var dist = (byWidth ? 0 : -1);
|
||||
var n = node;
|
||||
while (n !== start) {
|
||||
var lvl = n.levels-1;
|
||||
while (n !== start)
|
||||
{
|
||||
var lvl = n.levels - 1;
|
||||
n = n.upPtrs[lvl];
|
||||
if (byWidth) dist += n.downSkipWidths[lvl];
|
||||
else dist += n.downSkips[lvl];
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
/*function _debugToString() {
|
||||
/*function _debugToString() {
|
||||
var array = [start];
|
||||
while (array[array.length-1] !== end) {
|
||||
array[array.length] = array[array.length-1].downPtrs[0];
|
||||
|
@ -224,32 +303,40 @@ function newSkipList() {
|
|||
return map(processedArray, function (x) { return x.toSource(); }).join("\n");
|
||||
}*/
|
||||
|
||||
function _getNodeByKey(key) {
|
||||
return keyToNodeMap['$KEY$'+key];
|
||||
function _getNodeByKey(key)
|
||||
{
|
||||
return keyToNodeMap['$KEY$' + key];
|
||||
}
|
||||
|
||||
// Returns index of first entry such that entryFunc(entry) is truthy,
|
||||
// or length() if no such entry. Assumes all falsy entries come before
|
||||
// all truthy entries.
|
||||
function _search(entryFunc) {
|
||||
|
||||
|
||||
function _search(entryFunc)
|
||||
{
|
||||
var low = start;
|
||||
var lvl = start.levels-1;
|
||||
var lvl = start.levels - 1;
|
||||
var lowIndex = -1;
|
||||
function f(node) {
|
||||
|
||||
function f(node)
|
||||
{
|
||||
if (node === start) return false;
|
||||
else if (node === end) return true;
|
||||
else return entryFunc(node.entry);
|
||||
}
|
||||
while (lvl >= 0) {
|
||||
while (lvl >= 0)
|
||||
{
|
||||
var nextLow = low.downPtrs[lvl];
|
||||
while (!f(nextLow)) {
|
||||
lowIndex += low.downSkips[lvl];
|
||||
low = nextLow;
|
||||
nextLow = low.downPtrs[lvl];
|
||||
while (!f(nextLow))
|
||||
{
|
||||
lowIndex += low.downSkips[lvl];
|
||||
low = nextLow;
|
||||
nextLow = low.downPtrs[lvl];
|
||||
}
|
||||
lvl--;
|
||||
}
|
||||
return lowIndex+1;
|
||||
return lowIndex + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -257,43 +344,55 @@ The skip-list contains "entries", JavaScript objects that each must have a uniqu
|
|||
that is a string.
|
||||
*/
|
||||
var self = {
|
||||
length: function() { return numNodes; },
|
||||
atIndex: function(i) {
|
||||
if (i < 0) console.warn("atIndex("+i+")");
|
||||
if (i >= numNodes) console.warn("atIndex("+i+">="+numNodes+")");
|
||||
length: function()
|
||||
{
|
||||
return numNodes;
|
||||
},
|
||||
atIndex: function(i)
|
||||
{
|
||||
if (i < 0) console.warn("atIndex(" + i + ")");
|
||||
if (i >= numNodes) console.warn("atIndex(" + i + ">=" + numNodes + ")");
|
||||
return _getNodeAtPoint(_getPoint(i)).entry;
|
||||
},
|
||||
// differs from Array.splice() in that new elements are in an array, not varargs
|
||||
splice: function(start, deleteCount, newEntryArray) {
|
||||
if (start < 0) console.warn("splice("+start+", ...)");
|
||||
if (start + deleteCount > numNodes) {
|
||||
console.warn("splice("+start+", "+deleteCount+", ...), N="+numNodes);
|
||||
console.warn("%s %s %s", typeof start, typeof deleteCount, typeof numNodes);
|
||||
console.trace();
|
||||
splice: function(start, deleteCount, newEntryArray)
|
||||
{
|
||||
if (start < 0) console.warn("splice(" + start + ", ...)");
|
||||
if (start + deleteCount > numNodes)
|
||||
{
|
||||
console.warn("splice(" + start + ", " + deleteCount + ", ...), N=" + numNodes);
|
||||
console.warn("%s %s %s", typeof start, typeof deleteCount, typeof numNodes);
|
||||
console.trace();
|
||||
}
|
||||
|
||||
if (! newEntryArray) newEntryArray = [];
|
||||
if (!newEntryArray) newEntryArray = [];
|
||||
var pt = _getPoint(start);
|
||||
for(var i=0;i<deleteCount;i++) {
|
||||
_deleteKeyAtPoint(pt);
|
||||
for (var i = 0; i < deleteCount; i++)
|
||||
{
|
||||
_deleteKeyAtPoint(pt);
|
||||
}
|
||||
for(var i=(newEntryArray.length-1);i>=0;i--) {
|
||||
var entry = newEntryArray[i];
|
||||
_insertKeyAtPoint(pt, entry.key, entry);
|
||||
var node = _getNodeByKey(entry.key);
|
||||
node.entry = entry;
|
||||
for (var i = (newEntryArray.length - 1); i >= 0; i--)
|
||||
{
|
||||
var entry = newEntryArray[i];
|
||||
_insertKeyAtPoint(pt, entry.key, entry);
|
||||
var node = _getNodeByKey(entry.key);
|
||||
node.entry = entry;
|
||||
}
|
||||
},
|
||||
next: function (entry) {
|
||||
next: function(entry)
|
||||
{
|
||||
return _getNodeByKey(entry.key).downPtrs[0].entry || null;
|
||||
},
|
||||
prev: function (entry) {
|
||||
prev: function(entry)
|
||||
{
|
||||
return _getNodeByKey(entry.key).upPtrs[0].entry || null;
|
||||
},
|
||||
push: function(entry) {
|
||||
push: function(entry)
|
||||
{
|
||||
self.splice(numNodes, 0, [entry]);
|
||||
},
|
||||
slice: function(start, end) {
|
||||
slice: function(start, end)
|
||||
{
|
||||
// act like Array.slice()
|
||||
if (start === undefined) start = 0;
|
||||
else if (start < 0) start += numNodes;
|
||||
|
@ -305,43 +404,81 @@ that is a string.
|
|||
if (end < 0) end = 0;
|
||||
if (end > numNodes) end = numNodes;
|
||||
|
||||
dmesg(String([start,end,numNodes]));
|
||||
dmesg(String([start, end, numNodes]));
|
||||
if (end <= start) return [];
|
||||
var n = self.atIndex(start);
|
||||
var array = [n];
|
||||
for(var i=1;i<(end-start);i++) {
|
||||
n = self.next(n);
|
||||
array.push(n);
|
||||
for (var i = 1; i < (end - start); i++)
|
||||
{
|
||||
n = self.next(n);
|
||||
array.push(n);
|
||||
}
|
||||
return array;
|
||||
},
|
||||
atKey: function(key) { return _getNodeByKey(key).entry; },
|
||||
indexOfKey: function(key) { return _getNodeIndex(_getNodeByKey(key)); },
|
||||
indexOfEntry: function (entry) { return self.indexOfKey(entry.key); },
|
||||
containsKey: function(key) { return !!(_getNodeByKey(key)); },
|
||||
atKey: function(key)
|
||||
{
|
||||
return _getNodeByKey(key).entry;
|
||||
},
|
||||
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
|
||||
atOffset: function(offset) { return _getNodeAtOffset(offset).entry; },
|
||||
keyAtOffset: function(offset) { 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) {
|
||||
atOffset: function(offset)
|
||||
{
|
||||
return _getNodeAtOffset(offset).entry;
|
||||
},
|
||||
keyAtOffset: function(offset)
|
||||
{
|
||||
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 >= numNodes) return totalWidth;
|
||||
return self.offsetOfEntry(self.atIndex(i));
|
||||
},
|
||||
indexOfOffset: function(offset) {
|
||||
indexOfOffset: function(offset)
|
||||
{
|
||||
if (offset <= 0) return 0;
|
||||
if (offset >= totalWidth) return numNodes;
|
||||
return self.indexOfEntry(self.atOffset(offset));
|
||||
},
|
||||
search: function(entryFunc) {
|
||||
search: function(entryFunc)
|
||||
{
|
||||
return _search(entryFunc);
|
||||
},
|
||||
//debugToString: _debugToString,
|
||||
debugGetPoint: _getPoint,
|
||||
debugDepth: function() { return start.levels; }
|
||||
debugDepth: function()
|
||||
{
|
||||
return start.levels;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -14,12 +14,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
if (window._orig_windowOpen) {
|
||||
if (window._orig_windowOpen)
|
||||
{
|
||||
window.open = _orig_windowOpen;
|
||||
}
|
||||
if (window._orig_windowSetTimeout) {
|
||||
if (window._orig_windowSetTimeout)
|
||||
{
|
||||
window.setTimeout = _orig_windowSetTimeout;
|
||||
}
|
||||
if (window._orig_windowSetInterval) {
|
||||
if (window._orig_windowSetInterval)
|
||||
{
|
||||
window.setInterval = _orig_windowSetInterval;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,8 +15,10 @@
|
|||
*/
|
||||
|
||||
|
||||
undoModule = (function() {
|
||||
var stack = (function() {
|
||||
undoModule = (function()
|
||||
{
|
||||
var stack = (function()
|
||||
{
|
||||
var stackElements = [];
|
||||
// two types of stackElements:
|
||||
// 1) { elementType: UNDOABLE_EVENT, eventType: "anything", [backset: <changeset>,]
|
||||
|
@ -28,119 +30,153 @@ undoModule = (function() {
|
|||
var UNDOABLE_EVENT = "undoableEvent";
|
||||
var EXTERNAL_CHANGE = "externalChange";
|
||||
|
||||
function clearStack() {
|
||||
function clearStack()
|
||||
{
|
||||
stackElements.length = 0;
|
||||
stackElements.push({ elementType: UNDOABLE_EVENT, eventType: "bottom" });
|
||||
stackElements.push(
|
||||
{
|
||||
elementType: UNDOABLE_EVENT,
|
||||
eventType: "bottom"
|
||||
});
|
||||
numUndoableEvents = 1;
|
||||
}
|
||||
clearStack();
|
||||
|
||||
function pushEvent(event) {
|
||||
var e = extend({}, event);
|
||||
function pushEvent(event)
|
||||
{
|
||||
var e = extend(
|
||||
{}, event);
|
||||
e.elementType = UNDOABLE_EVENT;
|
||||
stackElements.push(e);
|
||||
numUndoableEvents++;
|
||||
//dmesg("pushEvent backset: "+event.backset);
|
||||
}
|
||||
|
||||
function pushExternalChange(cs) {
|
||||
var idx = stackElements.length-1;
|
||||
if (stackElements[idx].elementType == EXTERNAL_CHANGE) {
|
||||
stackElements[idx].changeset = Changeset.compose(stackElements[idx].changeset, cs, getAPool());
|
||||
function pushExternalChange(cs)
|
||||
{
|
||||
var idx = stackElements.length - 1;
|
||||
if (stackElements[idx].elementType == EXTERNAL_CHANGE)
|
||||
{
|
||||
stackElements[idx].changeset = Changeset.compose(stackElements[idx].changeset, cs, getAPool());
|
||||
}
|
||||
else {
|
||||
stackElements.push({elementType: EXTERNAL_CHANGE, changeset: cs});
|
||||
else
|
||||
{
|
||||
stackElements.push(
|
||||
{
|
||||
elementType: EXTERNAL_CHANGE,
|
||||
changeset: cs
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _exposeEvent(nthFromTop) {
|
||||
function _exposeEvent(nthFromTop)
|
||||
{
|
||||
// precond: 0 <= nthFromTop < numUndoableEvents
|
||||
var targetIndex = stackElements.length - 1 - nthFromTop;
|
||||
var idx = stackElements.length - 1;
|
||||
while (idx > targetIndex || stackElements[idx].elementType == EXTERNAL_CHANGE) {
|
||||
if (stackElements[idx].elementType == EXTERNAL_CHANGE) {
|
||||
var ex = stackElements[idx];
|
||||
var un = stackElements[idx-1];
|
||||
if (un.backset) {
|
||||
var excs = ex.changeset;
|
||||
var unbs = un.backset;
|
||||
un.backset = Changeset.follow(excs, un.backset, false, getAPool());
|
||||
ex.changeset = Changeset.follow(unbs, ex.changeset, true, getAPool());
|
||||
if ((typeof un.selStart) == "number") {
|
||||
var newSel = Changeset.characterRangeFollow(excs, un.selStart, un.selEnd);
|
||||
un.selStart = newSel[0];
|
||||
un.selEnd = newSel[1];
|
||||
if (un.selStart == un.selEnd) {
|
||||
un.selFocusAtStart = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
stackElements[idx-1] = ex;
|
||||
stackElements[idx] = un;
|
||||
if (idx >= 2 && stackElements[idx-2].elementType == EXTERNAL_CHANGE) {
|
||||
ex.changeset = Changeset.compose(stackElements[idx-2].changeset,
|
||||
ex.changeset, getAPool());
|
||||
stackElements.splice(idx-2, 1);
|
||||
idx--;
|
||||
}
|
||||
}
|
||||
else {
|
||||
idx--;
|
||||
}
|
||||
while (idx > targetIndex || stackElements[idx].elementType == EXTERNAL_CHANGE)
|
||||
{
|
||||
if (stackElements[idx].elementType == EXTERNAL_CHANGE)
|
||||
{
|
||||
var ex = stackElements[idx];
|
||||
var un = stackElements[idx - 1];
|
||||
if (un.backset)
|
||||
{
|
||||
var excs = ex.changeset;
|
||||
var unbs = un.backset;
|
||||
un.backset = Changeset.follow(excs, un.backset, false, getAPool());
|
||||
ex.changeset = Changeset.follow(unbs, ex.changeset, true, getAPool());
|
||||
if ((typeof un.selStart) == "number")
|
||||
{
|
||||
var newSel = Changeset.characterRangeFollow(excs, un.selStart, un.selEnd);
|
||||
un.selStart = newSel[0];
|
||||
un.selEnd = newSel[1];
|
||||
if (un.selStart == un.selEnd)
|
||||
{
|
||||
un.selFocusAtStart = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
stackElements[idx - 1] = ex;
|
||||
stackElements[idx] = un;
|
||||
if (idx >= 2 && stackElements[idx - 2].elementType == EXTERNAL_CHANGE)
|
||||
{
|
||||
ex.changeset = Changeset.compose(stackElements[idx - 2].changeset, ex.changeset, getAPool());
|
||||
stackElements.splice(idx - 2, 1);
|
||||
idx--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
idx--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNthFromTop(n) {
|
||||
function getNthFromTop(n)
|
||||
{
|
||||
// precond: 0 <= n < numEvents()
|
||||
_exposeEvent(n);
|
||||
return stackElements[stackElements.length - 1 - n];
|
||||
}
|
||||
|
||||
function numEvents() {
|
||||
function numEvents()
|
||||
{
|
||||
return numUndoableEvents;
|
||||
}
|
||||
|
||||
function popEvent() {
|
||||
function popEvent()
|
||||
{
|
||||
// precond: numEvents() > 0
|
||||
_exposeEvent(0);
|
||||
numUndoableEvents--;
|
||||
return stackElements.pop();
|
||||
}
|
||||
|
||||
return {numEvents:numEvents, popEvent:popEvent, pushEvent: pushEvent,
|
||||
pushExternalChange: pushExternalChange, clearStack: clearStack,
|
||||
getNthFromTop:getNthFromTop};
|
||||
return {
|
||||
numEvents: numEvents,
|
||||
popEvent: popEvent,
|
||||
pushEvent: pushEvent,
|
||||
pushExternalChange: pushExternalChange,
|
||||
clearStack: clearStack,
|
||||
getNthFromTop: getNthFromTop
|
||||
};
|
||||
})();
|
||||
|
||||
// invariant: stack always has at least one undoable event
|
||||
|
||||
var undoPtr = 0; // zero-index from top of stack, 0 == top
|
||||
|
||||
function clearHistory() {
|
||||
function clearHistory()
|
||||
{
|
||||
stack.clearStack();
|
||||
undoPtr = 0;
|
||||
}
|
||||
|
||||
function _charOccurrences(str, c) {
|
||||
function _charOccurrences(str, c)
|
||||
{
|
||||
var i = 0;
|
||||
var count = 0;
|
||||
while (i >= 0 && i < str.length) {
|
||||
while (i >= 0 && i < str.length)
|
||||
{
|
||||
i = str.indexOf(c, i);
|
||||
if (i >= 0) {
|
||||
count++;
|
||||
i++;
|
||||
if (i >= 0)
|
||||
{
|
||||
count++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function _opcodeOccurrences(cs, opcode) {
|
||||
function _opcodeOccurrences(cs, opcode)
|
||||
{
|
||||
return _charOccurrences(Changeset.unpack(cs).ops, opcode);
|
||||
}
|
||||
|
||||
function _mergeChangesets(cs1, cs2) {
|
||||
if (! cs1) return cs2;
|
||||
if (! cs2) return cs1;
|
||||
function _mergeChangesets(cs1, cs2)
|
||||
{
|
||||
if (!cs1) return cs2;
|
||||
if (!cs2) return cs1;
|
||||
|
||||
// Rough heuristic for whether changesets should be considered one action:
|
||||
// each does exactly one insertion, no dels, and the composition does also; or
|
||||
|
@ -152,71 +188,91 @@ undoModule = (function() {
|
|||
var plusCount2 = _opcodeOccurrences(cs2, '+');
|
||||
var minusCount1 = _opcodeOccurrences(cs1, '-');
|
||||
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 plusCount3 = _opcodeOccurrences(merge, '+');
|
||||
var minusCount3 = _opcodeOccurrences(merge, '-');
|
||||
if (plusCount3 == 1 && minusCount3 == 0) {
|
||||
return merge;
|
||||
if (plusCount3 == 1 && minusCount3 == 0)
|
||||
{
|
||||
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 plusCount3 = _opcodeOccurrences(merge, '+');
|
||||
var minusCount3 = _opcodeOccurrences(merge, '-');
|
||||
if (plusCount3 == 0 && minusCount3 == 1) {
|
||||
return merge;
|
||||
if (plusCount3 == 0 && minusCount3 == 1)
|
||||
{
|
||||
return merge;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function reportEvent(event) {
|
||||
function reportEvent(event)
|
||||
{
|
||||
var topEvent = stack.getNthFromTop(0);
|
||||
|
||||
function applySelectionToTop() {
|
||||
if ((typeof event.selStart) == "number") {
|
||||
topEvent.selStart = event.selStart;
|
||||
topEvent.selEnd = event.selEnd;
|
||||
topEvent.selFocusAtStart = event.selFocusAtStart;
|
||||
function applySelectionToTop()
|
||||
{
|
||||
if ((typeof event.selStart) == "number")
|
||||
{
|
||||
topEvent.selStart = event.selStart;
|
||||
topEvent.selEnd = event.selEnd;
|
||||
topEvent.selFocusAtStart = event.selFocusAtStart;
|
||||
}
|
||||
}
|
||||
|
||||
if ((! event.backset) || Changeset.isIdentity(event.backset)) {
|
||||
if ((!event.backset) || Changeset.isIdentity(event.backset))
|
||||
{
|
||||
applySelectionToTop();
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
var merged = false;
|
||||
if (topEvent.eventType == event.eventType) {
|
||||
var merge = _mergeChangesets(event.backset, topEvent.backset);
|
||||
if (merge) {
|
||||
topEvent.backset = merge;
|
||||
//dmesg("reportEvent merge: "+merge);
|
||||
applySelectionToTop();
|
||||
merged = true;
|
||||
}
|
||||
if (topEvent.eventType == event.eventType)
|
||||
{
|
||||
var merge = _mergeChangesets(event.backset, topEvent.backset);
|
||||
if (merge)
|
||||
{
|
||||
topEvent.backset = merge;
|
||||
//dmesg("reportEvent merge: "+merge);
|
||||
applySelectionToTop();
|
||||
merged = true;
|
||||
}
|
||||
}
|
||||
if (! merged) {
|
||||
stack.pushEvent(event);
|
||||
if (!merged)
|
||||
{
|
||||
stack.pushEvent(event);
|
||||
}
|
||||
undoPtr = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function reportExternalChange(changeset) {
|
||||
if (changeset && ! Changeset.isIdentity(changeset)) {
|
||||
function reportExternalChange(changeset)
|
||||
{
|
||||
if (changeset && !Changeset.isIdentity(changeset))
|
||||
{
|
||||
stack.pushExternalChange(changeset);
|
||||
}
|
||||
}
|
||||
|
||||
function _getSelectionInfo(event) {
|
||||
if ((typeof event.selStart) != "number") {
|
||||
function _getSelectionInfo(event)
|
||||
{
|
||||
if ((typeof event.selStart) != "number")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return {selStart: event.selStart, selEnd: event.selEnd,
|
||||
selFocusAtStart: event.selFocusAtStart};
|
||||
else
|
||||
{
|
||||
return {
|
||||
selStart: event.selStart,
|
||||
selEnd: event.selEnd,
|
||||
selFocusAtStart: event.selFocusAtStart
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,10 +282,12 @@ undoModule = (function() {
|
|||
// or can be called with no arguments to mean that no undo is possible.
|
||||
// "eventFunc" will be called exactly once.
|
||||
|
||||
function performUndo(eventFunc) {
|
||||
if (undoPtr < stack.numEvents()-1) {
|
||||
function performUndo(eventFunc)
|
||||
{
|
||||
if (undoPtr < stack.numEvents() - 1)
|
||||
{
|
||||
var backsetEvent = stack.getNthFromTop(undoPtr);
|
||||
var selectionEvent = stack.getNthFromTop(undoPtr+1);
|
||||
var selectionEvent = stack.getNthFromTop(undoPtr + 1);
|
||||
var undoEvent = eventFunc(backsetEvent.backset, _getSelectionInfo(selectionEvent));
|
||||
stack.pushEvent(undoEvent);
|
||||
undoPtr += 2;
|
||||
|
@ -237,8 +295,10 @@ undoModule = (function() {
|
|||
else eventFunc();
|
||||
}
|
||||
|
||||
function performRedo(eventFunc) {
|
||||
if (undoPtr >= 2) {
|
||||
function performRedo(eventFunc)
|
||||
{
|
||||
if (undoPtr >= 2)
|
||||
{
|
||||
var backsetEvent = stack.getNthFromTop(0);
|
||||
var selectionEvent = stack.getNthFromTop(1);
|
||||
eventFunc(backsetEvent.backset, _getSelectionInfo(selectionEvent));
|
||||
|
@ -248,11 +308,18 @@ undoModule = (function() {
|
|||
else eventFunc();
|
||||
}
|
||||
|
||||
function getAPool() {
|
||||
function getAPool()
|
||||
{
|
||||
return undoModule.apool;
|
||||
}
|
||||
|
||||
return {clearHistory:clearHistory, reportEvent:reportEvent, reportExternalChange:reportExternalChange,
|
||||
performUndo:performUndo, performRedo:performRedo, enabled: true,
|
||||
apool: null}; // apool is filled in by caller
|
||||
})();
|
||||
return {
|
||||
clearHistory: clearHistory,
|
||||
reportEvent: reportEvent,
|
||||
reportExternalChange: reportExternalChange,
|
||||
performUndo: performUndo,
|
||||
performRedo: performRedo,
|
||||
enabled: true,
|
||||
apool: null
|
||||
}; // apool is filled in by caller
|
||||
})();
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -14,8 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function makeVirtualLineView(lineNode) {
|
||||
|
||||
function makeVirtualLineView(lineNode)
|
||||
{
|
||||
|
||||
// how much to jump forward or backward at once in a charSeeker before
|
||||
// constructing a DOM node and checking the coordinates (which takes a
|
||||
// significant fraction of a millisecond). From the
|
||||
|
@ -26,12 +27,15 @@ function makeVirtualLineView(lineNode) {
|
|||
var maxCharIncrement = 20;
|
||||
var seekerAtEnd = null;
|
||||
|
||||
function getNumChars() {
|
||||
function getNumChars()
|
||||
{
|
||||
return lineNode.textContent.length;
|
||||
}
|
||||
|
||||
function getNumVirtualLines() {
|
||||
if (! seekerAtEnd) {
|
||||
|
||||
function getNumVirtualLines()
|
||||
{
|
||||
if (!seekerAtEnd)
|
||||
{
|
||||
var seeker = makeCharSeeker();
|
||||
seeker.forwardByWhile(maxCharIncrement);
|
||||
seekerAtEnd = seeker;
|
||||
|
@ -39,75 +43,112 @@ function makeVirtualLineView(lineNode) {
|
|||
return seekerAtEnd.getVirtualLine() + 1;
|
||||
}
|
||||
|
||||
function getVLineAndOffsetForChar(lineChar) {
|
||||
function getVLineAndOffsetForChar(lineChar)
|
||||
{
|
||||
var seeker = makeCharSeeker();
|
||||
seeker.forwardByWhile(maxCharIncrement, null, lineChar);
|
||||
var theLine = seeker.getVirtualLine();
|
||||
seeker.backwardByWhile(8, function() { return seeker.getVirtualLine() == theLine; });
|
||||
seeker.forwardByWhile(1, function() { return seeker.getVirtualLine() != theLine; });
|
||||
seeker.backwardByWhile(8, function()
|
||||
{
|
||||
return seeker.getVirtualLine() == theLine;
|
||||
});
|
||||
seeker.forwardByWhile(1, function()
|
||||
{
|
||||
return seeker.getVirtualLine() != theLine;
|
||||
});
|
||||
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.
|
||||
// if offset is beyond end of line, for example, will give new offset at end of line.
|
||||
var seeker = makeCharSeeker();
|
||||
// go to start of line
|
||||
seeker.binarySearch(function() {
|
||||
seeker.binarySearch(function()
|
||||
{
|
||||
return seeker.getVirtualLine() >= vline;
|
||||
});
|
||||
var lineStart = seeker.getOffset();
|
||||
var theLine = seeker.getVirtualLine();
|
||||
// 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
|
||||
seeker.backwardByWhile(1, function() { return seeker.getVirtualLine() != theLine; }, lineStart);
|
||||
seeker.backwardByWhile(1, function()
|
||||
{
|
||||
return seeker.getVirtualLine() != theLine;
|
||||
}, lineStart);
|
||||
var lineChar = seeker.getOffset();
|
||||
var theOffset = lineChar - lineStart;
|
||||
// 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();
|
||||
theOffset += lineLen-lineChar;
|
||||
theOffset += lineLen - lineChar;
|
||||
lineChar = lineLen;
|
||||
}
|
||||
|
||||
return { vline:theLine, offset:theOffset, lineChar:lineChar };
|
||||
|
||||
return {
|
||||
vline: theLine,
|
||||
offset: theOffset,
|
||||
lineChar: lineChar
|
||||
};
|
||||
}
|
||||
|
||||
return {getNumVirtualLines:getNumVirtualLines, getVLineAndOffsetForChar:getVLineAndOffsetForChar,
|
||||
getCharForVLineAndOffset:getCharForVLineAndOffset,
|
||||
makeCharSeeker: function() { return makeCharSeeker(); } };
|
||||
return {
|
||||
getNumVirtualLines: getNumVirtualLines,
|
||||
getVLineAndOffsetForChar: getVLineAndOffsetForChar,
|
||||
getCharForVLineAndOffset: getCharForVLineAndOffset,
|
||||
makeCharSeeker: function()
|
||||
{
|
||||
return makeCharSeeker();
|
||||
}
|
||||
};
|
||||
|
||||
function deepFirstChildTextNode(nd) {
|
||||
function deepFirstChildTextNode(nd)
|
||||
{
|
||||
nd = nd.firstChild;
|
||||
while (nd && nd.firstChild) nd = nd.firstChild;
|
||||
if (nd.data) return nd;
|
||||
return null;
|
||||
}
|
||||
|
||||
function makeCharSeeker(/*lineNode*/) {
|
||||
|
||||
function charCoords(tnode, i) {
|
||||
function makeCharSeeker( /*lineNode*/ )
|
||||
{
|
||||
|
||||
function charCoords(tnode, i)
|
||||
{
|
||||
var container = tnode.parentNode;
|
||||
|
||||
// treat space specially; a space at the end of a virtual line
|
||||
// will have weird coordinates
|
||||
var isSpace = (tnode.nodeValue.charAt(i) === " ");
|
||||
if (isSpace) {
|
||||
if (i == 0) {
|
||||
if (container.previousSibling && deepFirstChildTextNode(container.previousSibling)) {
|
||||
tnode = deepFirstChildTextNode(container.previousSibling);
|
||||
i = tnode.length-1;
|
||||
container = tnode.parentNode;
|
||||
}
|
||||
else {
|
||||
return {top:container.offsetTop, left:container.offsetLeft};
|
||||
}
|
||||
}
|
||||
else {
|
||||
i--; // use previous char
|
||||
}
|
||||
if (isSpace)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
if (container.previousSibling && deepFirstChildTextNode(container.previousSibling))
|
||||
{
|
||||
tnode = deepFirstChildTextNode(container.previousSibling);
|
||||
i = tnode.length - 1;
|
||||
container = tnode.parentNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return {
|
||||
top: container.offsetTop,
|
||||
left: container.offsetLeft
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i--; // use previous char
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -119,16 +160,18 @@ function makeVirtualLineView(lineNode) {
|
|||
frag.appendChild(document.createTextNode(tnodeText.substring(0, i)));
|
||||
charWrapper.appendChild(document.createTextNode(tnodeText.substr(i, 1)));
|
||||
frag.appendChild(charWrapper);
|
||||
frag.appendChild(document.createTextNode(tnodeText.substring(i+1)));
|
||||
frag.appendChild(document.createTextNode(tnodeText.substring(i + 1)));
|
||||
container.replaceChild(frag, tnode);
|
||||
|
||||
var result = {top:charWrapper.offsetTop,
|
||||
left:charWrapper.offsetLeft + (isSpace ? charWrapper.offsetWidth : 0),
|
||||
height:charWrapper.offsetHeight};
|
||||
|
||||
|
||||
var result = {
|
||||
top: charWrapper.offsetTop,
|
||||
left: charWrapper.offsetLeft + (isSpace ? charWrapper.offsetWidth : 0),
|
||||
height: charWrapper.offsetHeight
|
||||
};
|
||||
|
||||
while (container.firstChild) container.removeChild(container.firstChild);
|
||||
container.appendChild(tnode);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -143,136 +186,186 @@ function makeVirtualLineView(lineNode) {
|
|||
var approxLineHeight;
|
||||
var whichLine = 0;
|
||||
|
||||
function nextNode() {
|
||||
function nextNode()
|
||||
{
|
||||
var n = curNode;
|
||||
if (! n) n = lineNode.firstChild;
|
||||
if (!n) n = lineNode.firstChild;
|
||||
else n = n.nextSibling;
|
||||
while (n && ! deepFirstChildTextNode(n)) {
|
||||
n = n.nextSibling;
|
||||
while (n && !deepFirstChildTextNode(n))
|
||||
{
|
||||
n = n.nextSibling;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
function prevNode() {
|
||||
|
||||
function prevNode()
|
||||
{
|
||||
var n = curNode;
|
||||
if (! n) n = lineNode.lastChild;
|
||||
if (!n) n = lineNode.lastChild;
|
||||
else n = n.previousSibling;
|
||||
while (n && ! deepFirstChildTextNode(n)) {
|
||||
n = n.previousSibling;
|
||||
while (n && !deepFirstChildTextNode(n))
|
||||
{
|
||||
n = n.previousSibling;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
var seeker;
|
||||
if (lineLength > 0) {
|
||||
if (lineLength > 0)
|
||||
{
|
||||
curNode = nextNode();
|
||||
var firstCharData = charCoords(deepFirstChildTextNode(curNode), 0);
|
||||
approxLineHeight = firstCharData.height;
|
||||
curTop = firstCharData.top;
|
||||
curLeft = firstCharData.left;
|
||||
|
||||
function updateCharData(tnode, i) {
|
||||
var coords = charCoords(tnode, i);
|
||||
whichLine += Math.round((coords.top - curTop) / approxLineHeight);
|
||||
curTop = coords.top;
|
||||
curLeft = coords.left;
|
||||
function updateCharData(tnode, i)
|
||||
{
|
||||
var coords = charCoords(tnode, i);
|
||||
whichLine += Math.round((coords.top - curTop) / approxLineHeight);
|
||||
curTop = coords.top;
|
||||
curLeft = coords.left;
|
||||
}
|
||||
|
||||
seeker = {
|
||||
forward: function(numChars) {
|
||||
var oldChar = curChar;
|
||||
var newChar = curChar + numChars;
|
||||
if (newChar > (lineLength-1))
|
||||
newChar = lineLength-1;
|
||||
while (curChar < newChar) {
|
||||
var curNodeLength = deepFirstChildTextNode(curNode).length;
|
||||
var toGo = curNodeLength - curCharWithinNode;
|
||||
if (curChar + toGo > newChar || ! nextNode()) {
|
||||
// going to next node would be too far
|
||||
var n = newChar - curChar;
|
||||
if (n >= toGo) n = toGo-1;
|
||||
curChar += n;
|
||||
curCharWithinNode += n;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// go to next node
|
||||
curChar += toGo;
|
||||
curCharWithinNode = 0;
|
||||
curNode = nextNode();
|
||||
}
|
||||
}
|
||||
updateCharData(deepFirstChildTextNode(curNode), curCharWithinNode);
|
||||
return curChar - oldChar;
|
||||
},
|
||||
backward: function(numChars) {
|
||||
var oldChar = curChar;
|
||||
var newChar = curChar - numChars;
|
||||
if (newChar < 0) newChar = 0;
|
||||
while (curChar > newChar) {
|
||||
if (curChar - curCharWithinNode <= newChar || !prevNode()) {
|
||||
// going to prev node would be too far
|
||||
var n = curChar - newChar;
|
||||
if (n > curCharWithinNode) n = curCharWithinNode;
|
||||
curChar -= n;
|
||||
curCharWithinNode -= n;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// go to prev node
|
||||
curChar -= curCharWithinNode+1;
|
||||
curNode = prevNode();
|
||||
curCharWithinNode = deepFirstChildTextNode(curNode).length-1;
|
||||
}
|
||||
}
|
||||
updateCharData(deepFirstChildTextNode(curNode), curCharWithinNode);
|
||||
return oldChar - curChar;
|
||||
},
|
||||
getVirtualLine: function() { return whichLine; },
|
||||
getLeftCoord: function() { return curLeft; }
|
||||
forward: function(numChars)
|
||||
{
|
||||
var oldChar = curChar;
|
||||
var newChar = curChar + numChars;
|
||||
if (newChar > (lineLength - 1)) newChar = lineLength - 1;
|
||||
while (curChar < newChar)
|
||||
{
|
||||
var curNodeLength = deepFirstChildTextNode(curNode).length;
|
||||
var toGo = curNodeLength - curCharWithinNode;
|
||||
if (curChar + toGo > newChar || !nextNode())
|
||||
{
|
||||
// going to next node would be too far
|
||||
var n = newChar - curChar;
|
||||
if (n >= toGo) n = toGo - 1;
|
||||
curChar += n;
|
||||
curCharWithinNode += n;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// go to next node
|
||||
curChar += toGo;
|
||||
curCharWithinNode = 0;
|
||||
curNode = nextNode();
|
||||
}
|
||||
}
|
||||
updateCharData(deepFirstChildTextNode(curNode), curCharWithinNode);
|
||||
return curChar - oldChar;
|
||||
},
|
||||
backward: function(numChars)
|
||||
{
|
||||
var oldChar = curChar;
|
||||
var newChar = curChar - numChars;
|
||||
if (newChar < 0) newChar = 0;
|
||||
while (curChar > newChar)
|
||||
{
|
||||
if (curChar - curCharWithinNode <= newChar || !prevNode())
|
||||
{
|
||||
// going to prev node would be too far
|
||||
var n = curChar - newChar;
|
||||
if (n > curCharWithinNode) n = curCharWithinNode;
|
||||
curChar -= n;
|
||||
curCharWithinNode -= n;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// go to prev node
|
||||
curChar -= curCharWithinNode + 1;
|
||||
curNode = prevNode();
|
||||
curCharWithinNode = deepFirstChildTextNode(curNode).length - 1;
|
||||
}
|
||||
}
|
||||
updateCharData(deepFirstChildTextNode(curNode), curCharWithinNode);
|
||||
return oldChar - curChar;
|
||||
},
|
||||
getVirtualLine: function()
|
||||
{
|
||||
return whichLine;
|
||||
},
|
||||
getLeftCoord: function()
|
||||
{
|
||||
return curLeft;
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
curLeft = lineNode.offsetLeft;
|
||||
seeker = { forward: function(numChars) { return 0; },
|
||||
backward: function(numChars) { return 0; },
|
||||
getVirtualLine: function() { return 0; },
|
||||
getLeftCoord: function() { return curLeft; }
|
||||
};
|
||||
seeker = {
|
||||
forward: function(numChars)
|
||||
{
|
||||
return 0;
|
||||
},
|
||||
backward: function(numChars)
|
||||
{
|
||||
return 0;
|
||||
},
|
||||
getVirtualLine: function()
|
||||
{
|
||||
return 0;
|
||||
},
|
||||
getLeftCoord: function()
|
||||
{
|
||||
return curLeft;
|
||||
}
|
||||
};
|
||||
}
|
||||
seeker.getOffset = function() { return curChar; };
|
||||
seeker.getLineLength = function() { return lineLength; };
|
||||
seeker.toString = function() {
|
||||
return "seeker[curChar: "+curChar+"("+lineText.charAt(curChar)+"), left: "+seeker.getLeftCoord()+", vline: "+seeker.getVirtualLine()+"]";
|
||||
seeker.getOffset = function()
|
||||
{
|
||||
return curChar;
|
||||
};
|
||||
seeker.getLineLength = function()
|
||||
{
|
||||
return lineLength;
|
||||
};
|
||||
seeker.toString = function()
|
||||
{
|
||||
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 hasCondFunc = ((typeof optCondFunc) == "function");
|
||||
var condFunc = optCondFunc;
|
||||
var hasCharLimit = ((typeof optCharLimit) == "number");
|
||||
var charLimit = optCharLimit;
|
||||
while (charsMovedLast !== 0 && ((! hasCondFunc) || condFunc())) {
|
||||
var toMove = amount;
|
||||
if (hasCharLimit) {
|
||||
var untilLimit = (isBackward ? curChar - charLimit : charLimit - curChar);
|
||||
if (untilLimit < toMove) toMove = untilLimit;
|
||||
}
|
||||
if (toMove < 0) break;
|
||||
charsMovedLast = (isBackward ? seeker.backward(toMove) : seeker.forward(toMove));
|
||||
while (charsMovedLast !== 0 && ((!hasCondFunc) || condFunc()))
|
||||
{
|
||||
var toMove = amount;
|
||||
if (hasCharLimit)
|
||||
{
|
||||
var untilLimit = (isBackward ? curChar - charLimit : charLimit - curChar);
|
||||
if (untilLimit < toMove) toMove = untilLimit;
|
||||
}
|
||||
if (toMove < 0) break;
|
||||
charsMovedLast = (isBackward ? seeker.backward(toMove) : seeker.forward(toMove));
|
||||
}
|
||||
}
|
||||
|
||||
seeker.forwardByWhile = function(amount, optCondFunc, optCharLimit) {
|
||||
|
||||
seeker.forwardByWhile = function(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);
|
||||
}
|
||||
seeker.binarySearch = function(condFunc) {
|
||||
seeker.binarySearch = function(condFunc)
|
||||
{
|
||||
// returns index of boundary between false chars and true chars;
|
||||
// positions seeker at first true char, or else last char
|
||||
var trueFunc = condFunc;
|
||||
var falseFunc = function() { return ! condFunc(); };
|
||||
var falseFunc = function()
|
||||
{
|
||||
return !condFunc();
|
||||
};
|
||||
seeker.forwardByWhile(20, falseFunc);
|
||||
seeker.backwardByWhile(20, trueFunc);
|
||||
seeker.forwardByWhile(10, falseFunc);
|
||||
|
@ -280,7 +373,7 @@ function makeVirtualLineView(lineNode) {
|
|||
seeker.forwardByWhile(1, falseFunc);
|
||||
return seeker.getOffset() + (condFunc() ? 0 : 1);
|
||||
}
|
||||
|
||||
|
||||
return seeker;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue