diff options
author | Peter 'Pita' Martischka <petermartischka@googlemail.com> | 2011-07-07 18:59:34 +0100 |
---|---|---|
committer | Peter 'Pita' Martischka <petermartischka@googlemail.com> | 2011-07-07 18:59:34 +0100 |
commit | 271ee1776b6900d90d138d6770fe288768162ca1 (patch) | |
tree | b6996fed98a30d24bb6cad2b3b60e5367b180858 /static/js | |
parent | 2fa1d8768b52ebf7483a4ecb41c2a0ad007efaef (diff) | |
download | etherpad-lite-271ee1776b6900d90d138d6770fe288768162ca1.zip |
beautified all static js files
Diffstat (limited to 'static/js')
36 files changed, 9584 insertions, 6163 deletions
diff --git a/static/js/ace.js b/static/js/ace.js index 07906491..e71b857a 100644 --- a/static/js/ace.js +++ b/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.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.getFrame = function() + { + return info.frame || null; + }; + editor.focus = pendingInit(function() + { + info.ace_focus(); + }); + editor.setEditable = pendingInit(function(newVal) + { + info.ace_setEditable(newVal); + }); + editor.getFormattedCode = function() + { + return info.ace_getFormattedCode(); + }; + editor.setOnKeyPress = pendingInit(function(handler) + { + info.ace_setOnKeyPress(handler); + }); + editor.setOnKeyDown = pendingInit(function(handler) + { + info.ace_setOnKeyDown(handler); + }); + editor.setNotifyDirty = pendingInit(function(handler) + { + info.ace_setNotifyDirty(handler); + }); + + editor.setProperty = pendingInit(function(key, value) + { + info.ace_setProperty(key, value); + }); + editor.getDebugProperty = function(prop) + { + return info.ace_getDebugProperty(prop); + }; + + editor.setBaseText = pendingInit(function(txt) + { + info.ace_setBaseText(txt); + }); + editor.setBaseAttributedText = pendingInit(function(atxt, apoolJsonObj) + { + info.ace_setBaseAttributedText(atxt, apoolJsonObj); + }); + editor.applyChangesToBase = pendingInit(function(changes, optAuthor, apoolJsonObj) + { + info.ace_applyChangesToBase(changes, optAuthor, apoolJsonObj); + }); // prepareUserChangeset: // Returns null if no new changes or ACE not ready. Otherwise, bundles up all user changes // to the latest base text into a Changeset, which is returned (as a string if encodeAsString). @@ -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 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 + 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>']; + + 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; diff --git a/static/js/ace2_common.js b/static/js/ace2_common.js index 4a08de6f..02dc350f 100644 --- a/static/js/ace2_common.js +++ b/static/js/ace2_common.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. @@ -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'); } diff --git a/static/js/ace2_inner.js b/static/js/ace2_inner.js index 842c4ea6..69fce950 100644 --- a/static/js/ace2_inner.js +++ b/static/js/ace2_inner.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. @@ -14,13 +14,14 @@ * limitations under the License. */ -function OUTER(gscope) { +function OUTER(gscope) +{ - var DEBUG=false;//$$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;" -// changed to false + var DEBUG = false; //$$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;" + // changed to false var isSetUp = false; - var THE_TAB = ' ';//4 + var THE_TAB = ' '; //4 var MAX_LIST_LEVEL = 8; var LINE_NUMBER_PADDING_RIGHT = 4; @@ -45,24 +46,35 @@ function OUTER(gscope) { var overlaysdiv = lineMetricsDiv.nextSibling; initLineNumbers(); - var outsideKeyDown = function(evt) {}; - var outsideKeyPress = function(evt) { return true; }; - var outsideNotifyDirty = function() {}; + var outsideKeyDown = function(evt) + {}; + var outsideKeyPress = function(evt) + { + return true; + }; + var outsideNotifyDirty = function() + {}; // selFocusAtStart -- determines whether the selection extends "backwards", so that the focus // point (controlled with the arrow keys) is at the beginning; not supported in IE, though // native IE selections have that behavior (which we try not to interfere with). // Must be false if selection is collapsed! - var rep = { lines: newSkipList(), selStart: null, selEnd: null, selFocusAtStart: false, - alltext: "", alines: [], - apool: new AttribPool() }; + var rep = { + lines: newSkipList(), + selStart: null, + selEnd: null, + selFocusAtStart: false, + alltext: "", + alines: [], + apool: new AttribPool() + }; // lines, alltext, alines, and DOM are set up in setup() - if (undoModule.enabled) { + if (undoModule.enabled) + { undoModule.apool = rep.apool; } var root, doc; // set in setup() - var isEditable = true; var doesWrap = true; var hasLineNumbers = true; @@ -71,24 +83,42 @@ function OUTER(gscope) { // space around the innermost iframe element var iframePadLeft = MIN_LINEDIV_WIDTH + LINE_NUMBER_PADDING_RIGHT + EDIT_BODY_PADDING_LEFT; var iframePadTop = EDIT_BODY_PADDING_TOP; - var iframePadBottom = 0, iframePadRight = 0; + var iframePadBottom = 0, + iframePadRight = 0; var console = (DEBUG && window.console); - if (! window.console) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", - "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + if (!window.console) + { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; console = {}; for (var i = 0; i < names.length; ++i) - console[names[i]] = function() {}; + console[names[i]] = function() + {}; //console.error = function(str) { alert(str); }; } 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 identity(x) + { + return x; } - function noop() {} - function identity(x) { return x; } // "dmesg" is for displaying messages in the in-page output pane // visible when "?djs=1" is appended to the pad URL. It generally @@ -101,28 +131,39 @@ function OUTER(gscope) { var textFace = 'monospace'; var textSize = 12; - function textLineHeight() { return Math.round(textSize * 4/3); } + + function textLineHeight() + { + return Math.round(textSize * 4 / 3); + } var dynamicCSS = null; - function initDynamicCSS() { + + function initDynamicCSS() + { dynamicCSS = makeCSSManager("dynamicsyntax"); } var changesetTracker = makeChangesetTracker(scheduler, rep.apool, { - withCallbacks: function(operationName, f) { - inCallStackIfNecessary(operationName, function() { + withCallbacks: function(operationName, f) + { + inCallStackIfNecessary(operationName, function() + { fastIncorp(1); - f({ - setDocumentAttributedText: function(atext) { - setDocAText(atext); + f( + { + setDocumentAttributedText: function(atext) + { + setDocAText(atext); }, - applyChangesetToDocument: function(changeset, preferInsertionAfterCaret) { - var oldEventType = currentCallStack.editEvent.eventType; - currentCallStack.startNewEvent("nonundoable"); + applyChangesetToDocument: function(changeset, preferInsertionAfterCaret) + { + var oldEventType = currentCallStack.editEvent.eventType; + currentCallStack.startNewEvent("nonundoable"); - performDocumentApplyChangeset(changeset, preferInsertionAfterCaret); + performDocumentApplyChangeset(changeset, preferInsertionAfterCaret); - currentCallStack.startNewEvent(oldEventType); + currentCallStack.startNewEvent(oldEventType); } }); }); @@ -131,71 +172,95 @@ function OUTER(gscope) { var authorInfos = {}; // presence of key determines if author is present in doc - function setAuthorInfo(author, info) { - if ((typeof author) != "string") { - throw new Error("setAuthorInfo: author ("+author+") is not a string"); + function setAuthorInfo(author, info) + { + if ((typeof author) != "string") + { + throw new Error("setAuthorInfo: author (" + author + ") is not a string"); } - if (! info) { + if (!info) + { delete authorInfos[author]; - if (dynamicCSS) { - dynamicCSS.removeSelectorStyle(getAuthorColorClassSelector(getAuthorClassName(author))); + if (dynamicCSS) + { + dynamicCSS.removeSelectorStyle(getAuthorColorClassSelector(getAuthorClassName(author))); } } - else { + else + { authorInfos[author] = info; - if (info.bgcolor) { - if (dynamicCSS) { - var bgcolor = info.bgcolor; - if ((typeof info.fade) == "number") { - bgcolor = fadeColor(bgcolor, info.fade); - } + if (info.bgcolor) + { + if (dynamicCSS) + { + var bgcolor = info.bgcolor; + if ((typeof info.fade) == "number") + { + bgcolor = fadeColor(bgcolor, info.fade); + } - dynamicCSS.selectorStyle(getAuthorColorClassSelector( - getAuthorClassName(author))).backgroundColor = bgcolor; - } + dynamicCSS.selectorStyle(getAuthorColorClassSelector( + getAuthorClassName(author))).backgroundColor = bgcolor; + } } } } - function getAuthorClassName(author) { - return "author-"+author.replace(/[^a-y0-9]/g, function(c) { + function getAuthorClassName(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'; }); } - function className2Author(className) { - if (className.substring(0,7) == "author-") { - return className.substring(7).replace(/[a-y0-9]+|-|z.+?z/g, function(cc) { + + function className2Author(className) + { + if (className.substring(0, 7) == "author-") + { + return className.substring(7).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; } }); } return null; } - function getAuthorColorClassSelector(oneClassName) { - return ".authorColors ."+oneClassName; + + function getAuthorColorClassSelector(oneClassName) + { + return ".authorColors ." + oneClassName; } - function setUpTrackingCSS() { - if (dynamicCSS) { + + function setUpTrackingCSS() + { + if (dynamicCSS) + { var backgroundHeight = lineMetricsDiv.offsetHeight; var lineHeight = textLineHeight(); var extraBodding = 0; var extraTodding = 0; - if (backgroundHeight < lineHeight) { - extraBodding = Math.ceil((lineHeight - backgroundHeight)/2); - extraTodding = lineHeight - backgroundHeight - extraBodding; + if (backgroundHeight < lineHeight) + { + extraBodding = Math.ceil((lineHeight - backgroundHeight) / 2); + extraTodding = lineHeight - backgroundHeight - extraBodding; } var spanStyle = dynamicCSS.selectorStyle("#innerdocbody span"); - spanStyle.paddingTop = extraTodding+"px"; - spanStyle.paddingBottom = extraBodding+"px"; + spanStyle.paddingTop = extraTodding + "px"; + spanStyle.paddingBottom = extraBodding + "px"; } } - function boldColorFromColor(lightColorCSS) { + + function boldColorFromColor(lightColorCSS) + { var color = colorutils.css2triple(lightColorCSS); // amp up the saturation to full @@ -206,128 +271,174 @@ function OUTER(gscope) { return colorutils.triple2css(color); } - function fadeColor(colorCSS, fadeFrac) { + + function fadeColor(colorCSS, fadeFrac) + { var color = colorutils.css2triple(colorCSS); - color = colorutils.blend(color, [1,1,1], fadeFrac); + color = colorutils.blend(color, [1, 1, 1], fadeFrac); return colorutils.triple2css(color); } - function doAlert(str) { - scheduler.setTimeout(function() { alert(str); }, 0); + function doAlert(str) + { + scheduler.setTimeout(function() + { + alert(str); + }, 0); } - editorInfo.ace_getRep = function () { + editorInfo.ace_getRep = function() + { return rep; } var currentCallStack = null; - function inCallStack(type, action) { + + function inCallStack(type, action) + { if (disposed) return; - if (currentCallStack) { - console.error("Can't enter callstack "+type+", already in "+ - currentCallStack.type); + if (currentCallStack) + { + console.error("Can't enter callstack " + type + ", already in " + currentCallStack.type); } var profiling = false; - function profileRest() { + + function profileRest() + { profiling = true; console.profile(); } - function newEditEvent(eventType) { - return {eventType:eventType, backset: null}; - } - - function submitOldEvent(evt) { - if (rep.selStart && rep.selEnd) { - var selStartChar = - rep.lines.offsetOfIndex(rep.selStart[0]) + rep.selStart[1]; - var selEndChar = - rep.lines.offsetOfIndex(rep.selEnd[0]) + rep.selEnd[1]; - evt.selStart = selStartChar; - evt.selEnd = selEndChar; - evt.selFocusAtStart = rep.selFocusAtStart; - } - if (undoModule.enabled) { - var undoWorked = false; - try { - if (evt.eventType == "setup" || evt.eventType == "importText" || - evt.eventType == "setBaseText") { - undoModule.clearHistory(); - } - else if (evt.eventType == "nonundoable") { - if (evt.changeset) { - undoModule.reportExternalChange(evt.changeset); - } - } - else { - undoModule.reportEvent(evt); - } - undoWorked = true; - } - finally { - if (! undoWorked) { - undoModule.enabled = false; // for safety - } - } - } - } - - function startNewEvent(eventType, dontSubmitOld) { + function newEditEvent(eventType) + { + return { + eventType: eventType, + backset: null + }; + } + + function submitOldEvent(evt) + { + if (rep.selStart && rep.selEnd) + { + var selStartChar = rep.lines.offsetOfIndex(rep.selStart[0]) + rep.selStart[1]; + var selEndChar = rep.lines.offsetOfIndex(rep.selEnd[0]) + rep.selEnd[1]; + evt.selStart = selStartChar; + evt.selEnd = selEndChar; + evt.selFocusAtStart = rep.selFocusAtStart; + } + if (undoModule.enabled) + { + var undoWorked = false; + try + { + if (evt.eventType == "setup" || evt.eventType == "importText" || evt.eventType == "setBaseText") + { + undoModule.clearHistory(); + } + else if (evt.eventType == "nonundoable") + { + if (evt.changeset) + { + undoModule.reportExternalChange(evt.changeset); + } + } + else + { + undoModule.reportEvent(evt); + } + undoWorked = true; + } + finally + { + if (!undoWorked) + { + undoModule.enabled = false; // for safety + } + } + } + } + + function startNewEvent(eventType, dontSubmitOld) + { var oldEvent = currentCallStack.editEvent; - if (! dontSubmitOld) { - submitOldEvent(oldEvent); + if (!dontSubmitOld) + { + submitOldEvent(oldEvent); } currentCallStack.editEvent = newEditEvent(eventType); return oldEvent; } - currentCallStack = {type: type, docTextChanged: false, selectionAffected: false, - userChangedSelection: false, - domClean: false, profileRest:profileRest, - isUserChange: false, // is this a "user change" type of call-stack - repChanged: false, editEvent: newEditEvent(type), - startNewEvent:startNewEvent}; + currentCallStack = { + type: type, + docTextChanged: false, + selectionAffected: false, + userChangedSelection: false, + domClean: false, + profileRest: profileRest, + isUserChange: false, + // is this a "user change" type of call-stack + repChanged: false, + editEvent: newEditEvent(type), + startNewEvent: startNewEvent + }; var cleanExit = false; var result; - try { + try + { result = action(); //console.log("Just did action for: "+type); cleanExit = true; } - catch (e) { - caughtErrors.push({error: e, time: +new Date()}); + catch (e) + { + caughtErrors.push( + { + error: e, + time: +new Date() + }); dmesg(e.toString()); throw e; } - finally { + finally + { var cs = currentCallStack; //console.log("Finished action for: "+type); - if (cleanExit) { - submitOldEvent(cs.editEvent); - if (cs.domClean && cs.type != "setup") { - if (cs.isUserChange) { - if (cs.repChanged) parenModule.notifyChange(); - else parenModule.notifyTick(); - } - recolorModule.recolorLines(); - if (cs.selectionAffected) { - updateBrowserSelectionFromRep(); - } - if ((cs.docTextChanged || cs.userChangedSelection) && cs.type != "applyChangesToBase") { - scrollSelectionIntoView(); - } - if (cs.docTextChanged && cs.type.indexOf("importText") < 0) { - outsideNotifyDirty(); - } - } - } - else { - // non-clean exit - if (currentCallStack.type == "idleWorkTimer") { - idleWorkTimer.atLeast(1000); - } + if (cleanExit) + { + submitOldEvent(cs.editEvent); + if (cs.domClean && cs.type != "setup") + { + if (cs.isUserChange) + { + if (cs.repChanged) parenModule.notifyChange(); + else parenModule.notifyTick(); + } + recolorModule.recolorLines(); + if (cs.selectionAffected) + { + updateBrowserSelectionFromRep(); + } + if ((cs.docTextChanged || cs.userChangedSelection) && cs.type != "applyChangesToBase") + { + scrollSelectionIntoView(); + } + if (cs.docTextChanged && cs.type.indexOf("importText") < 0) + { + outsideNotifyDirty(); + } + } + } + else + { + // non-clean exit + if (currentCallStack.type == "idleWorkTimer") + { + idleWorkTimer.atLeast(1000); + } } currentCallStack = null; if (profiling) console.profileEnd(); @@ -336,59 +447,74 @@ function OUTER(gscope) { } editorInfo.ace_inCallStack = inCallStack; - function inCallStackIfNecessary(type, action) { - if (! currentCallStack) { + function inCallStackIfNecessary(type, action) + { + if (!currentCallStack) + { inCallStack(type, action); } - else { + else + { action(); } } editorInfo.ace_inCallStackIfNecessary = inCallStackIfNecessary; - function recolorLineByKey(key) { - if (rep.lines.containsKey(key)) { + function recolorLineByKey(key) + { + if (rep.lines.containsKey(key)) + { var offset = rep.lines.offsetOfKey(key); var width = rep.lines.atKey(key).width; recolorLinesInRange(offset, offset + width); } } - function getLineKeyForOffset(charOffset) { + function getLineKeyForOffset(charOffset) + { return rep.lines.atOffset(charOffset).key; } - var recolorModule = (function() { + var recolorModule = (function() + { var dirtyLineKeys = {}; var module = {}; - module.setCharNeedsRecoloring = function(offset) { - if (offset >= rep.alltext.length) { - offset = rep.alltext.length-1; + module.setCharNeedsRecoloring = function(offset) + { + if (offset >= rep.alltext.length) + { + offset = rep.alltext.length - 1; } dirtyLineKeys[getLineKeyForOffset(offset)] = true; } - module.setCharRangeNeedsRecoloring = function(offset1, offset2) { - if (offset1 >= rep.alltext.length) { - offset1 = rep.alltext.length-1; + module.setCharRangeNeedsRecoloring = function(offset1, offset2) + { + if (offset1 >= rep.alltext.length) + { + offset1 = rep.alltext.length - 1; } - if (offset2 >= rep.alltext.length) { - offset2 = rep.alltext.length-1; + if (offset2 >= rep.alltext.length) + { + offset2 = rep.alltext.length - 1; } var firstEntry = rep.lines.atOffset(offset1); var lastKey = rep.lines.atOffset(offset2).key; dirtyLineKeys[lastKey] = true; var entry = firstEntry; - while (entry && entry.key != lastKey) { - dirtyLineKeys[entry.key] = true; - entry = rep.lines.next(entry); + while (entry && entry.key != lastKey) + { + dirtyLineKeys[entry.key] = true; + entry = rep.lines.next(entry); } } - module.recolorLines = function() { - for(var k in dirtyLineKeys) { - recolorLineByKey(k); + module.recolorLines = function() + { + for (var k in dirtyLineKeys) + { + recolorLineByKey(k); } dirtyLineKeys = {}; } @@ -396,286 +522,382 @@ function OUTER(gscope) { return module; })(); - var parenModule = (function() { + var parenModule = (function() + { var module = {}; - module.notifyTick = function() { handleFlashing(false); }; - module.notifyChange = function() { handleFlashing(true); }; - module.shouldNormalizeOnChar = function (c) { - if (parenFlashRep.active) { - // avoid highlight style from carrying on to typed text - return true; + module.notifyTick = function() + { + handleFlashing(false); + }; + module.notifyChange = function() + { + handleFlashing(true); + }; + module.shouldNormalizeOnChar = function(c) + { + if (parenFlashRep.active) + { + // avoid highlight style from carrying on to typed text + return true; } c = String.fromCharCode(c); - return !! (bracketMap[c]); + return !!(bracketMap[c]); } - var parenFlashRep = { active: false, whichChars: null, whichLineKeys: null, expireTime: null }; - var bracketMap = {'(': 1, ')':-1, '[':2, ']':-2, '{':3, '}':-3}; + var parenFlashRep = { + active: false, + whichChars: null, + whichLineKeys: null, + expireTime: null + }; + var bracketMap = { + '(': 1, + ')': -1, + '[': 2, + ']': -2, + '{': 3, + '}': -3 + }; var bracketRegex = /[{}\[\]()]/g; - function handleFlashing(docChanged) { - function getSearchRange(aroundLoc) { - var rng = getVisibleCharRange(); - var d = 100; // minimum radius - var e = 3000; // maximum radius; - if (rng[0] > aroundLoc-d) rng[0] = aroundLoc-d; - if (rng[0] < aroundLoc-e) rng[0] = aroundLoc-e; - if (rng[0] < 0) rng[0] = 0; - if (rng[1] < aroundLoc+d) rng[1] = aroundLoc+d; - if (rng[1] > aroundLoc+e) rng[1] = aroundLoc+e; - if (rng[1] > rep.lines.totalWidth()) rng[1] = rep.lines.totalWidth(); - return rng; - } - function findMatchingVisibleBracket(startLoc, forwards) { - var rng = getSearchRange(startLoc); - var str = rep.alltext.substring(rng[0], rng[1]); - var bstr = str.replace(bracketRegex, '('); // handy for searching - var loc = startLoc - rng[0]; - var bracketState = []; - var foundParen = false; - var goodParen = false; - function nextLoc() { - if (loc < 0) return; - if (forwards) loc++; else loc--; - if (loc < 0 || loc >= str.length) loc = -1; - if (loc >= 0) { - if (forwards) loc = bstr.indexOf('(', loc); - else loc = bstr.lastIndexOf('(', loc); - } - } - while ((! foundParen) && (loc >= 0)) { - if (getCharType(loc + rng[0]) == "p") { - var b = bracketMap[str.charAt(loc)]; // -1, 1, -2, 2, -3, 3 - var into = forwards; - var typ = b; - if (typ < 0) { into = ! into; typ = -typ; } - if (into) bracketState.push(typ); - else { - var recent = bracketState.pop(); - if (recent != typ) { - foundParen = true; goodParen = false; - } - else if (bracketState.length == 0) { - foundParen = true; goodParen = true; - } - } - } - //console.log(bracketState.toSource()); - if ((! foundParen) && (loc >= 0)) nextLoc(); - } - if (! foundParen) return null; - return {chr: (loc + rng[0]), good: goodParen}; + + function handleFlashing(docChanged) + { + function getSearchRange(aroundLoc) + { + var rng = getVisibleCharRange(); + var d = 100; // minimum radius + var e = 3000; // maximum radius; + if (rng[0] > aroundLoc - d) rng[0] = aroundLoc - d; + if (rng[0] < aroundLoc - e) rng[0] = aroundLoc - e; + if (rng[0] < 0) rng[0] = 0; + if (rng[1] < aroundLoc + d) rng[1] = aroundLoc + d; + if (rng[1] > aroundLoc + e) rng[1] = aroundLoc + e; + if (rng[1] > rep.lines.totalWidth()) rng[1] = rep.lines.totalWidth(); + return rng; + } + + function findMatchingVisibleBracket(startLoc, forwards) + { + var rng = getSearchRange(startLoc); + var str = rep.alltext.substring(rng[0], rng[1]); + var bstr = str.replace(bracketRegex, '('); // handy for searching + var loc = startLoc - rng[0]; + var bracketState = []; + var foundParen = false; + var goodParen = false; + + function nextLoc() + { + if (loc < 0) return; + if (forwards) loc++; + else loc--; + if (loc < 0 || loc >= str.length) loc = -1; + if (loc >= 0) + { + if (forwards) loc = bstr.indexOf('(', loc); + else loc = bstr.lastIndexOf('(', loc); + } + } + while ((!foundParen) && (loc >= 0)) + { + if (getCharType(loc + rng[0]) == "p") + { + var b = bracketMap[str.charAt(loc)]; // -1, 1, -2, 2, -3, 3 + var into = forwards; + var typ = b; + if (typ < 0) + { + into = !into; + typ = -typ; + } + if (into) bracketState.push(typ); + else + { + var recent = bracketState.pop(); + if (recent != typ) + { + foundParen = true; + goodParen = false; + } + else if (bracketState.length == 0) + { + foundParen = true; + goodParen = true; + } + } + } + //console.log(bracketState.toSource()); + if ((!foundParen) && (loc >= 0)) nextLoc(); + } + if (!foundParen) return null; + return { + chr: (loc + rng[0]), + good: goodParen + }; } var r = parenFlashRep; var charsToHighlight = null; var linesToUnhighlight = null; - if (r.active && (docChanged || (now() > r.expireTime))) { - linesToUnhighlight = r.whichLineKeys; - r.active = false; - } - if ((! r.active) && docChanged && isCaret() && caretColumn() > 0) { - var caret = caretDocChar(); - if (caret > 0 && getCharType(caret-1) == "p") { - var charBefore = rep.alltext.charAt(caret-1); - if (bracketMap[charBefore]) { - var lookForwards = (bracketMap[charBefore] > 0); - var findResult = findMatchingVisibleBracket(caret-1, lookForwards); - if (findResult) { - var mateLoc = findResult.chr; - var mateGood = findResult.good; - r.active = true; - charsToHighlight = {}; - charsToHighlight[caret-1] = 'flash'; - charsToHighlight[mateLoc] = (mateGood ? 'flash' : 'flashbad'); - r.whichLineKeys = []; - r.whichLineKeys.push(getLineKeyForOffset(caret-1)); - r.whichLineKeys.push(getLineKeyForOffset(mateLoc)); - r.expireTime = now() + 4000; - newlyActive = true; - } - } - } - - } - if (linesToUnhighlight) { - recolorLineByKey(linesToUnhighlight[0]); - recolorLineByKey(linesToUnhighlight[1]); - } - if (r.active && charsToHighlight) { - function f(txt, cls, next, ofst) { - var flashClass = charsToHighlight[ofst]; - if (cls) { - next(txt, cls+" "+flashClass); - } - else next(txt, cls); - } - for(var c in charsToHighlight) { - recolorLinesInRange((+c), (+c)+1, null, f); - } + if (r.active && (docChanged || (now() > r.expireTime))) + { + linesToUnhighlight = r.whichLineKeys; + r.active = false; + } + if ((!r.active) && docChanged && isCaret() && caretColumn() > 0) + { + var caret = caretDocChar(); + if (caret > 0 && getCharType(caret - 1) == "p") + { + var charBefore = rep.alltext.charAt(caret - 1); + if (bracketMap[charBefore]) + { + var lookForwards = (bracketMap[charBefore] > 0); + var findResult = findMatchingVisibleBracket(caret - 1, lookForwards); + if (findResult) + { + var mateLoc = findResult.chr; + var mateGood = findResult.good; + r.active = true; + charsToHighlight = {}; + charsToHighlight[caret - 1] = 'flash'; + charsToHighlight[mateLoc] = (mateGood ? 'flash' : 'flashbad'); + r.whichLineKeys = []; + r.whichLineKeys.push(getLineKeyForOffset(caret - 1)); + r.whichLineKeys.push(getLineKeyForOffset(mateLoc)); + r.expireTime = now() + 4000; + newlyActive = true; + } + } + } + + } + if (linesToUnhighlight) + { + recolorLineByKey(linesToUnhighlight[0]); + recolorLineByKey(linesToUnhighlight[1]); + } + if (r.active && charsToHighlight) + { + function f(txt, cls, next, ofst) + { + var flashClass = charsToHighlight[ofst]; + if (cls) + { + next(txt, cls + " " + flashClass); + } + else next(txt, cls); + } + for (var c in charsToHighlight) + { + recolorLinesInRange((+c), (+c) + 1, null, f); + } } } return module; })(); - function dispose() { + function dispose() + { disposed = true; if (idleWorkTimer) idleWorkTimer.never(); teardown(); } - function checkALines() { + function checkALines() + { return; // disable for speed - function error() { throw new Error("checkALines"); } - if (rep.alines.length != rep.lines.length()) { + + + function error() + { + throw new Error("checkALines"); + } + if (rep.alines.length != rep.lines.length()) + { error(); } - for(var i=0;i<rep.alines.length;i++) { + for (var i = 0; i < rep.alines.length; i++) + { var aline = rep.alines[i]; - var lineText = rep.lines.atIndex(i).text+"\n"; + var lineText = rep.lines.atIndex(i).text + "\n"; var lineTextLength = lineText.length; var opIter = Changeset.opIterator(aline); var alineLength = 0; - while (opIter.hasNext()) { - var o = opIter.next(); - alineLength += o.chars; - if (opIter.hasNext()) { - if (o.lines != 0) error(); - } - else { - if (o.lines != 1) error(); - } + while (opIter.hasNext()) + { + var o = opIter.next(); + alineLength += o.chars; + if (opIter.hasNext()) + { + if (o.lines != 0) error(); + } + else + { + if (o.lines != 1) error(); + } } - if (alineLength != lineTextLength) { - error(); + if (alineLength != lineTextLength) + { + error(); } } } - function setWraps(newVal) { + function setWraps(newVal) + { doesWrap = newVal; var dwClass = "doesWrap"; setClassPresence(root, "doesWrap", doesWrap); - scheduler.setTimeout(function() { - inCallStackIfNecessary("setWraps", function() { - fastIncorp(7); - recreateDOM(); - fixView(); + scheduler.setTimeout(function() + { + inCallStackIfNecessary("setWraps", function() + { + fastIncorp(7); + recreateDOM(); + fixView(); }); }, 0); } - function setStyled(newVal) { + function setStyled(newVal) + { var oldVal = isStyled; - isStyled = !!newVal; - - if (newVal != oldVal) { - if (! newVal) { - // clear styles - inCallStackIfNecessary("setStyled", function() { - fastIncorp(12); - var clearStyles = []; - for(var k in STYLE_ATTRIBS) { - clearStyles.push([k,'']); - } - performDocumentApplyAttributesToCharRange(0, rep.alltext.length, clearStyles); - }); + isStyled = !! newVal; + + if (newVal != oldVal) + { + if (!newVal) + { + // clear styles + inCallStackIfNecessary("setStyled", function() + { + fastIncorp(12); + var clearStyles = []; + for (var k in STYLE_ATTRIBS) + { + clearStyles.push([k, '']); + } + performDocumentApplyAttributesToCharRange(0, rep.alltext.length, clearStyles); + }); } } } - function setTextFace(face) { + function setTextFace(face) + { textFace = face; root.style.fontFamily = textFace; lineMetricsDiv.style.fontFamily = textFace; - scheduler.setTimeout(function() { + scheduler.setTimeout(function() + { setUpTrackingCSS(); }, 0); } - function setTextSize(size) { + function setTextSize(size) + { textSize = size; - root.style.fontSize = textSize+"px"; - root.style.lineHeight = textLineHeight()+"px"; - sideDiv.style.lineHeight = textLineHeight()+"px"; - lineMetricsDiv.style.fontSize = textSize+"px"; - scheduler.setTimeout(function() { + root.style.fontSize = textSize + "px"; + root.style.lineHeight = textLineHeight() + "px"; + sideDiv.style.lineHeight = textLineHeight() + "px"; + lineMetricsDiv.style.fontSize = textSize + "px"; + scheduler.setTimeout(function() + { setUpTrackingCSS(); }, 0); } - function recreateDOM() { + function recreateDOM() + { // precond: normalized recolorLinesInRange(0, rep.alltext.length); } - function setEditable(newVal) { + function setEditable(newVal) + { isEditable = newVal; // the following may fail, e.g. if iframe is hidden - if (! isEditable) { + if (!isEditable) + { setDesignMode(false); } - else { + else + { setDesignMode(true); } - setClassPresence(root, "static", ! isEditable); + setClassPresence(root, "static", !isEditable); } - function enforceEditability() { + function enforceEditability() + { setEditable(isEditable); } - function importText(text, undoable, dontProcess) { + function importText(text, undoable, dontProcess) + { var lines; - if (dontProcess) { - if (text.charAt(text.length-1) != "\n") { - throw new Error("new raw text must end with newline"); + if (dontProcess) + { + if (text.charAt(text.length - 1) != "\n") + { + throw new Error("new raw text must end with newline"); } - if (/[\r\t\xa0]/.exec(text)) { - throw new Error("new raw text must not contain CR, tab, or nbsp"); + if (/[\r\t\xa0]/.exec(text)) + { + throw new Error("new raw text must not contain CR, tab, or nbsp"); } - lines = text.substring(0, text.length-1).split('\n'); + lines = text.substring(0, text.length - 1).split('\n'); } - else { + else + { lines = map(text.split('\n'), textify); } var newText = "\n"; - if (lines.length > 0) { - newText = lines.join('\n')+'\n'; + if (lines.length > 0) + { + newText = lines.join('\n') + '\n'; } - inCallStackIfNecessary("importText"+(undoable?"Undoable":""), function() { + inCallStackIfNecessary("importText" + (undoable ? "Undoable" : ""), function() + { setDocText(newText); }); - if (dontProcess && rep.alltext != text) { + if (dontProcess && rep.alltext != text) + { throw new Error("mismatch error setting raw text in importText"); } } - function importAText(atext, apoolJsonObj, undoable) { + function importAText(atext, apoolJsonObj, undoable) + { atext = Changeset.cloneAText(atext); - if (apoolJsonObj) { + if (apoolJsonObj) + { var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj); atext.attribs = Changeset.moveOpsToNewPool(atext.attribs, wireApool, rep.apool); } - inCallStackIfNecessary("importText"+(undoable?"Undoable":""), function() { + inCallStackIfNecessary("importText" + (undoable ? "Undoable" : ""), function() + { setDocAText(atext); }); } - function setDocAText(atext) { + function setDocAText(atext) + { fastIncorp(8); var oldLen = rep.lines.totalWidth(); var numLines = rep.lines.length(); - var upToLastLine = rep.lines.offsetOfIndex(numLines-1); - var lastLineLength = rep.lines.atIndex(numLines-1).text.length; + var upToLastLine = rep.lines.offsetOfIndex(numLines - 1); + var lastLineLength = rep.lines.atIndex(numLines - 1).text.length; var assem = Changeset.smartOpAssembler(); var o = Changeset.newOp('-'); o.chars = upToLastLine; - o.lines = numLines-1; + o.lines = numLines - 1; assem.append(o); o.chars = lastLineLength; o.lines = 0; @@ -683,107 +905,128 @@ function OUTER(gscope) { Changeset.appendATextToAssembler(atext, assem); var newLen = oldLen + assem.getLengthChange(); var changeset = Changeset.checkRep( - Changeset.pack(oldLen, newLen, assem.toString(), - atext.text.slice(0, -1))); + Changeset.pack(oldLen, newLen, assem.toString(), atext.text.slice(0, -1))); performDocumentApplyChangeset(changeset); - performSelectionChange([0,rep.lines.atIndex(0).lineMarker], - [0,rep.lines.atIndex(0).lineMarker]); + performSelectionChange([0, rep.lines.atIndex(0).lineMarker], [0, rep.lines.atIndex(0).lineMarker]); idleWorkTimer.atMost(100); - if (rep.alltext != atext.text) { + if (rep.alltext != atext.text) + { dmesg(htmlPrettyEscape(rep.alltext)); dmesg(htmlPrettyEscape(atext.text)); throw new Error("mismatch error setting raw text in setDocAText"); } } - function setDocText(text) { + function setDocText(text) + { setDocAText(Changeset.makeAText(text)); } - function getDocText() { + function getDocText() + { var alltext = rep.alltext; var len = alltext.length; if (len > 0) len--; // final extra newline return alltext.substring(0, len); } - function exportText() { - if (currentCallStack && ! currentCallStack.domClean) { - inCallStackIfNecessary("exportText", function() { fastIncorp(2); }); + function exportText() + { + if (currentCallStack && !currentCallStack.domClean) + { + inCallStackIfNecessary("exportText", function() + { + fastIncorp(2); + }); } return getDocText(); } - function editorChangedSize() { + function editorChangedSize() + { fixView(); } - function setOnKeyPress(handler) { + function setOnKeyPress(handler) + { outsideKeyPress = handler; } - function setOnKeyDown(handler) { + function setOnKeyDown(handler) + { outsideKeyDown = handler; } - function setNotifyDirty(handler) { + function setNotifyDirty(handler) + { outsideNotifyDirty = handler; } - function getFormattedCode() { - if (currentCallStack && ! currentCallStack.domClean) { + function getFormattedCode() + { + if (currentCallStack && !currentCallStack.domClean) + { inCallStackIfNecessary("getFormattedCode", incorporateUserChanges); } var buf = []; - if (rep.lines.length() > 0) { + if (rep.lines.length() > 0) + { // should be the case, even for empty file var entry = rep.lines.atIndex(0); - while (entry) { - var domInfo = entry.domInfo; - buf.push((domInfo && domInfo.getInnerHTML()) || - domline.processSpaces(domline.escapeHTML(entry.text), - doesWrap) || - ' ' /*empty line*/); - entry = rep.lines.next(entry); + while (entry) + { + var domInfo = entry.domInfo; + buf.push((domInfo && domInfo.getInnerHTML()) || domline.processSpaces(domline.escapeHTML(entry.text), doesWrap) || ' ' /*empty line*/ ); + entry = rep.lines.next(entry); } } - return '<div class="syntax"><div>'+buf.join('</div>\n<div>')+ - '</div></div>'; + return '<div class="syntax"><div>' + buf.join('</div>\n<div>') + '</div></div>'; } var CMDS = { - clearauthorship: function(prompt) { - if ((!(rep.selStart && rep.selEnd)) || isCaret()) { - if (prompt) { + clearauthorship: function(prompt) + { + if ((!(rep.selStart && rep.selEnd)) || isCaret()) + { + if (prompt) + { prompt(); } - else { - performDocumentApplyAttributesToCharRange(0, rep.alltext.length, - [['author', '']]); + else + { + performDocumentApplyAttributesToCharRange(0, rep.alltext.length, [ + ['author', ''] + ]); } } - else { + else + { setAttributeOnSelection('author', ''); } } }; - function execCommand(cmd) { + function execCommand(cmd) + { cmd = cmd.toLowerCase(); var cmdArgs = Array.prototype.slice.call(arguments, 1); - if (CMDS[cmd]) { - inCallStack(cmd, function() { - fastIncorp(9); - CMDS[cmd].apply(CMDS, cmdArgs); + if (CMDS[cmd]) + { + inCallStack(cmd, function() + { + fastIncorp(9); + CMDS[cmd].apply(CMDS, cmdArgs); }); } } - function replaceRange(start, end, text) { - inCallStack('replaceRange', function() { + function replaceRange(start, end, text) + { + inCallStack('replaceRange', function() + { fastIncorp(9); performDocumentReplaceRange(start, end, text); }); @@ -803,295 +1046,366 @@ function OUTER(gscope) { editorInfo.ace_execCommand = execCommand; editorInfo.ace_replaceRange = replaceRange; - editorInfo.ace_callWithAce = function (fn, callStack, normalize) { - var wrapper = function () { - return fn(editorInfo); - } - - if (normalize !== undefined) { + editorInfo.ace_callWithAce = function(fn, callStack, normalize) + { + var wrapper = function() + { + return fn(editorInfo); + } + + + + if (normalize !== undefined) + { var wrapper1 = wrapper; - wrapper = function () { + wrapper = function() + { editorInfo.ace_fastIncorp(9); - wrapper1(); + wrapper1(); } } - if (callStack !== undefined) { + if (callStack !== undefined) + { return editorInfo.ace_inCallStack(callStack, wrapper); - } else { + } + else + { return wrapper(); } } - editorInfo.ace_setProperty = function(key, value) { + editorInfo.ace_setProperty = function(key, value) + { var k = key.toLowerCase(); - if (k == "wraps") { + if (k == "wraps") + { setWraps(value); } - else if (k == "showsauthorcolors") { - setClassPresence(root, "authorColors", !!value); + else if (k == "showsauthorcolors") + { + setClassPresence(root, "authorColors", !! value); } - else if (k == "showsuserselections") { - setClassPresence(root, "userSelections", !!value); + else if (k == "showsuserselections") + { + setClassPresence(root, "userSelections", !! value); } - else if (k == "showslinenumbers") { - hasLineNumbers = !!value; - setClassPresence(sideDiv, "sidedivhidden", ! hasLineNumbers); + else if (k == "showslinenumbers") + { + hasLineNumbers = !! value; + setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers); fixView(); } - else if (k == "grayedout") { - setClassPresence(outerWin.document.body, "grayedout", !!value); + else if (k == "grayedout") + { + setClassPresence(outerWin.document.body, "grayedout", !! value); } - else if (k == "dmesg") { + else if (k == "dmesg") + { dmesg = value; window.dmesg = value; } - else if (k == 'userauthor') { + else if (k == 'userauthor') + { thisAuthor = String(value); } - else if (k == 'styled') { + else if (k == 'styled') + { setStyled(value); } - else if (k == 'textface') { + else if (k == 'textface') + { setTextFace(value); } - else if (k == 'textsize') { + else if (k == 'textsize') + { setTextSize(value); } } - editorInfo.ace_setBaseText = function(txt) { + editorInfo.ace_setBaseText = function(txt) + { changesetTracker.setBaseText(txt); }; - editorInfo.ace_setBaseAttributedText = function(atxt, apoolJsonObj) { + editorInfo.ace_setBaseAttributedText = function(atxt, apoolJsonObj) + { setUpTrackingCSS(); changesetTracker.setBaseAttributedText(atxt, apoolJsonObj); }; - editorInfo.ace_applyChangesToBase = function(c, optAuthor, apoolJsonObj) { + editorInfo.ace_applyChangesToBase = function(c, optAuthor, apoolJsonObj) + { changesetTracker.applyChangesToBase(c, optAuthor, apoolJsonObj); }; - editorInfo.ace_prepareUserChangeset = function() { + editorInfo.ace_prepareUserChangeset = function() + { return changesetTracker.prepareUserChangeset(); }; - editorInfo.ace_applyPreparedChangesetToBase = function() { + editorInfo.ace_applyPreparedChangesetToBase = function() + { changesetTracker.applyPreparedChangesetToBase(); }; - editorInfo.ace_setUserChangeNotificationCallback = function(f) { + editorInfo.ace_setUserChangeNotificationCallback = function(f) + { changesetTracker.setUserChangeNotificationCallback(f); }; - editorInfo.ace_setAuthorInfo = function(author, info) { + editorInfo.ace_setAuthorInfo = function(author, info) + { setAuthorInfo(author, info); }; - editorInfo.ace_setAuthorSelectionRange = function(author, start, end) { + editorInfo.ace_setAuthorSelectionRange = function(author, start, end) + { changesetTracker.setAuthorSelectionRange(author, start, end); }; - editorInfo.ace_getUnhandledErrors = function() { + editorInfo.ace_getUnhandledErrors = function() + { return caughtErrors.slice(); }; - editorInfo.ace_getDebugProperty = function(prop) { - if (prop == "debugger") { + editorInfo.ace_getDebugProperty = function(prop) + { + if (prop == "debugger") + { // obfuscate "eval" so as not to scare yuicompressor - window['ev'+'al']("debugger"); + window['ev' + 'al']("debugger"); } - else if (prop == "rep") { + else if (prop == "rep") + { return rep; } - else if (prop == "window") { + else if (prop == "window") + { return window; } - else if (prop == "document") { + else if (prop == "document") + { return document; } return undefined; }; - function now() { return (new Date()).getTime(); } + function now() + { + return (new Date()).getTime(); + } - function newTimeLimit(ms) { + function newTimeLimit(ms) + { //console.debug("new time limit"); var startTime = now(); var lastElapsed = 0; var exceededAlready = false; var printedTrace = false; - var isTimeUp = function () { - if (exceededAlready) { - if ((! printedTrace)) {// && now() - startTime - ms > 300) { - //console.trace(); - printedTrace = true; - } - return true; - } - var elapsed = now() - startTime; - if (elapsed > ms) { - exceededAlready = true; - //console.debug("time limit hit, before was %d/%d", lastElapsed, ms); - //console.trace(); - return true; - } - else { - lastElapsed = elapsed; - return false; - } - } - isTimeUp.elapsed = function() { return now() - startTime; } + var isTimeUp = function() + { + if (exceededAlready) + { + if ((!printedTrace)) + { // && now() - startTime - ms > 300) { + //console.trace(); + printedTrace = true; + } + return true; + } + var elapsed = now() - startTime; + if (elapsed > ms) + { + exceededAlready = true; + //console.debug("time limit hit, before was %d/%d", lastElapsed, ms); + //console.trace(); + return true; + } + else + { + lastElapsed = elapsed; + return false; + } + } + + isTimeUp.elapsed = function() + { + return now() - startTime; + } return isTimeUp; } - function makeIdleAction(func) { + function makeIdleAction(func) + { var scheduledTimeout = null; var scheduledTime = 0; - function unschedule() { - if (scheduledTimeout) { - scheduler.clearTimeout(scheduledTimeout); - scheduledTimeout = null; + + function unschedule() + { + if (scheduledTimeout) + { + scheduler.clearTimeout(scheduledTimeout); + scheduledTimeout = null; } } - function reschedule(time) { + + function reschedule(time) + { unschedule(); scheduledTime = time; var delay = time - now(); if (delay < 0) delay = 0; scheduledTimeout = scheduler.setTimeout(callback, delay); } - function callback() { + + function callback() + { scheduledTimeout = null; // func may reschedule the action func(); } return { - atMost: function (ms) { - var latestTime = now() + ms; - if ((! scheduledTimeout) || scheduledTime > latestTime) { - reschedule(latestTime); - } + atMost: function(ms) + { + var latestTime = now() + ms; + if ((!scheduledTimeout) || scheduledTime > latestTime) + { + reschedule(latestTime); + } }, // atLeast(ms) will schedule the action if not scheduled yet. // In other words, "infinity" is replaced by ms, even though // it is technically larger. - atLeast: function (ms) { - var earliestTime = now()+ms; - if ((! scheduledTimeout) || scheduledTime < earliestTime) { - reschedule(earliestTime); - } + atLeast: function(ms) + { + var earliestTime = now() + ms; + if ((!scheduledTimeout) || scheduledTime < earliestTime) + { + reschedule(earliestTime); + } }, - never: function() { - unschedule(); + never: function() + { + unschedule(); } } } - function fastIncorp(n) { + function fastIncorp(n) + { // normalize but don't do any lexing or anything incorporateUserChanges(newTimeLimit(0)); } editorInfo.ace_fastIncorp = fastIncorp; - function incorpIfQuick() { + function incorpIfQuick() + { var me = incorpIfQuick; var failures = (me.failures || 0); - if (failures < 5) { + if (failures < 5) + { var isTimeUp = newTimeLimit(40); var madeChanges = incorporateUserChanges(isTimeUp); - if (isTimeUp()) { - me.failures = failures+1; + if (isTimeUp()) + { + me.failures = failures + 1; } return true; } - else { + else + { var skipCount = (me.skipCount || 0); skipCount++; - if (skipCount == 20) { - skipCount = 0; - me.failures = 0; + if (skipCount == 20) + { + skipCount = 0; + me.failures = 0; } me.skipCount = skipCount; } return false; } - var idleWorkTimer = makeIdleAction(function() { + var idleWorkTimer = makeIdleAction(function() + { //if (! top.BEFORE) top.BEFORE = []; //top.BEFORE.push(magicdom.root.dom.innerHTML); - //if (! isEditable) return; // and don't reschedule - - if (inInternationalComposition) { + if (inInternationalComposition) + { // don't do idle input incorporation during international input composition idleWorkTimer.atLeast(500); return; } - inCallStack("idleWorkTimer", function() { + inCallStack("idleWorkTimer", function() + { var isTimeUp = newTimeLimit(250); //console.time("idlework"); - var finishedImportantWork = false; var finishedWork = false; - try { - - // isTimeUp() is a soft constraint for incorporateUserChanges, - // which always renormalizes the DOM, no matter how long it takes, - // but doesn't necessarily lex and highlight it - incorporateUserChanges(isTimeUp); + try + { - if (isTimeUp()) return; + // isTimeUp() is a soft constraint for incorporateUserChanges, + // which always renormalizes the DOM, no matter how long it takes, + // but doesn't necessarily lex and highlight it + incorporateUserChanges(isTimeUp); - updateLineNumbers(); // update line numbers if any time left + if (isTimeUp()) return; - if (isTimeUp()) return; + updateLineNumbers(); // update line numbers if any time left + if (isTimeUp()) return; - var visibleRange = getVisibleCharRange(); - var docRange = [0, rep.lines.totalWidth()]; - //console.log("%o %o", docRange, visibleRange); - - finishedImportantWork = true; - finishedWork = true; + var visibleRange = getVisibleCharRange(); + var docRange = [0, rep.lines.totalWidth()]; + //console.log("%o %o", docRange, visibleRange); + finishedImportantWork = true; + finishedWork = true; } - finally { - //console.timeEnd("idlework"); - if (finishedWork) { - idleWorkTimer.atMost(1000); - } - else if (finishedImportantWork) { - // if we've finished highlighting the view area, - // more highlighting could be counter-productive, - // e.g. if the user just opened a triple-quote and will soon close it. - idleWorkTimer.atMost(500); - } - else { - var timeToWait = Math.round(isTimeUp.elapsed() / 2); - if (timeToWait < 100) timeToWait = 100; - idleWorkTimer.atMost(timeToWait); - } + finally + { + //console.timeEnd("idlework"); + if (finishedWork) + { + idleWorkTimer.atMost(1000); + } + else if (finishedImportantWork) + { + // if we've finished highlighting the view area, + // more highlighting could be counter-productive, + // e.g. if the user just opened a triple-quote and will soon close it. + idleWorkTimer.atMost(500); + } + else + { + var timeToWait = Math.round(isTimeUp.elapsed() / 2); + if (timeToWait < 100) timeToWait = 100; + idleWorkTimer.atMost(timeToWait); + } } }); //if (! top.AFTER) top.AFTER = []; //top.AFTER.push(magicdom.root.dom.innerHTML); - }); var _nextId = 1; - function uniqueId(n) { + + function uniqueId(n) + { // not actually guaranteed to be unique, e.g. if user copy-pastes // nodes with ids var nid = n.id; if (nid) return nid; - return (n.id = "magicdomid"+(_nextId++)); + return (n.id = "magicdomid" + (_nextId++)); } - function recolorLinesInRange(startChar, endChar, isTimeUp, optModFunc) { + function recolorLinesInRange(startChar, endChar, isTimeUp, optModFunc) + { if (endChar <= startChar) return; if (startChar < 0 || startChar >= rep.lines.totalWidth()) return; - var lineEntry = rep.lines.atOffset(startChar); // rounds down to line boundary + var lineEntry = rep.lines.atOffset(startChar); // rounds down to line boundary var lineStart = rep.lines.offsetOfEntry(lineEntry); var lineIndex = rep.lines.indexOfEntry(lineEntry); var selectionNeedsResetting = false; @@ -1102,18 +1416,22 @@ function OUTER(gscope) { // tokenFunc function; accesses current value of lineEntry and curDocChar, // also mutates curDocChar var curDocChar; - var tokenFunc = function(tokenText, tokenClass) { - lineEntry.domInfo.appendSpan(tokenText, tokenClass); - }; - if (optModFunc) { + var tokenFunc = function(tokenText, tokenClass) + { + lineEntry.domInfo.appendSpan(tokenText, tokenClass); + }; + if (optModFunc) + { var f = tokenFunc; - tokenFunc = function(tokenText, tokenClass) { - optModFunc(tokenText, tokenClass, f, curDocChar); - curDocChar += tokenText.length; + tokenFunc = function(tokenText, tokenClass) + { + optModFunc(tokenText, tokenClass, f, curDocChar); + curDocChar += tokenText.length; }; } - while (lineEntry && lineStart < endChar && ! isTimeUp()) { + while (lineEntry && lineStart < endChar && !isTimeUp()) + { //var timer = newTimeLimit(200); var lineEnd = lineStart + lineEntry.width; @@ -1124,20 +1442,20 @@ function OUTER(gscope) { markNodeClean(lineEntry.lineNode); - if (rep.selStart && rep.selStart[0] == lineIndex || - rep.selEnd && rep.selEnd[0] == lineIndex) { - selectionNeedsResetting = true; + if (rep.selStart && rep.selStart[0] == lineIndex || rep.selEnd && rep.selEnd[0] == lineIndex) + { + selectionNeedsResetting = true; } //if (timer()) console.dirxml(lineEntry.lineNode.dom); - if (firstLine === null) firstLine = lineIndex; lastLine = lineIndex; lineStart = lineEnd; lineEntry = rep.lines.next(lineEntry); lineIndex++; } - if (selectionNeedsResetting) { + if (selectionNeedsResetting) + { currentCallStack.selectionAffected = true; } //console.debug("Recolored line range %d-%d", firstLine, lastLine); @@ -1146,48 +1464,60 @@ function OUTER(gscope) { // like getSpansForRange, but for a line, and the func takes (text,class) // instead of (width,class); excludes the trailing '\n' from // consideration by func - function getSpansForLine(lineEntry, textAndClassFunc, lineEntryOffsetHint) { + + + function getSpansForLine(lineEntry, textAndClassFunc, lineEntryOffsetHint) + { var lineEntryOffset = lineEntryOffsetHint; - if ((typeof lineEntryOffset) != "number") { + if ((typeof lineEntryOffset) != "number") + { lineEntryOffset = rep.lines.offsetOfEntry(lineEntry); } var text = lineEntry.text; var width = lineEntry.width; // text.length+1 - - if (text.length == 0) { + if (text.length == 0) + { // allow getLineStyleFilter to set line-div styles var func = linestylefilter.getLineStyleFilter( - 0, '', textAndClassFunc, rep.apool); + 0, '', textAndClassFunc, rep.apool); func('', ''); } - else { + else + { var offsetIntoLine = 0; var filteredFunc = linestylefilter.getFilterStack(text, textAndClassFunc, browser); var lineNum = rep.lines.indexOfEntry(lineEntry); var aline = rep.alines[lineNum]; filteredFunc = linestylefilter.getLineStyleFilter( - text.length, aline, filteredFunc, rep.apool); + text.length, aline, filteredFunc, rep.apool); filteredFunc(text, ''); } } - function getCharType(charIndex) { + function getCharType(charIndex) + { return ''; } var observedChanges; - function clearObservedChanges() { - observedChanges = { cleanNodesNearChanges: {} }; + + function clearObservedChanges() + { + observedChanges = { + cleanNodesNearChanges: {} + }; } clearObservedChanges(); - function getCleanNodeByKey(key) { + function getCleanNodeByKey(key) + { var p = PROFILER("getCleanNodeByKey", false); p.extra = 0; var n = doc.getElementById(key); // copying and pasting can lead to duplicate ids - while (n && isNodeDirty(n)) { + while (n && isNodeDirty(n)) + { p.extra++; n.id = ""; n = doc.getElementById(key); @@ -1197,54 +1527,62 @@ function OUTER(gscope) { return n; } - function observeChangesAroundNode(node) { + function observeChangesAroundNode(node) + { // Around this top-level DOM node, look for changes to the document // (from how it looks in our representation) and record them in a way // that can be used to "normalize" the document (apply the changes to our // representation, and put the DOM in a canonical form). - //top.console.log("observeChangesAroundNode(%o)", node); - var cleanNode; var hasAdjacentDirtyness; - if (! isNodeDirty(node)) { + if (!isNodeDirty(node)) + { cleanNode = node; var prevSib = cleanNode.previousSibling; var nextSib = cleanNode.nextSibling; - hasAdjacentDirtyness = ((prevSib && isNodeDirty(prevSib)) - || (nextSib && isNodeDirty(nextSib))); + hasAdjacentDirtyness = ((prevSib && isNodeDirty(prevSib)) || (nextSib && isNodeDirty(nextSib))); } - else { + else + { // node is dirty, look for clean node above var upNode = node.previousSibling; - while (upNode && isNodeDirty(upNode)) { - upNode = upNode.previousSibling; - } - if (upNode) { - cleanNode = upNode; - } - else { - var downNode = node.nextSibling; - while (downNode && isNodeDirty(downNode)) { - downNode = downNode.nextSibling; - } - if (downNode) { - cleanNode = downNode; - } - } - if (! cleanNode) { - // Couldn't find any adjacent clean nodes! - // Since top and bottom of doc is dirty, the dirty area will be detected. - return; + while (upNode && isNodeDirty(upNode)) + { + upNode = upNode.previousSibling; + } + if (upNode) + { + cleanNode = upNode; + } + else + { + var downNode = node.nextSibling; + while (downNode && isNodeDirty(downNode)) + { + downNode = downNode.nextSibling; + } + if (downNode) + { + cleanNode = downNode; + } + } + if (!cleanNode) + { + // Couldn't find any adjacent clean nodes! + // Since top and bottom of doc is dirty, the dirty area will be detected. + return; } hasAdjacentDirtyness = true; } - if (hasAdjacentDirtyness) { + if (hasAdjacentDirtyness) + { // previous or next line is dirty - observedChanges.cleanNodesNearChanges['$'+uniqueId(cleanNode)] = true; + observedChanges.cleanNodesNearChanges['$' + uniqueId(cleanNode)] = true; } - else { + else + { // next and prev lines are clean (if they exist) var lineKey = uniqueId(cleanNode); var prevSib = cleanNode.previousSibling; @@ -1255,62 +1593,77 @@ function OUTER(gscope) { var repNextEntry = rep.lines.next(rep.lines.atKey(lineKey)); var repPrevKey = ((repPrevEntry && repPrevEntry.key) || null); var repNextKey = ((repNextEntry && repNextEntry.key) || null); - if (actualPrevKey != repPrevKey || actualNextKey != repNextKey) { - observedChanges.cleanNodesNearChanges['$'+uniqueId(cleanNode)] = true; + if (actualPrevKey != repPrevKey || actualNextKey != repNextKey) + { + observedChanges.cleanNodesNearChanges['$' + uniqueId(cleanNode)] = true; } } } - function observeChangesAroundSelection() { + function observeChangesAroundSelection() + { if (currentCallStack.observedSelection) return; currentCallStack.observedSelection = true; var p = PROFILER("getSelection", false); var selection = getSelection(); p.end(); - if (selection) { - function topLevel(n) { - if ((!n) || n == root) return null; - while (n.parentNode != root) { - n = n.parentNode; - } - return n; + if (selection) + { + function topLevel(n) + { + if ((!n) || n == root) return null; + while (n.parentNode != root) + { + n = n.parentNode; + } + return n; } var node1 = topLevel(selection.startPoint.node); var node2 = topLevel(selection.endPoint.node); if (node1) observeChangesAroundNode(node1); - if (node2 && node1 != node2) { - observeChangesAroundNode(node2); + if (node2 && node1 != node2) + { + observeChangesAroundNode(node2); } } } - function observeSuspiciousNodes() { + function observeSuspiciousNodes() + { // inspired by Firefox bug #473255, where pasting formatted text // causes the cursor to jump away, making the new HTML never found. - if (root.getElementsByTagName) { + if (root.getElementsByTagName) + { var nds = root.getElementsByTagName("style"); - for(var i=0;i<nds.length;i++) { - var n = nds[i]; - while (n.parentNode && n.parentNode != root) { - n = n.parentNode; - } - if (n.parentNode == root) { - observeChangesAroundNode(n); - } + for (var i = 0; i < nds.length; i++) + { + var n = nds[i]; + while (n.parentNode && n.parentNode != root) + { + n = n.parentNode; + } + if (n.parentNode == root) + { + observeChangesAroundNode(n); + } } } } - function incorporateUserChanges(isTimeUp) { + function incorporateUserChanges(isTimeUp) + { if (currentCallStack.domClean) return false; inInternationalComposition = false; // if we need the document normalized, so be it - currentCallStack.isUserChange = true; - isTimeUp = (isTimeUp || function() { return false; }); + isTimeUp = (isTimeUp || + function() + { + return false; + }); if (DEBUG && window.DONT_INCORP || window.DEBUG_DONT_INCORP) return false; @@ -1319,10 +1672,9 @@ function OUTER(gscope) { //if (doc.body.innerHTML.indexOf("AppJet") >= 0) //dmesg(htmlPrettyEscape(doc.body.innerHTML)); //if (top.RECORD) top.RECORD.push(doc.body.innerHTML); - // returns true if dom changes were made - - if (! root.firstChild) { + if (!root.firstChild) + { root.innerHTML = "<div><!-- --></div>"; } @@ -1332,25 +1684,28 @@ function OUTER(gscope) { p.mark("dirty"); var dirtyRanges = getDirtyRanges(); //console.log("dirtyRanges: "+toSource(dirtyRanges)); - var dirtyRangesCheckOut = true; var j = 0; - var a,b; - while (j < dirtyRanges.length) { + var a, b; + while (j < dirtyRanges.length) + { a = dirtyRanges[j][0]; b = dirtyRanges[j][1]; - if (! ((a == 0 || getCleanNodeByKey(rep.lines.atIndex(a-1).key)) && - (b == rep.lines.length() || getCleanNodeByKey(rep.lines.atIndex(b).key)))) { + if (!((a == 0 || getCleanNodeByKey(rep.lines.atIndex(a - 1).key)) && (b == rep.lines.length() || getCleanNodeByKey(rep.lines.atIndex(b).key)))) + { dirtyRangesCheckOut = false; break; } j++; } - if (! dirtyRangesCheckOut) { + if (!dirtyRangesCheckOut) + { var numBodyNodes = root.childNodes.length; - for(var k=0;k<numBodyNodes;k++) { + for (var k = 0; k < numBodyNodes; k++) + { var bodyNode = root.childNodes.item(k); - if ((bodyNode.tagName) && ((! bodyNode.id) || (! rep.lines.containsKey(bodyNode.id)))) { + if ((bodyNode.tagName) && ((!bodyNode.id) || (!rep.lines.containsKey(bodyNode.id)))) + { observeChangesAroundNode(bodyNode); } } @@ -1365,7 +1720,6 @@ function OUTER(gscope) { //console.log(magicdom.root.dom.innerHTML); //console.log("got selection: %o", selection); var selStart, selEnd; // each one, if truthy, has [line,char] needed to set selection - var i = 0; var splicesToDo = []; var netNumLinesChangeSoFar = 0; @@ -1373,48 +1727,52 @@ function OUTER(gscope) { p.mark("ranges"); p.literal(dirtyRanges.length, "numdirt"); var domInsertsNeeded = []; // each entry is [nodeToInsertAfter, [info1, info2, ...]] - while (i < dirtyRanges.length) { + while (i < dirtyRanges.length) + { var range = dirtyRanges[i]; a = range[0]; b = range[1]; - var firstDirtyNode = (((a == 0) && root.firstChild) || - getCleanNodeByKey(rep.lines.atIndex(a-1).key).nextSibling); + var firstDirtyNode = (((a == 0) && root.firstChild) || getCleanNodeByKey(rep.lines.atIndex(a - 1).key).nextSibling); firstDirtyNode = (firstDirtyNode && isNodeDirty(firstDirtyNode) && firstDirtyNode); - var lastDirtyNode = (((b == rep.lines.length()) && root.lastChild) || - getCleanNodeByKey(rep.lines.atIndex(b).key).previousSibling); + var lastDirtyNode = (((b == rep.lines.length()) && root.lastChild) || getCleanNodeByKey(rep.lines.atIndex(b).key).previousSibling); lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode); - if (firstDirtyNode && lastDirtyNode) { - var cc = makeContentCollector(isStyled, browser, rep.apool, null, - className2Author); - cc.notifySelection(selection); - var dirtyNodes = []; - for(var n = firstDirtyNode; n && ! (n.previousSibling && - n.previousSibling == lastDirtyNode); - n = n.nextSibling) { - if (browser.msie) { + if (firstDirtyNode && lastDirtyNode) + { + var cc = makeContentCollector(isStyled, browser, rep.apool, null, className2Author); + cc.notifySelection(selection); + var dirtyNodes = []; + for (var n = firstDirtyNode; n && !(n.previousSibling && n.previousSibling == lastDirtyNode); + n = n.nextSibling) + { + if (browser.msie) + { // try to undo IE's pesky and overzealous linkification - try { n.createTextRange().execCommand("unlink", false, null); } - catch (e) {} + try + { + n.createTextRange().execCommand("unlink", false, null); + } + catch (e) + {} } - cc.collectContent(n); - dirtyNodes.push(n); - } - cc.notifyNextNode(lastDirtyNode.nextSibling); - var lines = cc.getLines(); - if ((lines.length <= 1 || lines[lines.length-1] !== "") - && lastDirtyNode.nextSibling) { - // dirty region doesn't currently end a line, even taking the following node - // (or lack of node) into account, so include the following clean node. - // It could be SPAN or a DIV; basically this is any case where the contentCollector - // decides it isn't done. - // Note that this clean node might need to be there for the next dirty range. - //console.log("inclusive of "+lastDirtyNode.next().dom.tagName); - b++; - var cleanLine = lastDirtyNode.nextSibling; - cc.collectContent(cleanLine); - toDeleteAtEnd.push(cleanLine); - cc.notifyNextNode(cleanLine.nextSibling); - } + cc.collectContent(n); + dirtyNodes.push(n); + } + cc.notifyNextNode(lastDirtyNode.nextSibling); + var lines = cc.getLines(); + if ((lines.length <= 1 || lines[lines.length - 1] !== "") && lastDirtyNode.nextSibling) + { + // dirty region doesn't currently end a line, even taking the following node + // (or lack of node) into account, so include the following clean node. + // It could be SPAN or a DIV; basically this is any case where the contentCollector + // decides it isn't done. + // Note that this clean node might need to be there for the next dirty range. + //console.log("inclusive of "+lastDirtyNode.next().dom.tagName); + b++; + var cleanLine = lastDirtyNode.nextSibling; + cc.collectContent(cleanLine); + toDeleteAtEnd.push(cleanLine); + cc.notifyNextNode(cleanLine.nextSibling); + } var ccData = cc.finish(); var ss = ccData.selStart; @@ -1423,36 +1781,41 @@ function OUTER(gscope) { var lineAttribs = ccData.lineAttribs; var linesWrapped = ccData.linesWrapped; - if (linesWrapped > 0) { - doAlert("Editor warning: "+linesWrapped+" long line"+ - (linesWrapped == 1 ? " was" : "s were")+" hard-wrapped into "+ - ccData.numLinesAfter - +" lines."); - } + if (linesWrapped > 0) + { + doAlert("Editor warning: " + linesWrapped + " long line" + (linesWrapped == 1 ? " was" : "s were") + " hard-wrapped into " + ccData.numLinesAfter + " lines."); + } - if (ss[0] >= 0) selStart = [ss[0]+a+netNumLinesChangeSoFar, ss[1]]; - if (se[0] >= 0) selEnd = [se[0]+a+netNumLinesChangeSoFar, se[1]]; + if (ss[0] >= 0) selStart = [ss[0] + a + netNumLinesChangeSoFar, ss[1]]; + if (se[0] >= 0) selEnd = [se[0] + a + netNumLinesChangeSoFar, se[1]]; - var entries = []; - var nodeToAddAfter = lastDirtyNode; - var lineNodeInfos = new Array(lines.length); - for(var k=0;k<lines.length;k++) { + var entries = []; + var nodeToAddAfter = lastDirtyNode; + var lineNodeInfos = new Array(lines.length); + for (var k = 0; k < lines.length; k++) + { var lineString = lines[k]; - var newEntry = createDomLineEntry(lineString); - entries.push(newEntry); - lineNodeInfos[k] = newEntry.domInfo; - } - //var fragment = magicdom.wrapDom(document.createDocumentFragment()); - domInsertsNeeded.push([nodeToAddAfter, lineNodeInfos]); - forEach(dirtyNodes, function (n) { toDeleteAtEnd.push(n); }); - var spliceHints = {}; - if (selStart) spliceHints.selStart = selStart; - if (selEnd) spliceHints.selEnd = selEnd; - splicesToDo.push([a+netNumLinesChangeSoFar, b-a, entries, lineAttribs, spliceHints]); - netNumLinesChangeSoFar += (lines.length - (b-a)); - } - else if (b > a) { - splicesToDo.push([a+netNumLinesChangeSoFar, b-a, [], []]); + var newEntry = createDomLineEntry(lineString); + entries.push(newEntry); + lineNodeInfos[k] = newEntry.domInfo; + } + //var fragment = magicdom.wrapDom(document.createDocumentFragment()); + domInsertsNeeded.push([nodeToAddAfter, lineNodeInfos]); + forEach(dirtyNodes, function(n) + { + toDeleteAtEnd.push(n); + }); + var spliceHints = {}; + if (selStart) spliceHints.selStart = selStart; + if (selEnd) spliceHints.selEnd = selEnd; + splicesToDo.push([a + netNumLinesChangeSoFar, b - a, entries, lineAttribs, spliceHints]); + netNumLinesChangeSoFar += (lines.length - (b - a)); + } + else if (b > a) + { + splicesToDo.push([a + netNumLinesChangeSoFar, b - a, [], + [] + ]); } i++; } @@ -1461,25 +1824,26 @@ function OUTER(gscope) { // update the representation p.mark("splice"); - forEach(splicesToDo, function (splice) { + forEach(splicesToDo, function(splice) + { doIncorpLineSplice(splice[0], splice[1], splice[2], splice[3], splice[4]); }); //p.mark("relex"); //rep.lexer.lexCharRange(getVisibleCharRange(), function() { return false; }); //var isTimeUp = newTimeLimit(100); - // do DOM inserts p.mark("insert"); - forEach(domInsertsNeeded, function (ins) { + forEach(domInsertsNeeded, function(ins) + { insertDomLines(ins[0], ins[1], isTimeUp); }); p.mark("del"); // delete old dom nodes - forEach(toDeleteAtEnd, function (n) { + forEach(toDeleteAtEnd, function(n) + { //var id = n.uniqueId(); - // parent of n may not be "root" in IE due to non-tree-shaped DOM (wtf) n.parentNode.removeChild(n); @@ -1490,23 +1854,27 @@ function OUTER(gscope) { p.mark("findsel"); // if the nodes that define the selection weren't encountered during // content collection, figure out where those nodes are now. - if (selection && !selStart) { + if (selection && !selStart) + { //if (domChanges) dmesg("selection not collected"); selStart = getLineAndCharForPoint(selection.startPoint); } - if (selection && !selEnd) { + if (selection && !selEnd) + { selEnd = getLineAndCharForPoint(selection.endPoint); } // selection from content collection can, in various ways, extend past final // BR in firefox DOM, so cap the line var numLines = rep.lines.length(); - if (selStart && selStart[0] >= numLines) { - selStart[0] = numLines-1; + if (selStart && selStart[0] >= numLines) + { + selStart[0] = numLines - 1; selStart[1] = rep.lines.atIndex(selStart[0]).text.length; } - if (selEnd && selEnd[0] >= numLines) { - selEnd[0] = numLines-1; + if (selEnd && selEnd[0] >= numLines) + { + selEnd[0] = numLines - 1; selEnd[1] = rep.lines.atIndex(selEnd[0]).text.length; } @@ -1515,11 +1883,11 @@ function OUTER(gscope) { // NOTE: IE loses the selection when you click stuff in e.g. the // editbar, so removing the selection when it's lost is not a good // idea. - if (selection) - repSelectionChange(selStart, selEnd, selection && selection.focusAtStart); + if (selection) repSelectionChange(selStart, selEnd, selection && selection.focusAtStart); // update browser selection p.mark("browsel"); - if (selection && (domChanges || isCaret())) { + if (selection && (domChanges || isCaret())) + { // if no DOM changes (not this case), want to treat range selection delicately, // e.g. in IE not lose which end of the selection is the focus/anchor; // on the other hand, we may have just noticed a press of PageUp/PageDown @@ -1537,72 +1905,95 @@ function OUTER(gscope) { return domChanges; } - function htmlForRemovedChild(n) { + function htmlForRemovedChild(n) + { var div = doc.createElement("DIV"); div.appendChild(n); return div.innerHTML; } - var STYLE_ATTRIBS = {bold: true, italic: true, underline: true, - strikethrough: true, list: true}; - var OTHER_INCORPED_ATTRIBS = {insertorder: true, author: true}; + var STYLE_ATTRIBS = { + bold: true, + italic: true, + underline: true, + strikethrough: true, + list: true + }; + var OTHER_INCORPED_ATTRIBS = { + insertorder: true, + author: true + }; - function isStyleAttribute(aname) { - return !! STYLE_ATTRIBS[aname]; + function isStyleAttribute(aname) + { + return !!STYLE_ATTRIBS[aname]; } - function isIncorpedAttribute(aname) { - return (!! STYLE_ATTRIBS[aname]) || (!! OTHER_INCORPED_ATTRIBS[aname]); + + function isIncorpedAttribute(aname) + { + return ( !! STYLE_ATTRIBS[aname]) || ( !! OTHER_INCORPED_ATTRIBS[aname]); } - function insertDomLines(nodeToAddAfter, infoStructs, isTimeUp) { - isTimeUp = (isTimeUp || function() { return false; }); + function insertDomLines(nodeToAddAfter, infoStructs, isTimeUp) + { + isTimeUp = (isTimeUp || + function() + { + return false; + }); var lastEntry; var lineStartOffset; if (infoStructs.length < 1) return; var startEntry = rep.lines.atKey(uniqueId(infoStructs[0].node)); - var endEntry = rep.lines.atKey(uniqueId(infoStructs[infoStructs.length-1].node)); + var endEntry = rep.lines.atKey(uniqueId(infoStructs[infoStructs.length - 1].node)); var charStart = rep.lines.offsetOfEntry(startEntry); var charEnd = rep.lines.offsetOfEntry(endEntry) + endEntry.width; //rep.lexer.lexCharRange([charStart, charEnd], isTimeUp); - - forEach(infoStructs, function (info) { + forEach(infoStructs, function(info) + { var p2 = PROFILER("insertLine", false); var node = info.node; var key = uniqueId(node); var entry; p2.mark("findEntry"); - if (lastEntry) { - // optimization to avoid recalculation - var next = rep.lines.next(lastEntry); - if (next && next.key == key) { - entry = next; - lineStartOffset += lastEntry.width; - } - } - if (! entry) { - p2.literal(1, "nonopt"); - entry = rep.lines.atKey(key); - lineStartOffset = rep.lines.offsetOfKey(key); + if (lastEntry) + { + // optimization to avoid recalculation + var next = rep.lines.next(lastEntry); + if (next && next.key == key) + { + entry = next; + lineStartOffset += lastEntry.width; + } + } + if (!entry) + { + p2.literal(1, "nonopt"); + entry = rep.lines.atKey(key); + lineStartOffset = rep.lines.offsetOfKey(key); } else p2.literal(0, "nonopt"); lastEntry = entry; p2.mark("spans"); - getSpansForLine(entry, function (tokenText, tokenClass) { - info.appendSpan(tokenText, tokenClass); + getSpansForLine(entry, function(tokenText, tokenClass) + { + info.appendSpan(tokenText, tokenClass); }, lineStartOffset, isTimeUp()); //else if (entry.text.length > 0) { - //info.appendSpan(entry.text, 'dirty'); + //info.appendSpan(entry.text, 'dirty'); //} p2.mark("addLine"); info.prepareForAdd(); entry.lineMarker = info.lineMarker; - if (! nodeToAddAfter) { - root.insertBefore(node, root.firstChild); + if (!nodeToAddAfter) + { + root.insertBefore(node, root.firstChild); } - else { - root.insertBefore(node, nodeToAddAfter.nextSibling); + else + { + root.insertBefore(node, nodeToAddAfter.nextSibling); } nodeToAddAfter = node; info.notifyAdded(); @@ -1612,22 +2003,35 @@ function OUTER(gscope) { }); } - function isCaret() { - return (rep.selStart && rep.selEnd && rep.selStart[0] == rep.selEnd[0] && - rep.selStart[1] == rep.selEnd[1]); + function isCaret() + { + return (rep.selStart && rep.selEnd && rep.selStart[0] == rep.selEnd[0] && rep.selStart[1] == rep.selEnd[1]); } editorInfo.ace_isCaret = isCaret; // prereq: isCaret() - function caretLine() { return rep.selStart[0]; } - function caretColumn() { return rep.selStart[1]; } - function caretDocChar() { + + + function caretLine() + { + return rep.selStart[0]; + } + + function caretColumn() + { + return rep.selStart[1]; + } + + function caretDocChar() + { return rep.lines.offsetOfIndex(caretLine()) + caretColumn(); } - function handleReturnIndentation() { + function handleReturnIndentation() + { // on return, indent to level of previous line - if (isCaret() && caretColumn() == 0 && caretLine() > 0) { + if (isCaret() && caretColumn() == 0 && caretLine() > 0) + { var lineNum = caretLine(); var thisLine = rep.lines.atIndex(lineNum); var prevLine = rep.lines.prev(thisLine); @@ -1635,15 +2039,18 @@ function OUTER(gscope) { var theIndent = /^ *(?:)/.exec(prevLineText)[0]; if (/[\[\(\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB; var cs = Changeset.builder(rep.lines.totalWidth()).keep( - rep.lines.offsetOfIndex(lineNum), lineNum).insert( - theIndent, [['author',thisAuthor]], rep.apool).toString(); + rep.lines.offsetOfIndex(lineNum), lineNum).insert( + theIndent, [ + ['author', thisAuthor] + ], rep.apool).toString(); performDocumentApplyChangeset(cs); performSelectionChange([lineNum, theIndent.length], [lineNum, theIndent.length]); } } - function setupMozillaCaretHack(lineNum) { + function setupMozillaCaretHack(lineNum) + { // This is really ugly, but by god, it works! // Fixes annoying Firefox caret artifact (observed in 2.0.0.12 // and unfixed in Firefox 2 as of now) where mutating the DOM @@ -1657,119 +2064,162 @@ function OUTER(gscope) { var lineNode = rep.lines.atIndex(lineNum).lineNode; var fc = lineNode.firstChild; - while (isBlockElement(fc) && fc.firstChild) { + while (isBlockElement(fc) && fc.firstChild) + { fc = fc.firstChild; } var textNode; - if (isNodeText(fc)) { - fc.nodeValue = " "+fc.nodeValue; + if (isNodeText(fc)) + { + fc.nodeValue = " " + fc.nodeValue; textNode = fc; } - else { + else + { textNode = doc.createTextNode(" "); fc.parentNode.insertBefore(textNode, fc); } markNodeClean(lineNode); - return { unhack: function() { - if (textNode.nodeValue == " ") { - textNode.parentNode.removeChild(textNode); - } - else { - textNode.nodeValue = textNode.nodeValue.substring(1); + return { + unhack: function() + { + if (textNode.nodeValue == " ") + { + textNode.parentNode.removeChild(textNode); + } + else + { + textNode.nodeValue = textNode.nodeValue.substring(1); + } + markNodeClean(lineNode); } - markNodeClean(lineNode); - } }; + }; } - function getPointForLineAndChar(lineAndChar) { + function getPointForLineAndChar(lineAndChar) + { var line = lineAndChar[0]; var charsLeft = lineAndChar[1]; //console.log("line: %d, key: %s, node: %o", line, rep.lines.atIndex(line).key, //getCleanNodeByKey(rep.lines.atIndex(line).key)); var lineEntry = rep.lines.atIndex(line); charsLeft -= lineEntry.lineMarker; - if (charsLeft < 0) { + if (charsLeft < 0) + { charsLeft = 0; } var lineNode = lineEntry.lineNode; var n = lineNode; var after = false; - if (charsLeft == 0) { + if (charsLeft == 0) + { var index = 0; - if (browser.msie && line == (rep.lines.length()-1) && lineNode.childNodes.length == 0) { - // best to stay at end of last empty div in IE - index = 1; - } - return {node: lineNode, index:index, maxIndex:1}; - } - while (!(n == lineNode && after)) { - if (after) { - if (n.nextSibling) { - n = n.nextSibling; - after = false; - } - else n = n.parentNode; - } - else { - if (isNodeText(n)) { - var len = n.nodeValue.length; - if (charsLeft <= len) { - return {node: n, index:charsLeft, maxIndex:len}; - } - charsLeft -= len; - after = true; - } - else { - if (n.firstChild) n = n.firstChild; - else after = true; - } - } - } - return {node: lineNode, index:1, maxIndex:1}; - } - - function nodeText(n) { + if (browser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length == 0) + { + // best to stay at end of last empty div in IE + index = 1; + } + return { + node: lineNode, + index: index, + maxIndex: 1 + }; + } + while (!(n == lineNode && after)) + { + if (after) + { + if (n.nextSibling) + { + n = n.nextSibling; + after = false; + } + else n = n.parentNode; + } + else + { + if (isNodeText(n)) + { + var len = n.nodeValue.length; + if (charsLeft <= len) + { + return { + node: n, + index: charsLeft, + maxIndex: len + }; + } + charsLeft -= len; + after = true; + } + else + { + if (n.firstChild) n = n.firstChild; + else after = true; + } + } + } + return { + node: lineNode, + index: 1, + maxIndex: 1 + }; + } + + function nodeText(n) + { return n.innerText || n.textContent || n.nodeValue || ''; } - function getLineAndCharForPoint(point) { + function getLineAndCharForPoint(point) + { // Turn DOM node selection into [line,char] selection. // This method has to work when the DOM is not pristine, // assuming the point is not in a dirty node. - if (point.node == root) { - if (point.index == 0) { - return [0, 0]; + if (point.node == root) + { + if (point.index == 0) + { + return [0, 0]; } - else { - var N = rep.lines.length(); - var ln = rep.lines.atIndex(N-1); - return [N-1, ln.text.length]; + else + { + var N = rep.lines.length(); + var ln = rep.lines.atIndex(N - 1); + return [N - 1, ln.text.length]; } } - else { + else + { var n = point.node; var col = 0; // if this part fails, it probably means the selection node // was dirty, and we didn't see it when collecting dirty nodes. - if (isNodeText(n)) { - col = point.index; + if (isNodeText(n)) + { + col = point.index; } - else if (point.index > 0) { - col = nodeText(n).length; + else if (point.index > 0) + { + col = nodeText(n).length; } var parNode, prevSib; - while ((parNode = n.parentNode) != root) { - if ((prevSib = n.previousSibling)) { - n = prevSib; - col += nodeText(n).length; - } - else { - n = parNode; - } + while ((parNode = n.parentNode) != root) + { + if ((prevSib = n.previousSibling)) + { + n = prevSib; + col += nodeText(n).length; + } + else + { + n = parNode; + } } if (n.id == "") console.debug("BAD"); - if (n.firstChild && isBlockElement(n.firstChild)) { + if (n.firstChild && isBlockElement(n.firstChild)) + { col += 1; // lineMarker } var lineEntry = rep.lines.atKey(n.id); @@ -1778,40 +2228,59 @@ function OUTER(gscope) { } } - function createDomLineEntry(lineString) { + function createDomLineEntry(lineString) + { var info = doCreateDomLine(lineString.length > 0); var newNode = info.node; - return {key: uniqueId(newNode), text: lineString, lineNode: newNode, - domInfo: info, lineMarker: 0}; + return { + key: uniqueId(newNode), + text: lineString, + lineNode: newNode, + domInfo: info, + lineMarker: 0 + }; } - function canApplyChangesetToDocument(changes) { + function canApplyChangesetToDocument(changes) + { return Changeset.oldLen(changes) == rep.alltext.length; } - function performDocumentApplyChangeset(changes, insertsAfterSelection) { + function performDocumentApplyChangeset(changes, insertsAfterSelection) + { doRepApplyChangeset(changes, insertsAfterSelection); var requiredSelectionSetting = null; - if (rep.selStart && rep.selEnd) { + if (rep.selStart && rep.selEnd) + { var selStartChar = rep.lines.offsetOfIndex(rep.selStart[0]) + rep.selStart[1]; var selEndChar = rep.lines.offsetOfIndex(rep.selEnd[0]) + rep.selEnd[1]; - var result = Changeset.characterRangeFollow(changes, selStartChar, selEndChar, - insertsAfterSelection); + var result = Changeset.characterRangeFollow(changes, selStartChar, selEndChar, insertsAfterSelection); requiredSelectionSetting = [result[0], result[1], rep.selFocusAtStart]; } var linesMutatee = { - splice: function(start, numRemoved, newLinesVA) { - domAndRepSplice(start, numRemoved, - map(Array.prototype.slice.call(arguments, 2), - function(s) { return s.slice(0,-1); }), - null); + splice: function(start, numRemoved, newLinesVA) + { + domAndRepSplice(start, numRemoved, map(Array.prototype.slice.call(arguments, 2), function(s) + { + return s.slice(0, -1); + }), null); + }, + get: function(i) + { + return rep.lines.atIndex(i).text + '\n'; + }, + length: function() + { + return rep.lines.length(); }, - get: function(i) { return rep.lines.atIndex(i).text+'\n'; }, - length: function() { return rep.lines.length(); }, - slice_notused: function(start, end) { - return map(rep.lines.slice(start, end), function(e) { return e.text+'\n'; }); + slice_notused: function(start, end) + { + return map(rep.lines.slice(start, end), function(e) + { + return e.text + '\n'; + }); } }; @@ -1819,24 +2288,26 @@ function OUTER(gscope) { checkALines(); - if (requiredSelectionSetting) { - performSelectionChange(lineAndColumnFromChar(requiredSelectionSetting[0]), - lineAndColumnFromChar(requiredSelectionSetting[1]), - requiredSelectionSetting[2]); + if (requiredSelectionSetting) + { + performSelectionChange(lineAndColumnFromChar(requiredSelectionSetting[0]), lineAndColumnFromChar(requiredSelectionSetting[1]), requiredSelectionSetting[2]); } - function domAndRepSplice(startLine, deleteCount, newLineStrings, isTimeUp) { + function domAndRepSplice(startLine, deleteCount, newLineStrings, isTimeUp) + { // dgreensp 3/2009: the spliced lines may be in the middle of a dirty region, // so if no explicit time limit, don't spend a lot of time highlighting isTimeUp = (isTimeUp || newTimeLimit(50)); var keysToDelete = []; - if (deleteCount > 0) { - var entryToDelete = rep.lines.atIndex(startLine); - for(var i=0;i<deleteCount;i++) { - keysToDelete.push(entryToDelete.key); - entryToDelete = rep.lines.next(entryToDelete); - } + if (deleteCount > 0) + { + var entryToDelete = rep.lines.atIndex(startLine); + for (var i = 0; i < deleteCount; i++) + { + keysToDelete.push(entryToDelete.key); + entryToDelete = rep.lines.next(entryToDelete); + } } var lineEntries = map(newLineStrings, createDomLineEntry); @@ -1844,169 +2315,199 @@ function OUTER(gscope) { doRepLineSplice(startLine, deleteCount, lineEntries); var nodeToAddAfter; - if (startLine > 0) { - nodeToAddAfter = getCleanNodeByKey(rep.lines.atIndex(startLine-1).key); + if (startLine > 0) + { + nodeToAddAfter = getCleanNodeByKey(rep.lines.atIndex(startLine - 1).key); } else nodeToAddAfter = null; - insertDomLines(nodeToAddAfter, map(lineEntries, function (entry) { return entry.domInfo; }), - isTimeUp); + insertDomLines(nodeToAddAfter, map(lineEntries, function(entry) + { + return entry.domInfo; + }), isTimeUp); - forEach(keysToDelete, function (k) { - var n = doc.getElementById(k); - n.parentNode.removeChild(n); + forEach(keysToDelete, function(k) + { + var n = doc.getElementById(k); + n.parentNode.removeChild(n); }); - if ((rep.selStart && rep.selStart[0] >= startLine && rep.selStart[0] <= startLine+deleteCount) || - (rep.selEnd && rep.selEnd[0] >= startLine && rep.selEnd[0] <= startLine+deleteCount)) { - currentCallStack.selectionAffected = true; + if ((rep.selStart && rep.selStart[0] >= startLine && rep.selStart[0] <= startLine + deleteCount) || (rep.selEnd && rep.selEnd[0] >= startLine && rep.selEnd[0] <= startLine + deleteCount)) + { + currentCallStack.selectionAffected = true; } } } - function checkChangesetLineInformationAgainstRep(changes) { + function checkChangesetLineInformationAgainstRep(changes) + { return true; // disable for speed var opIter = Changeset.opIterator(Changeset.unpack(changes).ops); var curOffset = 0; var curLine = 0; var curCol = 0; - while (opIter.hasNext()) { + while (opIter.hasNext()) + { var o = opIter.next(); - if (o.opcode == '-' || o.opcode == '=') { - curOffset += o.chars; - if (o.lines) { - curLine += o.lines; - curCol = 0; - } - else { - curCol += o.chars; - } + if (o.opcode == '-' || o.opcode == '=') + { + curOffset += o.chars; + if (o.lines) + { + curLine += o.lines; + curCol = 0; + } + else + { + curCol += o.chars; + } } var calcLine = rep.lines.indexOfOffset(curOffset); var calcLineStart = rep.lines.offsetOfIndex(calcLine); var calcCol = curOffset - calcLineStart; - if (calcCol != curCol || calcLine != curLine) { - return false; + if (calcCol != curCol || calcLine != curLine) + { + return false; } } return true; } - function doRepApplyChangeset(changes, insertsAfterSelection) { + function doRepApplyChangeset(changes, insertsAfterSelection) + { Changeset.checkRep(changes); - if (Changeset.oldLen(changes) != rep.alltext.length) - throw new Error("doRepApplyChangeset length mismatch: "+ - Changeset.oldLen(changes)+"/"+rep.alltext.length); + if (Changeset.oldLen(changes) != rep.alltext.length) throw new Error("doRepApplyChangeset length mismatch: " + Changeset.oldLen(changes) + "/" + rep.alltext.length); - if (! checkChangesetLineInformationAgainstRep(changes)) { + if (!checkChangesetLineInformationAgainstRep(changes)) + { throw new Error("doRepApplyChangeset line break mismatch"); } - (function doRecordUndoInformation(changes) { + (function doRecordUndoInformation(changes) + { var editEvent = currentCallStack.editEvent; - if (editEvent.eventType == "nonundoable") { - if (! editEvent.changeset) { - editEvent.changeset = changes; - } - else { - editEvent.changeset = Changeset.compose(editEvent.changeset, changes, - rep.apool); - } - } - else { - var inverseChangeset = Changeset.inverse(changes, {get: function(i) { - return rep.lines.atIndex(i).text+'\n'; - }, length: function() { return rep.lines.length(); }}, - rep.alines, rep.apool); - - if (! editEvent.backset) { - editEvent.backset = inverseChangeset; - } - else { - editEvent.backset = Changeset.compose(inverseChangeset, - editEvent.backset, rep.apool); - } + if (editEvent.eventType == "nonundoable") + { + if (!editEvent.changeset) + { + editEvent.changeset = changes; + } + else + { + editEvent.changeset = Changeset.compose(editEvent.changeset, changes, rep.apool); + } + } + else + { + var inverseChangeset = Changeset.inverse(changes, { + get: function(i) + { + return rep.lines.atIndex(i).text + '\n'; + }, + length: function() + { + return rep.lines.length(); + } + }, rep.alines, rep.apool); + + if (!editEvent.backset) + { + editEvent.backset = inverseChangeset; + } + else + { + editEvent.backset = Changeset.compose(inverseChangeset, editEvent.backset, rep.apool); + } } })(changes); //rep.alltext = Changeset.applyToText(changes, rep.alltext); Changeset.mutateAttributionLines(changes, rep.alines, rep.apool); - if (changesetTracker.isTracking()) { + if (changesetTracker.isTracking()) + { changesetTracker.composeUserChangeset(changes); } } - function lineAndColumnFromChar(x) { + function lineAndColumnFromChar(x) + { var lineEntry = rep.lines.atOffset(x); var lineStart = rep.lines.offsetOfEntry(lineEntry); var lineNum = rep.lines.indexOfEntry(lineEntry); return [lineNum, x - lineStart]; } - function performDocumentReplaceCharRange(startChar, endChar, newText) { - if (startChar == endChar && newText.length == 0) { + function performDocumentReplaceCharRange(startChar, endChar, newText) + { + if (startChar == endChar && newText.length == 0) + { return; } // Requires that the replacement preserve the property that the // internal document text ends in a newline. Given this, we // rewrite the splice so that it doesn't touch the very last // char of the document. - if (endChar == rep.alltext.length) { - if (startChar == endChar) { - // an insert at end - startChar--; - endChar--; - newText = '\n'+newText.substring(0, newText.length-1); + if (endChar == rep.alltext.length) + { + if (startChar == endChar) + { + // an insert at end + startChar--; + endChar--; + newText = '\n' + newText.substring(0, newText.length - 1); } - else if (newText.length == 0) { - // a delete at end - startChar--; - endChar--; + else if (newText.length == 0) + { + // a delete at end + startChar--; + endChar--; } - else { - // a replace at end - endChar--; - newText = newText.substring(0, newText.length-1); + else + { + // a replace at end + endChar--; + newText = newText.substring(0, newText.length - 1); } } - performDocumentReplaceRange(lineAndColumnFromChar(startChar), - lineAndColumnFromChar(endChar), - newText); + performDocumentReplaceRange(lineAndColumnFromChar(startChar), lineAndColumnFromChar(endChar), newText); } - function performDocumentReplaceRange(start, end, newText) { + function performDocumentReplaceRange(start, end, newText) + { if (start == undefined) start = rep.selStart; if (end == undefined) end = rep.selEnd; //dmesg(String([start.toSource(),end.toSource(),newText.toSource()])); - // start[0]: <--- start[1] --->CCCCCCCCCCC\n // CCCCCCCCCCCCCCCCCCCC\n // CCCC\n // end[0]: <CCC end[1] CCC>-------\n - var builder = Changeset.builder(rep.lines.totalWidth()); buildKeepToStartOfRange(builder, start); buildRemoveRange(builder, start, end); - builder.insert(newText, [['author',thisAuthor]], rep.apool); + builder.insert(newText, [ + ['author', thisAuthor] + ], rep.apool); var cs = builder.toString(); performDocumentApplyChangeset(cs); } - function performDocumentApplyAttributesToCharRange(start, end, attribs) { - if (end >= rep.alltext.length) { - end = rep.alltext.length-1; + function performDocumentApplyAttributesToCharRange(start, end, attribs) + { + if (end >= rep.alltext.length) + { + end = rep.alltext.length - 1; } - performDocumentApplyAttributesToRange(lineAndColumnFromChar(start), - lineAndColumnFromChar(end), attribs); + performDocumentApplyAttributesToRange(lineAndColumnFromChar(start), lineAndColumnFromChar(end), attribs); } editorInfo.ace_performDocumentApplyAttributesToCharRange = performDocumentApplyAttributesToCharRange; - function performDocumentApplyAttributesToRange(start, end, attribs) { + function performDocumentApplyAttributesToRange(start, end, attribs) + { var builder = Changeset.builder(rep.lines.totalWidth()); buildKeepToStartOfRange(builder, start); buildKeepRange(builder, start, end, attribs, rep.apool); @@ -2014,307 +2515,373 @@ function OUTER(gscope) { performDocumentApplyChangeset(cs); } - function buildKeepToStartOfRange(builder, start) { + function buildKeepToStartOfRange(builder, start) + { var startLineOffset = rep.lines.offsetOfIndex(start[0]); builder.keep(startLineOffset, start[0]); builder.keep(start[1]); } - function buildRemoveRange(builder, start, end) { + + function buildRemoveRange(builder, start, end) + { var startLineOffset = rep.lines.offsetOfIndex(start[0]); var endLineOffset = rep.lines.offsetOfIndex(end[0]); - if (end[0] > start[0]) { + if (end[0] > start[0]) + { builder.remove(endLineOffset - startLineOffset - start[1], end[0] - start[0]); builder.remove(end[1]); } - else { + else + { builder.remove(end[1] - start[1]); } } - function buildKeepRange(builder, start, end, attribs, pool) { + + function buildKeepRange(builder, start, end, attribs, pool) + { var startLineOffset = rep.lines.offsetOfIndex(start[0]); var endLineOffset = rep.lines.offsetOfIndex(end[0]); - if (end[0] > start[0]) { + if (end[0] > start[0]) + { builder.keep(endLineOffset - startLineOffset - start[1], end[0] - start[0], attribs, pool); builder.keep(end[1], 0, attribs, pool); } - else { + else + { builder.keep(end[1] - start[1], 0, attribs, pool); } } - function setAttributeOnSelection(attributeName, attributeValue) { + function setAttributeOnSelection(attributeName, attributeValue) + { if (!(rep.selStart && rep.selEnd)) return; - performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, - [[attributeName, attributeValue]]); + performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [ + [attributeName, attributeValue] + ]); } editorInfo.ace_setAttributeOnSelection = setAttributeOnSelection; - function toggleAttributeOnSelection(attributeName) { + function toggleAttributeOnSelection(attributeName) + { if (!(rep.selStart && rep.selEnd)) return; var selectionAllHasIt = true; - var withIt = Changeset.makeAttribsString('+', [[attributeName, 'true']], rep.apool); - var withItRegex = new RegExp(withIt.replace(/\*/g,'\\*')+"(\\*|$)"); - function hasIt(attribs) { return withItRegex.test(attribs); } + var withIt = Changeset.makeAttribsString('+', [ + [attributeName, 'true'] + ], rep.apool); + var withItRegex = new RegExp(withIt.replace(/\*/g, '\\*') + "(\\*|$)"); + + function hasIt(attribs) + { + return withItRegex.test(attribs); + } var selStartLine = rep.selStart[0]; var selEndLine = rep.selEnd[0]; - for(var n=selStartLine; n<=selEndLine; n++) { + for (var n = selStartLine; n <= selEndLine; n++) + { var opIter = Changeset.opIterator(rep.alines[n]); var indexIntoLine = 0; var selectionStartInLine = 0; var selectionEndInLine = rep.lines.atIndex(n).text.length; // exclude newline - if (n == selStartLine) { - selectionStartInLine = rep.selStart[1]; - } - if (n == selEndLine) { - selectionEndInLine = rep.selEnd[1]; - } - while (opIter.hasNext()) { - var op = opIter.next(); - var opStartInLine = indexIntoLine; - var opEndInLine = opStartInLine + op.chars; - if (! hasIt(op.attribs)) { - // does op overlap selection? - if (! (opEndInLine <= selectionStartInLine || opStartInLine >= selectionEndInLine)) { - selectionAllHasIt = false; - break; - } - } - indexIntoLine = opEndInLine; + if (n == selStartLine) + { + selectionStartInLine = rep.selStart[1]; + } + if (n == selEndLine) + { + selectionEndInLine = rep.selEnd[1]; + } + while (opIter.hasNext()) + { + var op = opIter.next(); + var opStartInLine = indexIntoLine; + var opEndInLine = opStartInLine + op.chars; + if (!hasIt(op.attribs)) + { + // does op overlap selection? + if (!(opEndInLine <= selectionStartInLine || opStartInLine >= selectionEndInLine)) + { + selectionAllHasIt = false; + break; + } + } + indexIntoLine = opEndInLine; } - if (! selectionAllHasIt) { - break; + if (!selectionAllHasIt) + { + break; } } - if (selectionAllHasIt) { - performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, - [[attributeName,'']]); + if (selectionAllHasIt) + { + performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [ + [attributeName, ''] + ]); } - else { - performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, - [[attributeName,'true']]); + else + { + performDocumentApplyAttributesToRange(rep.selStart, rep.selEnd, [ + [attributeName, 'true'] + ]); } } editorInfo.ace_toggleAttributeOnSelection = toggleAttributeOnSelection; - function performDocumentReplaceSelection(newText) { + function performDocumentReplaceSelection(newText) + { if (!(rep.selStart && rep.selEnd)) return; performDocumentReplaceRange(rep.selStart, rep.selEnd, newText); } // Change the abstract representation of the document to have a different set of lines. // Must be called after rep.alltext is set. - function doRepLineSplice(startLine, deleteCount, newLineEntries) { - forEach(newLineEntries, function (entry) { entry.width = entry.text.length+1; }); + + function doRepLineSplice(startLine, deleteCount, newLineEntries) + { + + forEach(newLineEntries, function(entry) + { + entry.width = entry.text.length + 1; + }); var startOldChar = rep.lines.offsetOfIndex(startLine); - var endOldChar = rep.lines.offsetOfIndex(startLine+deleteCount); + var endOldChar = rep.lines.offsetOfIndex(startLine + deleteCount); var oldRegionStart = rep.lines.offsetOfIndex(startLine); - var oldRegionEnd = rep.lines.offsetOfIndex(startLine+deleteCount); + var oldRegionEnd = rep.lines.offsetOfIndex(startLine + deleteCount); rep.lines.splice(startLine, deleteCount, newLineEntries); currentCallStack.docTextChanged = true; currentCallStack.repChanged = true; var newRegionEnd = rep.lines.offsetOfIndex(startLine + newLineEntries.length); - var newText = map(newLineEntries, function (e) { return e.text+'\n'; }).join(''); + var newText = map(newLineEntries, function(e) + { + return e.text + '\n'; + }).join(''); - rep.alltext = rep.alltext.substring(0, startOldChar) + newText + - rep.alltext.substring(endOldChar, rep.alltext.length); + rep.alltext = rep.alltext.substring(0, startOldChar) + newText + rep.alltext.substring(endOldChar, rep.alltext.length); //var newTotalLength = rep.alltext.length; - //rep.lexer.updateBuffer(rep.alltext, oldRegionStart, oldRegionEnd - oldRegionStart, //newRegionEnd - oldRegionStart); } - function doIncorpLineSplice(startLine, deleteCount, newLineEntries, lineAttribs, hints) { + function doIncorpLineSplice(startLine, deleteCount, newLineEntries, lineAttribs, hints) + { var startOldChar = rep.lines.offsetOfIndex(startLine); - var endOldChar = rep.lines.offsetOfIndex(startLine+deleteCount); + var endOldChar = rep.lines.offsetOfIndex(startLine + deleteCount); var oldRegionStart = rep.lines.offsetOfIndex(startLine); var selStartHintChar, selEndHintChar; - if (hints && hints.selStart) { - selStartHintChar = rep.lines.offsetOfIndex(hints.selStart[0]) + hints.selStart[1] - - oldRegionStart; + if (hints && hints.selStart) + { + selStartHintChar = rep.lines.offsetOfIndex(hints.selStart[0]) + hints.selStart[1] - oldRegionStart; } - if (hints && hints.selEnd) { - selEndHintChar = rep.lines.offsetOfIndex(hints.selEnd[0]) + hints.selEnd[1] - - oldRegionStart; + if (hints && hints.selEnd) + { + selEndHintChar = rep.lines.offsetOfIndex(hints.selEnd[0]) + hints.selEnd[1] - oldRegionStart; } - var newText = map(newLineEntries, function (e) { return e.text+'\n'; }).join(''); + var newText = map(newLineEntries, function(e) + { + return e.text + '\n'; + }).join(''); var oldText = rep.alltext.substring(startOldChar, endOldChar); - var oldAttribs = rep.alines.slice(startLine, startLine+deleteCount).join(''); - var newAttribs = lineAttribs.join('|1+1')+'|1+1'; // not valid in a changeset - var analysis = analyzeChange(oldText, newText, oldAttribs, newAttribs, - selStartHintChar, selEndHintChar); + var oldAttribs = rep.alines.slice(startLine, startLine + deleteCount).join(''); + var newAttribs = lineAttribs.join('|1+1') + '|1+1'; // not valid in a changeset + var analysis = analyzeChange(oldText, newText, oldAttribs, newAttribs, selStartHintChar, selEndHintChar); var commonStart = analysis[0]; var commonEnd = analysis[1]; var shortOldText = oldText.substring(commonStart, oldText.length - commonEnd); var shortNewText = newText.substring(commonStart, newText.length - commonEnd); - var spliceStart = startOldChar+commonStart; - var spliceEnd = endOldChar-commonEnd; + var spliceStart = startOldChar + commonStart; + var spliceEnd = endOldChar - commonEnd; var shiftFinalNewlineToBeforeNewText = false; // adjust the splice to not involve the final newline of the document; // be very defensive - if (shortOldText.charAt(shortOldText.length-1) == '\n' && - shortNewText.charAt(shortNewText.length-1) == '\n') { + if (shortOldText.charAt(shortOldText.length - 1) == '\n' && shortNewText.charAt(shortNewText.length - 1) == '\n') + { // replacing text that ends in newline with text that also ends in newline // (still, after analysis, somehow) - shortOldText = shortOldText.slice(0,-1); - shortNewText = shortNewText.slice(0,-1); + shortOldText = shortOldText.slice(0, -1); + shortNewText = shortNewText.slice(0, -1); spliceEnd--; commonEnd++; } - if (shortOldText.length == 0 && spliceStart == rep.alltext.length - && shortNewText.length > 0) { + if (shortOldText.length == 0 && spliceStart == rep.alltext.length && shortNewText.length > 0) + { // inserting after final newline, bad spliceStart--; spliceEnd--; - shortNewText = '\n'+shortNewText.slice(0,-1); + shortNewText = '\n' + shortNewText.slice(0, -1); shiftFinalNewlineToBeforeNewText = true; } - if (spliceEnd == rep.alltext.length && shortOldText.length > 0 && - shortNewText.length == 0) { + if (spliceEnd == rep.alltext.length && shortOldText.length > 0 && shortNewText.length == 0) + { // deletion at end of rep.alltext - if (rep.alltext.charAt(spliceStart-1) == '\n') { - // (if not then what the heck? it will definitely lead - // to a rep.alltext without a final newline) - spliceStart--; - spliceEnd--; + if (rep.alltext.charAt(spliceStart - 1) == '\n') + { + // (if not then what the heck? it will definitely lead + // to a rep.alltext without a final newline) + spliceStart--; + spliceEnd--; } } - if (! (shortOldText.length == 0 && shortNewText.length == 0)) { + if (!(shortOldText.length == 0 && shortNewText.length == 0)) + { var oldDocText = rep.alltext; var oldLen = oldDocText.length; var spliceStartLine = rep.lines.indexOfOffset(spliceStart); var spliceStartLineStart = rep.lines.offsetOfIndex(spliceStartLine); - function startBuilder() { - var builder = Changeset.builder(oldLen); - builder.keep(spliceStartLineStart, spliceStartLine); - builder.keep(spliceStart - spliceStartLineStart); - return builder; - } - - function eachAttribRun(attribs, func/*(startInNewText, endInNewText, attribs)*/) { - var attribsIter = Changeset.opIterator(attribs); - var textIndex = 0; - var newTextStart = commonStart; - var newTextEnd = newText.length - commonEnd - (shiftFinalNewlineToBeforeNewText ? 1 : 0); - while (attribsIter.hasNext()) { - var op = attribsIter.next(); - var nextIndex = textIndex + op.chars; - if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) { - func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); - } - textIndex = nextIndex; - } + + function startBuilder() + { + var builder = Changeset.builder(oldLen); + builder.keep(spliceStartLineStart, spliceStartLine); + builder.keep(spliceStart - spliceStartLineStart); + return builder; + } + + function eachAttribRun(attribs, func /*(startInNewText, endInNewText, attribs)*/ ) + { + var attribsIter = Changeset.opIterator(attribs); + var textIndex = 0; + var newTextStart = commonStart; + var newTextEnd = newText.length - commonEnd - (shiftFinalNewlineToBeforeNewText ? 1 : 0); + while (attribsIter.hasNext()) + { + var op = attribsIter.next(); + var nextIndex = textIndex + op.chars; + if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) + { + func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); + } + textIndex = nextIndex; + } } var justApplyStyles = (shortNewText == shortOldText); var theChangeset; - if (justApplyStyles) { - // create changeset that clears the incorporated styles on - // the existing text. we compose this with the - // changeset the applies the styles found in the DOM. - // This allows us to incorporate, e.g., Safari's native "unbold". - - var incorpedAttribClearer = cachedStrFunc(function (oldAtts) { - return Changeset.mapAttribNumbers(oldAtts, function(n) { - var k = rep.apool.getAttribKey(n); - if (isStyleAttribute(k)) { - return rep.apool.putAttrib([k,'']); - } - return false; - }); - }); - - var builder1 = startBuilder(); - if (shiftFinalNewlineToBeforeNewText) { - builder1.keep(1, 1); - } - eachAttribRun(oldAttribs, function(start, end, attribs) { - builder1.keepText(newText.substring(start, end), incorpedAttribClearer(attribs)); - }); - var clearer = builder1.toString(); - - var builder2 = startBuilder(); - if (shiftFinalNewlineToBeforeNewText) { - builder2.keep(1, 1); - } - eachAttribRun(newAttribs, function(start, end, attribs) { - builder2.keepText(newText.substring(start, end), attribs); - }); - var styler = builder2.toString(); - - theChangeset = Changeset.compose(clearer, styler, rep.apool); - } - else { - var builder = startBuilder(); - - var spliceEndLine = rep.lines.indexOfOffset(spliceEnd); - var spliceEndLineStart = rep.lines.offsetOfIndex(spliceEndLine); - if (spliceEndLineStart > spliceStart) { - builder.remove(spliceEndLineStart - spliceStart, spliceEndLine - spliceStartLine); - builder.remove(spliceEnd - spliceEndLineStart); - } - else { - builder.remove(spliceEnd - spliceStart); - } + if (justApplyStyles) + { + // create changeset that clears the incorporated styles on + // the existing text. we compose this with the + // changeset the applies the styles found in the DOM. + // This allows us to incorporate, e.g., Safari's native "unbold". + var incorpedAttribClearer = cachedStrFunc(function(oldAtts) + { + return Changeset.mapAttribNumbers(oldAtts, function(n) + { + var k = rep.apool.getAttribKey(n); + if (isStyleAttribute(k)) + { + return rep.apool.putAttrib([k, '']); + } + return false; + }); + }); + + var builder1 = startBuilder(); + if (shiftFinalNewlineToBeforeNewText) + { + builder1.keep(1, 1); + } + eachAttribRun(oldAttribs, function(start, end, attribs) + { + builder1.keepText(newText.substring(start, end), incorpedAttribClearer(attribs)); + }); + var clearer = builder1.toString(); + + var builder2 = startBuilder(); + if (shiftFinalNewlineToBeforeNewText) + { + builder2.keep(1, 1); + } + eachAttribRun(newAttribs, function(start, end, attribs) + { + builder2.keepText(newText.substring(start, end), attribs); + }); + var styler = builder2.toString(); + + theChangeset = Changeset.compose(clearer, styler, rep.apool); + } + else + { + var builder = startBuilder(); + + var spliceEndLine = rep.lines.indexOfOffset(spliceEnd); + var spliceEndLineStart = rep.lines.offsetOfIndex(spliceEndLine); + if (spliceEndLineStart > spliceStart) + { + builder.remove(spliceEndLineStart - spliceStart, spliceEndLine - spliceStartLine); + builder.remove(spliceEnd - spliceEndLineStart); + } + else + { + builder.remove(spliceEnd - spliceStart); + } var isNewTextMultiauthor = false; - var authorAtt = Changeset.makeAttribsString( - '+', (thisAuthor ? [['author', thisAuthor]] : []), rep.apool); - var authorizer = cachedStrFunc(function(oldAtts) { - if (isNewTextMultiauthor) { + var authorAtt = Changeset.makeAttribsString('+', (thisAuthor ? [ + ['author', thisAuthor] + ] : []), rep.apool); + var authorizer = cachedStrFunc(function(oldAtts) + { + if (isNewTextMultiauthor) + { // prefer colors from DOM - return Changeset.composeAttributes(authorAtt, oldAtts, true, rep.apool); + return Changeset.composeAttributes(authorAtt, oldAtts, true, rep.apool); } - else { + else + { // use this author's color - return Changeset.composeAttributes(oldAtts, authorAtt, true, rep.apool); + return Changeset.composeAttributes(oldAtts, authorAtt, true, rep.apool); } - }); + }); var foundDomAuthor = ''; - eachAttribRun(newAttribs, function(start, end, attribs) { + eachAttribRun(newAttribs, function(start, end, attribs) + { var a = Changeset.attribsAttributeValue(attribs, 'author', rep.apool); - if (a && a != foundDomAuthor) { - if (! foundDomAuthor) { + if (a && a != foundDomAuthor) + { + if (!foundDomAuthor) + { foundDomAuthor = a; } - else { + else + { isNewTextMultiauthor = true; // multiple authors in DOM! } } - }); + }); - if (shiftFinalNewlineToBeforeNewText) { - builder.insert('\n', authorizer('')); - } + if (shiftFinalNewlineToBeforeNewText) + { + builder.insert('\n', authorizer('')); + } - eachAttribRun(newAttribs, function(start, end, attribs) { - builder.insert(newText.substring(start, end), authorizer(attribs)); - }); - theChangeset = builder.toString(); + eachAttribRun(newAttribs, function(start, end, attribs) + { + builder.insert(newText.substring(start, end), authorizer(attribs)); + }); + theChangeset = builder.toString(); } //dmesg(htmlPrettyEscape(theChangeset)); - doRepApplyChangeset(theChangeset); } @@ -2325,44 +2892,57 @@ function OUTER(gscope) { checkALines(); } - function cachedStrFunc(func) { + function cachedStrFunc(func) + { var cache = {}; - return function(s) { - if (! cache[s]) { - cache[s] = func(s); + return function(s) + { + if (!cache[s]) + { + cache[s] = func(s); } return cache[s]; }; } - function analyzeChange(oldText, newText, oldAttribs, newAttribs, optSelStartHint, optSelEndHint) { - function incorpedAttribFilter(anum) { + function analyzeChange(oldText, newText, oldAttribs, newAttribs, optSelStartHint, optSelEndHint) + { + function incorpedAttribFilter(anum) + { return isStyleAttribute(rep.apool.getAttribKey(anum)); } - function attribRuns(attribs) { + + function attribRuns(attribs) + { var lengs = []; var atts = []; var iter = Changeset.opIterator(attribs); - while (iter.hasNext()) { - var op = iter.next(); - lengs.push(op.chars); - atts.push(op.attribs); + while (iter.hasNext()) + { + var op = iter.next(); + lengs.push(op.chars); + atts.push(op.attribs); } - return [lengs,atts]; + return [lengs, atts]; } - function attribIterator(runs, backward) { + + function attribIterator(runs, backward) + { var lengs = runs[0]; var atts = runs[1]; - var i = (backward ? lengs.length-1 : 0); + var i = (backward ? lengs.length - 1 : 0); var j = 0; - return function next() { - while (j >= lengs[i]) { - if (backward) i--; else i++; - j = 0; - } - var a = atts[i]; - j++; - return a; + return function next() + { + while (j >= lengs[i]) + { + if (backward) i--; + else i++; + j = 0; + } + var a = atts[i]; + j++; + return a; }; } @@ -2376,10 +2956,11 @@ function OUTER(gscope) { var commonStart = 0; var oldStartIter = attribIterator(oldARuns, false); var newStartIter = attribIterator(newARuns, false); - while (commonStart < minLen) { - if (oldText.charAt(commonStart) == newText.charAt(commonStart) && - oldStartIter() == newStartIter()) { - commonStart++; + while (commonStart < minLen) + { + if (oldText.charAt(commonStart) == newText.charAt(commonStart) && oldStartIter() == newStartIter()) + { + commonStart++; } else break; } @@ -2387,46 +2968,56 @@ function OUTER(gscope) { var commonEnd = 0; var oldEndIter = attribIterator(oldARuns, true); var newEndIter = attribIterator(newARuns, true); - while (commonEnd < minLen) { - if (commonEnd == 0) { - // assume newline in common - oldEndIter(); newEndIter(); - commonEnd++; - } - else if (oldText.charAt(oldLen-1-commonEnd) == newText.charAt(newLen-1-commonEnd) && - oldEndIter() == newEndIter()) { - commonEnd++; + while (commonEnd < minLen) + { + if (commonEnd == 0) + { + // assume newline in common + oldEndIter(); + newEndIter(); + commonEnd++; + } + else if (oldText.charAt(oldLen - 1 - commonEnd) == newText.charAt(newLen - 1 - commonEnd) && oldEndIter() == newEndIter()) + { + commonEnd++; } else break; } var hintedCommonEnd = -1; - if ((typeof optSelEndHint) == "number") { + if ((typeof optSelEndHint) == "number") + { hintedCommonEnd = newLen - optSelEndHint; } - if (commonStart + commonEnd > oldLen) { + if (commonStart + commonEnd > oldLen) + { // ambiguous insertion var minCommonEnd = oldLen - commonStart; var maxCommonEnd = commonEnd; - if (hintedCommonEnd >= minCommonEnd && hintedCommonEnd <= maxCommonEnd) { - commonEnd = hintedCommonEnd; + if (hintedCommonEnd >= minCommonEnd && hintedCommonEnd <= maxCommonEnd) + { + commonEnd = hintedCommonEnd; } - else { - commonEnd = minCommonEnd; + else + { + commonEnd = minCommonEnd; } commonStart = oldLen - commonEnd; } - if (commonStart + commonEnd > newLen) { + if (commonStart + commonEnd > newLen) + { // ambiguous deletion var minCommonEnd = newLen - commonStart; var maxCommonEnd = commonEnd; - if (hintedCommonEnd >= minCommonEnd && hintedCommonEnd <= maxCommonEnd) { - commonEnd = hintedCommonEnd; + if (hintedCommonEnd >= minCommonEnd && hintedCommonEnd <= maxCommonEnd) + { + commonEnd = hintedCommonEnd; } - else { - commonEnd = minCommonEnd; + else + { + commonEnd = minCommonEnd; } commonStart = newLen - commonEnd; } @@ -2434,31 +3025,33 @@ function OUTER(gscope) { return [commonStart, commonEnd]; } - function equalLineAndChars(a, b) { + function equalLineAndChars(a, b) + { if (!a) return !b; if (!b) return !a; return (a[0] == b[0] && a[1] == b[1]); } - function performSelectionChange(selectStart, selectEnd, focusAtStart) { - if (repSelectionChange(selectStart, selectEnd, focusAtStart)) { + function performSelectionChange(selectStart, selectEnd, focusAtStart) + { + if (repSelectionChange(selectStart, selectEnd, focusAtStart)) + { currentCallStack.selectionAffected = true; } } // Change the abstract representation of the document to have a different selection. // Should not rely on the line representation. Should not affect the DOM. - function repSelectionChange(selectStart, selectEnd, focusAtStart) { + + + function repSelectionChange(selectStart, selectEnd, focusAtStart) + { focusAtStart = !! focusAtStart; - var newSelFocusAtStart = (focusAtStart && - ((! selectStart) || (! selectEnd) || - (selectStart[0] != selectEnd[0]) || - (selectStart[1] != selectEnd[1]))); + var newSelFocusAtStart = (focusAtStart && ((!selectStart) || (!selectEnd) || (selectStart[0] != selectEnd[0]) || (selectStart[1] != selectEnd[1]))); - if ((! equalLineAndChars(rep.selStart, selectStart)) || - (! equalLineAndChars(rep.selEnd, selectEnd)) || - (rep.selFocusAtStart != newSelFocusAtStart)) { + if ((!equalLineAndChars(rep.selStart, selectStart)) || (!equalLineAndChars(rep.selEnd, selectEnd)) || (rep.selFocusAtStart != newSelFocusAtStart)) + { rep.selStart = selectStart; rep.selEnd = selectEnd; rep.selFocusAtStart = newSelFocusAtStart; @@ -2473,74 +3066,98 @@ function OUTER(gscope) { //console.log("%o %o %s", rep.selStart, rep.selEnd, rep.selFocusAtStart); } - function doCreateDomLine(nonEmpty) { - if (browser.msie && (! nonEmpty)) { - var result = { node: null, - appendSpan: noop, - prepareForAdd: noop, - notifyAdded: noop, - clearSpans: noop, - finishUpdate: noop, - lineMarker: 0 }; + function doCreateDomLine(nonEmpty) + { + if (browser.msie && (!nonEmpty)) + { + var result = { + node: null, + appendSpan: noop, + prepareForAdd: noop, + notifyAdded: noop, + clearSpans: noop, + finishUpdate: noop, + lineMarker: 0 + }; var lineElem = doc.createElement("div"); result.node = lineElem; - result.notifyAdded = function() { - // magic -- settng an empty div's innerHTML to the empty string - // keeps it from collapsing. Apparently innerHTML must be set *after* - // adding the node to the DOM. - // Such a div is what IE 6 creates naturally when you make a blank line - // in a document of divs. However, when copy-and-pasted the div will - // contain a space, so we note its emptiness with a property. - lineElem.innerHTML = ""; - // a primitive-valued property survives copy-and-paste - setAssoc(lineElem, "shouldBeEmpty", true); - // an object property doesn't - setAssoc(lineElem, "unpasted", {}); + result.notifyAdded = function() + { + // magic -- settng an empty div's innerHTML to the empty string + // keeps it from collapsing. Apparently innerHTML must be set *after* + // adding the node to the DOM. + // Such a div is what IE 6 creates naturally when you make a blank line + // in a document of divs. However, when copy-and-pasted the div will + // contain a space, so we note its emptiness with a property. + lineElem.innerHTML = ""; + // a primitive-valued property survives copy-and-paste + setAssoc(lineElem, "shouldBeEmpty", true); + // an object property doesn't + setAssoc(lineElem, "unpasted", {}); }; var lineClass = 'ace-line'; - result.appendSpan = function(txt, cls) { - if ((! txt) && cls) { - // gain a whole-line style (currently to show insertion point in CSS) - lineClass = domline.addToLineClass(lineClass, cls); - } - // otherwise, ignore appendSpan, this is an empty line + result.appendSpan = function(txt, cls) + { + if ((!txt) && cls) + { + // gain a whole-line style (currently to show insertion point in CSS) + lineClass = domline.addToLineClass(lineClass, cls); + } + // otherwise, ignore appendSpan, this is an empty line }; - result.clearSpans = function() { - lineClass = ''; // non-null to cause update + result.clearSpans = function() + { + lineClass = ''; // non-null to cause update }; - function writeClass() { - if (lineClass !== null) lineElem.className = lineClass; + + function writeClass() + { + if (lineClass !== null) lineElem.className = lineClass; } result.prepareForAdd = writeClass; result.finishUpdate = writeClass; - result.getInnerHTML = function() { return ""; }; + result.getInnerHTML = function() + { + return ""; + }; return result; } - else { + else + { return domline.createDomLine(nonEmpty, doesWrap, browser, doc); } } - function textify(str) { + function textify(str) + { return str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '); } - var _blockElems = { "div":1, "p":1, "pre":1, "li":1, "ol":1, "ul":1 }; - function isBlockElement(n) { + var _blockElems = { + "div": 1, + "p": 1, + "pre": 1, + "li": 1, + "ol": 1, + "ul": 1 + }; + + function isBlockElement(n) + { return !!_blockElems[(n.tagName || "").toLowerCase()]; } - function getDirtyRanges() { + function getDirtyRanges() + { // based on observedChanges, return a list of ranges of original lines // that need to be removed or replaced with new user content to incorporate // the user's changes into the line representation. ranges may be zero-length, // indicating inserted content. for example, [0,0] means content was inserted // at the top of the document, while [3,4] means line 3 was deleted, modified, // or replaced with one or more new lines of content. ranges do not touch. - var p = PROFILER("getDirtyRanges", false); p.forIndices = 0; p.consecutives = 0; @@ -2548,81 +3165,104 @@ function OUTER(gscope) { var cleanNodeForIndexCache = {}; var N = rep.lines.length(); // old number of lines - function cleanNodeForIndex(i) { + + + function cleanNodeForIndex(i) + { // if line (i) in the un-updated line representation maps to a clean node // in the document, return that node. // if (i) is out of bounds, return true. else return false. - if (cleanNodeForIndexCache[i] === undefined) { - p.forIndices++; - var result; - if (i < 0 || i >= N) { - result = true; // truthy, but no actual node - } - else { - var key = rep.lines.atIndex(i).key; - result = (getCleanNodeByKey(key) || false); - } - cleanNodeForIndexCache[i] = result; + if (cleanNodeForIndexCache[i] === undefined) + { + p.forIndices++; + var result; + if (i < 0 || i >= N) + { + result = true; // truthy, but no actual node + } + else + { + var key = rep.lines.atIndex(i).key; + result = (getCleanNodeByKey(key) || false); + } + cleanNodeForIndexCache[i] = result; } return cleanNodeForIndexCache[i]; } var isConsecutiveCache = {}; - function isConsecutive(i) { - if (isConsecutiveCache[i] === undefined) { - p.consecutives++; - isConsecutiveCache[i] = (function() { - // returns whether line (i) and line (i-1), assumed to be map to clean DOM nodes, - // or document boundaries, are consecutive in the changed DOM - var a = cleanNodeForIndex(i-1); - var b = cleanNodeForIndex(i); - if ((!a) || (!b)) return false; // violates precondition - if ((a === true) && (b === true)) return ! root.firstChild; - if ((a === true) && b.previousSibling) return false; - if ((b === true) && a.nextSibling) return false; - if ((a === true) || (b === true)) return true; - return a.nextSibling == b; - })(); + + function isConsecutive(i) + { + if (isConsecutiveCache[i] === undefined) + { + p.consecutives++; + isConsecutiveCache[i] = (function() + { + // returns whether line (i) and line (i-1), assumed to be map to clean DOM nodes, + // or document boundaries, are consecutive in the changed DOM + var a = cleanNodeForIndex(i - 1); + var b = cleanNodeForIndex(i); + if ((!a) || (!b)) return false; // violates precondition + if ((a === true) && (b === true)) return !root.firstChild; + if ((a === true) && b.previousSibling) return false; + if ((b === true) && a.nextSibling) return false; + if ((a === true) || (b === true)) return true; + return a.nextSibling == b; + })(); } return isConsecutiveCache[i]; } - function isClean(i) { + + function isClean(i) + { // returns whether line (i) in the un-updated representation maps to a clean node, // or is outside the bounds of the document - return !! cleanNodeForIndex(i); + return !!cleanNodeForIndex(i); } // list of pairs, each representing a range of lines that is clean and consecutive // in the changed DOM. lines (-1) and (N) are always clean, but may or may not // be consecutive with lines in the document. pairs are in sorted order. - var cleanRanges = [[-1,N+1]]; - function rangeForLine(i) { + var cleanRanges = [ + [-1, N + 1] + ]; + + function rangeForLine(i) + { // returns index of cleanRange containing i, or -1 if none var answer = -1; - forEach(cleanRanges, function (r, idx) { - if (i >= r[1]) return false; // keep looking - if (i < r[0]) return true; // not found, stop looking - answer = idx; - return true; // found, stop looking + forEach(cleanRanges, function(r, idx) + { + if (i >= r[1]) return false; // keep looking + if (i < r[0]) return true; // not found, stop looking + answer = idx; + return true; // found, stop looking }); return answer; } - function removeLineFromRange(rng, line) { + + function removeLineFromRange(rng, line) + { // rng is index into cleanRanges, line is line number // precond: line is in rng var a = cleanRanges[rng][0]; var b = cleanRanges[rng][1]; - if ((a+1) == b) cleanRanges.splice(rng, 1); + if ((a + 1) == b) cleanRanges.splice(rng, 1); else if (line == a) cleanRanges[rng][0]++; - else if (line == (b-1)) cleanRanges[rng][1]--; - else cleanRanges.splice(rng, 1, [a,line], [line+1,b]); + else if (line == (b - 1)) cleanRanges[rng][1]--; + else cleanRanges.splice(rng, 1, [a, line], [line + 1, b]); } - function splitRange(rng, pt) { + + function splitRange(rng, pt) + { // precond: pt splits cleanRanges[rng] into two non-empty ranges var a = cleanRanges[rng][0]; var b = cleanRanges[rng][1]; - cleanRanges.splice(rng, 1, [a,pt], [pt,b]); + cleanRanges.splice(rng, 1, [a, pt], [pt, b]); } var correctedLines = {}; - function correctlyAssignLine(line) { + + function correctlyAssignLine(line) + { if (correctedLines[line]) return true; p.corrections++; correctedLines[line] = true; @@ -2633,38 +3273,46 @@ function OUTER(gscope) { //console.log("correctly assigning: %d", line); var rng = rangeForLine(line); var lineClean = isClean(line); - if (rng < 0) { - if (lineClean) { - console.debug("somehow lost clean line"); - } - return true; - } - if (! lineClean) { - // a clean-range includes this dirty line, fix it - removeLineFromRange(rng, line); - return false; - } - else { - // line is clean, but could be wrongly connected to a clean line - // above or below - var a = cleanRanges[rng][0]; - var b = cleanRanges[rng][1]; - var didSomething = false; - // we'll leave non-clean adjacent nodes in the clean range for the caller to - // detect and deal with. we deal with whether the range should be split - // just above or just below this line. - if (a < line && isClean(line-1) && ! isConsecutive(line)) { - splitRange(rng, line); - didSomething = true; - } - if (b > (line+1) && isClean(line+1) && ! isConsecutive(line+1)) { - splitRange(rng, line+1); - didSomething = true; - } - return ! didSomething; - } - } - function detectChangesAroundLine(line, reqInARow) { + if (rng < 0) + { + if (lineClean) + { + console.debug("somehow lost clean line"); + } + return true; + } + if (!lineClean) + { + // a clean-range includes this dirty line, fix it + removeLineFromRange(rng, line); + return false; + } + else + { + // line is clean, but could be wrongly connected to a clean line + // above or below + var a = cleanRanges[rng][0]; + var b = cleanRanges[rng][1]; + var didSomething = false; + // we'll leave non-clean adjacent nodes in the clean range for the caller to + // detect and deal with. we deal with whether the range should be split + // just above or just below this line. + if (a < line && isClean(line - 1) && !isConsecutive(line)) + { + splitRange(rng, line); + didSomething = true; + } + if (b > (line + 1) && isClean(line + 1) && !isConsecutive(line + 1)) + { + splitRange(rng, line + 1); + didSomething = true; + } + return !didSomething; + } + } + + function detectChangesAroundLine(line, reqInARow) + { // make sure cleanRanges is correct about line number "line" and the surrounding // lines; only stops checking at end of document or after no changes need // making for several consecutive lines. note that iteration is over old lines, @@ -2672,43 +3320,52 @@ function OUTER(gscope) { // that are changed or missing, not the number of new lines inserted. var correctInARow = 0; var currentIndex = line; - while (correctInARow < reqInARow && currentIndex >= 0) { - if (correctlyAssignLine(currentIndex)) { - correctInARow++; - } - else correctInARow = 0; - currentIndex--; + while (correctInARow < reqInARow && currentIndex >= 0) + { + if (correctlyAssignLine(currentIndex)) + { + correctInARow++; + } + else correctInARow = 0; + currentIndex--; } correctInARow = 0; currentIndex = line; - while (correctInARow < reqInARow && currentIndex < N) { - if (correctlyAssignLine(currentIndex)) { - correctInARow++; - } - else correctInARow = 0; - currentIndex++; + while (correctInARow < reqInARow && currentIndex < N) + { + if (correctlyAssignLine(currentIndex)) + { + correctInARow++; + } + else correctInARow = 0; + currentIndex++; } } - if (N == 0) { + if (N == 0) + { p.cancel(); - if (! isConsecutive(0)) { - splitRange(0, 0); + if (!isConsecutive(0)) + { + splitRange(0, 0); } } - else { + else + { p.mark("topbot"); - detectChangesAroundLine(0,1); - detectChangesAroundLine(N-1,1); + detectChangesAroundLine(0, 1); + detectChangesAroundLine(N - 1, 1); p.mark("obs"); //console.log("observedChanges: "+toSource(observedChanges)); - for (var k in observedChanges.cleanNodesNearChanges) { - var key = k.substring(1); - if (rep.lines.containsKey(key)) { - var line = rep.lines.indexOfKey(key); - detectChangesAroundLine(line,2); - } + for (var k in observedChanges.cleanNodesNearChanges) + { + var key = k.substring(1); + if (rep.lines.containsKey(key)) + { + var line = rep.lines.indexOfKey(key); + detectChangesAroundLine(line, 2); + } } p.mark("stats&calc"); p.literal(p.forIndices, "byidx"); @@ -2717,8 +3374,9 @@ function OUTER(gscope) { } var dirtyRanges = []; - for(var r=0;r<cleanRanges.length-1;r++) { - dirtyRanges.push([cleanRanges[r][1], cleanRanges[r+1][0]]); + for (var r = 0; r < cleanRanges.length - 1; r++) + { + dirtyRanges.push([cleanRanges[r][1], cleanRanges[r + 1][0]]); } p.end(); @@ -2726,12 +3384,14 @@ function OUTER(gscope) { return dirtyRanges; } - function markNodeClean(n) { + function markNodeClean(n) + { // clean nodes have knownHTML that matches their innerHTML var dirtiness = {}; dirtiness.nodeId = uniqueId(n); dirtiness.knownHTML = n.innerHTML; - if (browser.msie) { + if (browser.msie) + { // adding a space to an "empty" div in IE designMode doesn't // change the innerHTML of the div's parent; also, other // browsers don't support innerText @@ -2740,13 +3400,15 @@ function OUTER(gscope) { setAssoc(n, "dirtiness", dirtiness); } - function isNodeDirty(n) { + function isNodeDirty(n) + { var p = PROFILER("cleanCheck", false); if (n.parentNode != root) return true; var data = getAssoc(n, "dirtiness"); if (!data) return true; if (n.id !== data.nodeId) return true; - if (browser.msie) { + if (browser.msie) + { if (n.innerText !== data.knownText) return true; } if (n.innerHTML !== data.knownHTML) return true; @@ -2754,117 +3416,148 @@ function OUTER(gscope) { return false; } - function getLineEntryTopBottom(entry, destObj) { + function getLineEntryTopBottom(entry, destObj) + { var dom = entry.lineNode; var top = dom.offsetTop; var height = dom.offsetHeight; var obj = (destObj || {}); obj.top = top; - obj.bottom = (top+height); + obj.bottom = (top + height); return obj; } - function getViewPortTopBottom() { + function getViewPortTopBottom() + { var theTop = getScrollY(); var doc = outerWin.document; var height = doc.documentElement.clientHeight; - return {top:theTop, bottom:(theTop+height)}; + return { + top: theTop, + bottom: (theTop + height) + }; } - function getVisibleLineRange() { + function getVisibleLineRange() + { var viewport = getViewPortTopBottom(); //console.log("viewport top/bottom: %o", viewport); var obj = {}; - var start = rep.lines.search(function (e) { + var start = rep.lines.search(function(e) + { return getLineEntryTopBottom(e, obj).bottom > viewport.top; }); - var end = rep.lines.search(function(e) { + var end = rep.lines.search(function(e) + { return getLineEntryTopBottom(e, obj).top >= viewport.bottom; }); if (end < start) end = start; // unlikely //console.log(start+","+end); - return [start,end]; + return [start, end]; } - function getVisibleCharRange() { + function getVisibleCharRange() + { var lineRange = getVisibleLineRange(); - return [rep.lines.offsetOfIndex(lineRange[0]), - rep.lines.offsetOfIndex(lineRange[1])]; + return [rep.lines.offsetOfIndex(lineRange[0]), rep.lines.offsetOfIndex(lineRange[1])]; } - function handleClick(evt) { - inCallStack("handleClick", function() { + function handleClick(evt) + { + inCallStack("handleClick", function() + { idleWorkTimer.atMost(200); }); // only want to catch left-click - if ((! evt.ctrlKey) && (evt.button != 2) && (evt.button != 3)) { + if ((!evt.ctrlKey) && (evt.button != 2) && (evt.button != 3)) + { // find A tag with HREF - function isLink(n) { return (n.tagName||'').toLowerCase() == "a" && n.href; } + + + function isLink(n) + { + return (n.tagName || '').toLowerCase() == "a" && n.href; + } var n = evt.target; - while (n && n.parentNode && ! isLink(n)) { n = n.parentNode; } - if (n && isLink(n)) { - try { - var newWindow = window.open(n.href, '_blank'); - newWindow.focus(); + while (n && n.parentNode && !isLink(n)) + { + n = n.parentNode; + } + if (n && isLink(n)) + { + try + { + var newWindow = window.open(n.href, '_blank'); + newWindow.focus(); } - catch (e) { + catch (e) + { // absorb "user canceled" error in IE for certain prompts } - evt.preventDefault(); + evt.preventDefault(); } } } - function doReturnKey() { - if (! (rep.selStart && rep.selEnd)) { + function doReturnKey() + { + if (!(rep.selStart && rep.selEnd)) + { return; } var lineNum = rep.selStart[0]; var listType = getLineListType(lineNum); performDocumentReplaceSelection('\n'); - if (listType) { - if (lineNum+1 < rep.lines.length()) { - setLineListType(lineNum+1, listType); + if (listType) + { + if (lineNum + 1 < rep.lines.length()) + { + setLineListType(lineNum + 1, listType); } } - else { + else + { handleReturnIndentation(); } } - function doIndentOutdent(isOut) { - if (! (rep.selStart && rep.selEnd)) { + function doIndentOutdent(isOut) + { + if (!(rep.selStart && rep.selEnd)) + { return false; } var firstLine, lastLine; firstLine = rep.selStart[0]; - lastLine = Math.max(firstLine, - rep.selEnd[0] - ((rep.selEnd[1] == 0) ? 1 : 0)); + lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] == 0) ? 1 : 0)); var mods = []; var foundLists = false; - for(var n=firstLine;n<=lastLine;n++) { + for (var n = firstLine; n <= lastLine; n++) + { var listType = getLineListType(n); - if (listType) { + if (listType) + { listType = /([a-z]+)([12345678])/.exec(listType); - if (listType) { + if (listType) + { foundLists = true; var t = listType[1]; var level = Number(listType[2]); - var newLevel = - Math.max(1, Math.min(MAX_LIST_LEVEL, - level + (isOut ? -1 : 1))); - if (level != newLevel) { - mods.push([n, t+newLevel]); + var newLevel = Math.max(1, Math.min(MAX_LIST_LEVEL, level + (isOut ? -1 : 1))); + if (level != newLevel) + { + mods.push([n, t + newLevel]); } } } } - if (mods.length > 0) { + if (mods.length > 0) + { setLineListTypes(mods); } @@ -2872,90 +3565,100 @@ function OUTER(gscope) { } editorInfo.ace_doIndentOutdent = doIndentOutdent; - function doTabKey(shiftDown) { - if (! doIndentOutdent(shiftDown)) { + function doTabKey(shiftDown) + { + if (!doIndentOutdent(shiftDown)) + { performDocumentReplaceSelection(THE_TAB); } } - function doDeleteKey(optEvt) { + function doDeleteKey(optEvt) + { var evt = optEvt || {}; var handled = false; - if (rep.selStart) { - if (isCaret()) { - var lineNum = caretLine(); - var col = caretColumn(); + if (rep.selStart) + { + if (isCaret()) + { + var lineNum = caretLine(); + var col = caretColumn(); var lineEntry = rep.lines.atIndex(lineNum); - var lineText = lineEntry.text; + var lineText = lineEntry.text; var lineMarker = lineEntry.lineMarker; - if (/^ +$/.exec(lineText.substring(lineMarker, col))) { + if (/^ +$/.exec(lineText.substring(lineMarker, col))) + { var col2 = col - lineMarker; - var tabSize = THE_TAB.length; - var toDelete = ((col2 - 1) % tabSize)+1; - performDocumentReplaceRange([lineNum,col-toDelete], - [lineNum,col], ''); - //scrollSelectionIntoView(); - handled = true; - } - } - if (! handled) { - if (isCaret()) { + var tabSize = THE_TAB.length; + var toDelete = ((col2 - 1) % tabSize) + 1; + performDocumentReplaceRange([lineNum, col - toDelete], [lineNum, col], ''); + //scrollSelectionIntoView(); + handled = true; + } + } + if (!handled) + { + if (isCaret()) + { var theLine = caretLine(); var lineEntry = rep.lines.atIndex(theLine); - if (caretColumn() <= lineEntry.lineMarker) { + if (caretColumn() <= lineEntry.lineMarker) + { // delete at beginning of line var action = 'delete_newline'; - var prevLineListType = - (theLine > 0 ? getLineListType(theLine-1) : ''); + var prevLineListType = (theLine > 0 ? getLineListType(theLine - 1) : ''); var thisLineListType = getLineListType(theLine); - var prevLineEntry = (theLine > 0 && - rep.lines.atIndex(theLine-1)); - var prevLineBlank = (prevLineEntry && - prevLineEntry.text.length == - prevLineEntry.lineMarker); - if (thisLineListType) { + var prevLineEntry = (theLine > 0 && rep.lines.atIndex(theLine - 1)); + var prevLineBlank = (prevLineEntry && prevLineEntry.text.length == prevLineEntry.lineMarker); + if (thisLineListType) + { // this line is a list - if (prevLineBlank && ! prevLineListType) { - // previous line is blank, remove it - performDocumentReplaceRange([theLine-1, prevLineEntry.text.length], - [theLine, 0], ''); - } - else { + if (prevLineBlank && !prevLineListType) + { + // previous line is blank, remove it + performDocumentReplaceRange([theLine - 1, prevLineEntry.text.length], [theLine, 0], ''); + } + else + { // delistify - performDocumentReplaceRange([theLine, 0], - [theLine, lineEntry.lineMarker], ''); + performDocumentReplaceRange([theLine, 0], [theLine, lineEntry.lineMarker], ''); } } - else if (theLine > 0) { + else if (theLine > 0) + { // remove newline - performDocumentReplaceRange([theLine-1, prevLineEntry.text.length], - [theLine, 0], ''); + performDocumentReplaceRange([theLine - 1, prevLineEntry.text.length], [theLine, 0], ''); } } - else { - var docChar = caretDocChar(); - if (docChar > 0) { - if (evt.metaKey || evt.ctrlKey || evt.altKey) { - // delete as many unicode "letters or digits" in a row as possible; - // always delete one char, delete further even if that first char - // isn't actually a word char. - var deleteBackTo = docChar-1; - while (deleteBackTo > lineEntry.lineMarker && - isWordChar(rep.alltext.charAt(deleteBackTo-1))) { - deleteBackTo--; - } - performDocumentReplaceCharRange(deleteBackTo, docChar, ''); - } - else { - // normal delete - performDocumentReplaceCharRange(docChar-1, docChar, ''); - } + else + { + var docChar = caretDocChar(); + if (docChar > 0) + { + if (evt.metaKey || evt.ctrlKey || evt.altKey) + { + // delete as many unicode "letters or digits" in a row as possible; + // always delete one char, delete further even if that first char + // isn't actually a word char. + var deleteBackTo = docChar - 1; + while (deleteBackTo > lineEntry.lineMarker && isWordChar(rep.alltext.charAt(deleteBackTo - 1))) + { + deleteBackTo--; + } + performDocumentReplaceCharRange(deleteBackTo, docChar, ''); + } + else + { + // normal delete + performDocumentReplaceCharRange(docChar - 1, docChar, ''); + } } - } - } - else { - performDocumentReplaceSelection(''); - } + } + } + else + { + performDocumentReplaceSelection(''); + } } } } @@ -2964,21 +3667,34 @@ function OUTER(gscope) { 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_SPACE = /\s/; - function isWordChar(c) { - return !! REGEX_WORDCHAR.exec(c); + function isWordChar(c) + { + return !!REGEX_WORDCHAR.exec(c); } - function isSpaceChar(c) { - return !! REGEX_SPACE.exec(c); + + function isSpaceChar(c) + { + return !!REGEX_SPACE.exec(c); } - function moveByWordInLine(lineText, initialIndex, forwardNotBack) { + function moveByWordInLine(lineText, initialIndex, forwardNotBack) + { var i = initialIndex; - function nextChar() { + + function nextChar() + { if (forwardNotBack) return lineText.charAt(i); - else return lineText.charAt(i-1); + else return lineText.charAt(i - 1); } - function advance() { if (forwardNotBack) i++; else i--; } - function isDone() { + + function advance() + { + if (forwardNotBack) i++; + else i--; + } + + function isDone() + { if (forwardNotBack) return i >= lineText.length; else return i <= 0; } @@ -2986,22 +3702,36 @@ function OUTER(gscope) { // On Mac and Linux, move right moves to end of word and move left moves to start; // on Windows, always move to start of word. // On Windows, Firefox and IE disagree on whether to stop for punctuation (FF says no). - if (browser.windows && forwardNotBack) { - while ((! isDone()) && isWordChar(nextChar())) { advance(); } - while ((! isDone()) && ! isWordChar(nextChar())) { advance(); } + if (browser.windows && forwardNotBack) + { + while ((!isDone()) && isWordChar(nextChar())) + { + advance(); + } + while ((!isDone()) && !isWordChar(nextChar())) + { + advance(); + } } - else { - while ((! isDone()) && ! isWordChar(nextChar())) { advance(); } - while ((! isDone()) && isWordChar(nextChar())) { advance(); } + else + { + while ((!isDone()) && !isWordChar(nextChar())) + { + advance(); + } + while ((!isDone()) && isWordChar(nextChar())) + { + advance(); + } } return i; } - function handleKeyEvent(evt) { + function handleKeyEvent(evt) + { // if (DEBUG && window.DONT_INCORP) return; - - if (! isEditable) return; + if (!isEditable) return; var type = evt.type; var charCode = evt.charCode; @@ -3009,224 +3739,234 @@ function OUTER(gscope) { var which = evt.which; //dmesg("keyevent type: "+type+", which: "+which); - // Don't take action based on modifier keys going up and down. // Modifier keys do not generate "keypress" events. // 224 is the command-key under Mac Firefox. // 91 is the Windows key in IE; it is ASCII for open-bracket but isn't the keycode for that key // 20 is capslock in IE. - var isModKey = ((!charCode) && - ((type == "keyup") || (type == "keydown")) && - (keyCode == 16 || keyCode == 17 || keyCode == 18 || keyCode == 20 || keyCode == 224 - || keyCode == 91)); + var isModKey = ((!charCode) && ((type == "keyup") || (type == "keydown")) && (keyCode == 16 || keyCode == 17 || keyCode == 18 || keyCode == 20 || keyCode == 224 || keyCode == 91)); if (isModKey) return; var specialHandled = false; - var isTypeForSpecialKey = ((browser.msie || browser.safari) ? - (type == "keydown") : (type == "keypress")); + var isTypeForSpecialKey = ((browser.msie || browser.safari) ? (type == "keydown") : (type == "keypress")); var isTypeForCmdKey = ((browser.msie || browser.safari) ? (type == "keydown") : (type == "keypress")); var stopped = false; - inCallStack("handleKeyEvent", function() { - - if (type == "keypress" || - (isTypeForSpecialKey && keyCode == 13/*return*/)) { - // in IE, special keys don't send keypress, the keydown does the action - if (! outsideKeyPress(evt)) { - evt.preventDefault(); - stopped = true; - } - } - else if (type == "keydown") { - outsideKeyDown(evt); - } - - if (! stopped) { - if (isTypeForSpecialKey && keyCode == 8) { - // "delete" key; in mozilla, if we're at the beginning of a line, normalize now, - // or else deleting a blank line can take two delete presses. - // -- - // we do deletes completely customly now: - // - allows consistent (and better) meta-delete behavior - // - normalizing and then allowing default behavior confused IE - // - probably eliminates a few minor quirks - fastIncorp(3); - evt.preventDefault(); + inCallStack("handleKeyEvent", function() + { + + if (type == "keypress" || (isTypeForSpecialKey && keyCode == 13 /*return*/ )) + { + // in IE, special keys don't send keypress, the keydown does the action + if (!outsideKeyPress(evt)) + { + evt.preventDefault(); + stopped = true; + } + } + else if (type == "keydown") + { + outsideKeyDown(evt); + } + + if (!stopped) + { + if (isTypeForSpecialKey && keyCode == 8) + { + // "delete" key; in mozilla, if we're at the beginning of a line, normalize now, + // or else deleting a blank line can take two delete presses. + // -- + // we do deletes completely customly now: + // - allows consistent (and better) meta-delete behavior + // - normalizing and then allowing default behavior confused IE + // - probably eliminates a few minor quirks + fastIncorp(3); + evt.preventDefault(); doDeleteKey(evt); specialHandled = true; - } - if ((!specialHandled) && isTypeForSpecialKey && keyCode == 13) { - // return key, handle specially; - // note that in mozilla we need to do an incorporation for proper return behavior anyway. - fastIncorp(4); - evt.preventDefault(); - doReturnKey(); - //scrollSelectionIntoView(); - scheduler.setTimeout(function() {outerWin.scrollBy(-100,0);}, 0); - specialHandled = true; - } - if ((!specialHandled) && isTypeForSpecialKey && keyCode == 9 && - ! (evt.metaKey || evt.ctrlKey)) { - // tab - fastIncorp(5); - evt.preventDefault(); + } + if ((!specialHandled) && isTypeForSpecialKey && keyCode == 13) + { + // return key, handle specially; + // note that in mozilla we need to do an incorporation for proper return behavior anyway. + fastIncorp(4); + evt.preventDefault(); + doReturnKey(); + //scrollSelectionIntoView(); + scheduler.setTimeout(function() + { + outerWin.scrollBy(-100, 0); + }, 0); + specialHandled = true; + } + if ((!specialHandled) && isTypeForSpecialKey && keyCode == 9 && !(evt.metaKey || evt.ctrlKey)) + { + // tab + fastIncorp(5); + evt.preventDefault(); doTabKey(evt.shiftKey); - //scrollSelectionIntoView(); - specialHandled = true; - } - if ((!specialHandled) && isTypeForCmdKey && - String.fromCharCode(which).toLowerCase() == "z" && - (evt.metaKey || evt.ctrlKey)) { - // cmd-Z (undo) - fastIncorp(6); - evt.preventDefault(); - if (evt.shiftKey) { - doUndoRedo("redo"); + //scrollSelectionIntoView(); + specialHandled = true; + } + if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "z" && (evt.metaKey || evt.ctrlKey)) + { + // cmd-Z (undo) + fastIncorp(6); + evt.preventDefault(); + if (evt.shiftKey) + { + doUndoRedo("redo"); } - else { - doUndoRedo("undo"); + else + { + doUndoRedo("undo"); } - specialHandled = true; - } - if ((!specialHandled) && isTypeForCmdKey && - String.fromCharCode(which).toLowerCase() == "y" && - (evt.metaKey || evt.ctrlKey)) { - // cmd-Y (redo) - fastIncorp(10); - evt.preventDefault(); - doUndoRedo("redo"); - specialHandled = true; - } - if ((!specialHandled) && isTypeForCmdKey && - String.fromCharCode(which).toLowerCase() == "b" && - (evt.metaKey || evt.ctrlKey)) { - // cmd-B (bold) - fastIncorp(13); - evt.preventDefault(); + specialHandled = true; + } + if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "y" && (evt.metaKey || evt.ctrlKey)) + { + // cmd-Y (redo) + fastIncorp(10); + evt.preventDefault(); + doUndoRedo("redo"); + specialHandled = true; + } + if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "b" && (evt.metaKey || evt.ctrlKey)) + { + // cmd-B (bold) + fastIncorp(13); + evt.preventDefault(); toggleAttributeOnSelection('bold'); - specialHandled = true; - } - if ((!specialHandled) && isTypeForCmdKey && - String.fromCharCode(which).toLowerCase() == "i" && - (evt.metaKey || evt.ctrlKey)) { - // cmd-I (italic) - fastIncorp(14); - evt.preventDefault(); + specialHandled = true; + } + if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "i" && (evt.metaKey || evt.ctrlKey)) + { + // cmd-I (italic) + fastIncorp(14); + evt.preventDefault(); toggleAttributeOnSelection('italic'); - specialHandled = true; - } - if ((!specialHandled) && isTypeForCmdKey && - String.fromCharCode(which).toLowerCase() == "u" && - (evt.metaKey || evt.ctrlKey)) { - // cmd-U (underline) - fastIncorp(15); - evt.preventDefault(); + specialHandled = true; + } + if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "u" && (evt.metaKey || evt.ctrlKey)) + { + // cmd-U (underline) + fastIncorp(15); + evt.preventDefault(); toggleAttributeOnSelection('underline'); - specialHandled = true; - } - if ((!specialHandled) && isTypeForCmdKey && - String.fromCharCode(which).toLowerCase() == "h" && - (evt.ctrlKey)) { - // cmd-H (backspace) - fastIncorp(20); - evt.preventDefault(); + specialHandled = true; + } + if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == "h" && (evt.ctrlKey)) + { + // cmd-H (backspace) + fastIncorp(20); + evt.preventDefault(); doDeleteKey(); - specialHandled = true; - } + specialHandled = true; + } - if (mozillaFakeArrows && mozillaFakeArrows.handleKeyEvent(evt)) { - evt.preventDefault(); - specialHandled = true; - } + if (mozillaFakeArrows && mozillaFakeArrows.handleKeyEvent(evt)) + { + evt.preventDefault(); + specialHandled = true; + } } - if (type == "keydown") { - idleWorkTimer.atLeast(500); + if (type == "keydown") + { + idleWorkTimer.atLeast(500); } - else if (type == "keypress") { - if ((! specialHandled) && parenModule.shouldNormalizeOnChar(charCode)) { - idleWorkTimer.atMost(0); - } - else { - idleWorkTimer.atLeast(500); - } + else if (type == "keypress") + { + if ((!specialHandled) && parenModule.shouldNormalizeOnChar(charCode)) + { + idleWorkTimer.atMost(0); + } + else + { + idleWorkTimer.atLeast(500); + } } - else if (type == "keyup") { - var wait = 200; - idleWorkTimer.atLeast(wait); - idleWorkTimer.atMost(wait); + else if (type == "keyup") + { + var wait = 200; + idleWorkTimer.atLeast(wait); + idleWorkTimer.atMost(wait); } // Is part of multi-keystroke international character on Firefox Mac - var isFirefoxHalfCharacter = - (browser.mozilla && evt.altKey && charCode == 0 && keyCode == 0); + var isFirefoxHalfCharacter = (browser.mozilla && evt.altKey && charCode == 0 && keyCode == 0); // Is part of multi-keystroke international character on Safari Mac - var isSafariHalfCharacter = - (browser.safari && evt.altKey && keyCode == 229); + var isSafariHalfCharacter = (browser.safari && evt.altKey && keyCode == 229); - if (thisKeyDoesntTriggerNormalize || isFirefoxHalfCharacter || isSafariHalfCharacter) { - idleWorkTimer.atLeast(3000); // give user time to type - // if this is a keydown, e.g., the keyup shouldn't trigger a normalize - thisKeyDoesntTriggerNormalize = true; + if (thisKeyDoesntTriggerNormalize || isFirefoxHalfCharacter || isSafariHalfCharacter) + { + idleWorkTimer.atLeast(3000); // give user time to type + // if this is a keydown, e.g., the keyup shouldn't trigger a normalize + thisKeyDoesntTriggerNormalize = true; } - if ((! specialHandled) && (! thisKeyDoesntTriggerNormalize) && - (! inInternationalComposition)) { - if (type != "keyup" || ! incorpIfQuick()) { - observeChangesAroundSelection(); - } + if ((!specialHandled) && (!thisKeyDoesntTriggerNormalize) && (!inInternationalComposition)) + { + if (type != "keyup" || !incorpIfQuick()) + { + observeChangesAroundSelection(); + } } - if (type == "keyup") { - thisKeyDoesntTriggerNormalize = false; + if (type == "keyup") + { + thisKeyDoesntTriggerNormalize = false; } }); } var thisKeyDoesntTriggerNormalize = false; - function doUndoRedo(which) { + function doUndoRedo(which) + { // precond: normalized DOM - if (undoModule.enabled) { + if (undoModule.enabled) + { var whichMethod; if (which == "undo") whichMethod = 'performUndo'; if (which == "redo") whichMethod = 'performRedo'; - if (whichMethod) { - var oldEventType = currentCallStack.editEvent.eventType; - currentCallStack.startNewEvent(which); - undoModule[whichMethod](function(backset, selectionInfo) { - if (backset) { - performDocumentApplyChangeset(backset); - } - if (selectionInfo) { - performSelectionChange(lineAndColumnFromChar(selectionInfo.selStart), - lineAndColumnFromChar(selectionInfo.selEnd), - selectionInfo.selFocusAtStart); - } - var oldEvent = currentCallStack.startNewEvent(oldEventType, true); - return oldEvent; - }); + if (whichMethod) + { + var oldEventType = currentCallStack.editEvent.eventType; + currentCallStack.startNewEvent(which); + undoModule[whichMethod](function(backset, selectionInfo) + { + if (backset) + { + performDocumentApplyChangeset(backset); + } + if (selectionInfo) + { + performSelectionChange(lineAndColumnFromChar(selectionInfo.selStart), lineAndColumnFromChar(selectionInfo.selEnd), selectionInfo.selFocusAtStart); + } + var oldEvent = currentCallStack.startNewEvent(oldEventType, true); + return oldEvent; + }); } } } editorInfo.ace_doUndoRedo = doUndoRedo; - function updateBrowserSelectionFromRep() { + function updateBrowserSelectionFromRep() + { // requires normalized DOM! - var selStart = rep.selStart, selEnd = rep.selEnd; + var selStart = rep.selStart, + selEnd = rep.selEnd; - if (!(selStart && selEnd)) { + if (!(selStart && selEnd)) + { setSelection(null); return; } - var mozillaCaretHack = (false && browser.mozilla && selStart && selEnd && - selStart[0] == selEnd[0] - && selStart[1] == rep.lines.atIndex(selStart[0]).lineMarker - && selEnd[1] == rep.lines.atIndex(selEnd[0]).lineMarker && - setupMozillaCaretHack(selStart[0])); + var mozillaCaretHack = (false && browser.mozilla && selStart && selEnd && selStart[0] == selEnd[0] && selStart[1] == rep.lines.atIndex(selStart[0]).lineMarker && selEnd[1] == rep.lines.atIndex(selEnd[0]).lineMarker && setupMozillaCaretHack(selStart[0])); var selection = {}; @@ -3238,172 +3978,267 @@ function OUTER(gscope) { if (mozillaCaretHack) se[1] += 1; selection.endPoint = getPointForLineAndChar(se); - selection.focusAtStart = !!rep.selFocusAtStart; + selection.focusAtStart = !! rep.selFocusAtStart; setSelection(selection); - if (mozillaCaretHack) { + if (mozillaCaretHack) + { mozillaCaretHack.unhack(); } } - function getRepHTML() { - return map(rep.lines.slice(), function (entry) { + function getRepHTML() + { + return map(rep.lines.slice(), function(entry) + { var text = entry.text; var content; - if (text.length == 0) { - content = '<span style="color: #aaa">--</span>'; + if (text.length == 0) + { + content = '<span style="color: #aaa">--</span>'; } - else { - content = htmlPrettyEscape(text); + else + { + content = htmlPrettyEscape(text); } - return '<div><code>'+content+'</div></code>'; + return '<div><code>' + content + '</div></code>'; }).join(''); } - function nodeMaxIndex(nd) { + function nodeMaxIndex(nd) + { if (isNodeText(nd)) return nd.nodeValue.length; else return 1; } - function hasIESelection() { + function hasIESelection() + { var browserSelection; - try { browserSelection = doc.selection; } catch (e) {} - if (! browserSelection) return false; + try + { + browserSelection = doc.selection; + } + catch (e) + {} + if (!browserSelection) return false; var origSelectionRange; - try { origSelectionRange = browserSelection.createRange(); } catch (e) {} - if (! origSelectionRange) return false; + try + { + origSelectionRange = browserSelection.createRange(); + } + catch (e) + {} + if (!origSelectionRange) return false; var selectionParent = origSelectionRange.parentElement(); if (selectionParent.ownerDocument != doc) return false; return true; } - function getSelection() { + function getSelection() + { // returns null, or a structure containing startPoint and endPoint, // each of which has node (a magicdom node), index, and maxIndex. If the node // is a text node, maxIndex is the length of the text; else maxIndex is 1. // index is between 0 and maxIndex, inclusive. - if (browser.msie) { + if (browser.msie) + { var browserSelection; - try { browserSelection = doc.selection; } catch (e) {} - if (! browserSelection) return null; + try + { + browserSelection = doc.selection; + } + catch (e) + {} + if (!browserSelection) return null; var origSelectionRange; - try { origSelectionRange = browserSelection.createRange(); } catch (e) {} - if (! origSelectionRange) return null; + try + { + origSelectionRange = browserSelection.createRange(); + } + catch (e) + {} + if (!origSelectionRange) return null; var selectionParent = origSelectionRange.parentElement(); if (selectionParent.ownerDocument != doc) return null; - function newRange() { - return doc.body.createTextRange(); - } - function rangeForElementNode(nd) { - var rng = newRange(); - // doesn't work on text nodes - rng.moveToElementText(nd); - return rng; - } - function pointFromCollapsedRange(rng) { - var parNode = rng.parentElement(); - var elemBelow = -1; - var elemAbove = parNode.childNodes.length; - var rangeWithin = rangeForElementNode(parNode); - - if (rng.compareEndPoints("StartToStart", rangeWithin) == 0) { - return {node:parNode, index:0, maxIndex:1}; - } - else if (rng.compareEndPoints("EndToEnd", rangeWithin) == 0) { - if (isBlockElement(parNode) && parNode.nextSibling) { - // caret after block is not consistent across browsers - // (same line vs next) so put caret before next node - return {node:parNode.nextSibling, index:0, maxIndex:1}; - } - return {node:parNode, index:1, maxIndex:1}; - } - else if (parNode.childNodes.length == 0) { - return {node:parNode, index:0, maxIndex:1}; - } - - for(var i=0;i<parNode.childNodes.length;i++) { - var n = parNode.childNodes.item(i); - if (! isNodeText(n)) { - var nodeRange = rangeForElementNode(n); - var startComp = rng.compareEndPoints("StartToStart", nodeRange); - var endComp = rng.compareEndPoints("EndToEnd", nodeRange); - if (startComp >= 0 && endComp <= 0) { - var index = 0; - if (startComp > 0) { - index = 1; - } - return {node:n, index:index, maxIndex:1}; - } - else if (endComp > 0) { - if (i > elemBelow) { - elemBelow = i; - rangeWithin.setEndPoint("StartToEnd", nodeRange); - } - } - else if (startComp < 0) { - if (i < elemAbove) { - elemAbove = i; - rangeWithin.setEndPoint("EndToStart", nodeRange); - } - } - } - } - if ((elemAbove - elemBelow) == 1) { - if (elemBelow >= 0) { - return {node:parNode.childNodes.item(elemBelow), index:1, maxIndex:1}; - } - else { - return {node:parNode.childNodes.item(elemAbove), index:0, maxIndex:1}; - } - } - var idx = 0; - var r = rng.duplicate(); - // infinite stateful binary search! call function for values 0 to inf, - // expecting the answer to be about 40. return index of smallest - // true value. - var indexIntoRange = binarySearchInfinite(40, function (i) { - // the search algorithm whips the caret back and forth, - // though it has to be moved relatively and may hit - // the end of the buffer - var delta = i-idx; - var moved = Math.abs(r.move("character", -delta)); - // next line is work-around for fact that when moving left, the beginning - // of a text node is considered to be after the start of the parent element: - if (r.move("character", -1)) r.move("character", 1); - if (delta < 0) idx -= moved; - else idx += moved; - return (r.compareEndPoints("StartToStart", rangeWithin) <= 0); - }); - // iterate over consecutive text nodes, point is in one of them - var textNode = elemBelow+1; - var indexLeft = indexIntoRange; - while (textNode < elemAbove) { - var tn = parNode.childNodes.item(textNode); - if (indexLeft <= tn.nodeValue.length) { - return {node:tn, index:indexLeft, maxIndex:tn.nodeValue.length}; - } - indexLeft -= tn.nodeValue.length; - textNode++; - } - var tn = parNode.childNodes.item(textNode-1); - return {node:tn, index:tn.nodeValue.length, maxIndex:tn.nodeValue.length}; + + function newRange() + { + return doc.body.createTextRange(); + } + + function rangeForElementNode(nd) + { + var rng = newRange(); + // doesn't work on text nodes + rng.moveToElementText(nd); + return rng; + } + + function pointFromCollapsedRange(rng) + { + var parNode = rng.parentElement(); + var elemBelow = -1; + var elemAbove = parNode.childNodes.length; + var rangeWithin = rangeForElementNode(parNode); + + if (rng.compareEndPoints("StartToStart", rangeWithin) == 0) + { + return { + node: parNode, + index: 0, + maxIndex: 1 + }; + } + else if (rng.compareEndPoints("EndToEnd", rangeWithin) == 0) + { + if (isBlockElement(parNode) && parNode.nextSibling) + { + // caret after block is not consistent across browsers + // (same line vs next) so put caret before next node + return { + node: parNode.nextSibling, + index: 0, + maxIndex: 1 + }; + } + return { + node: parNode, + index: 1, + maxIndex: 1 + }; + } + else if (parNode.childNodes.length == 0) + { + return { + node: parNode, + index: 0, + maxIndex: 1 + }; + } + + for (var i = 0; i < parNode.childNodes.length; i++) + { + var n = parNode.childNodes.item(i); + if (!isNodeText(n)) + { + var nodeRange = rangeForElementNode(n); + var startComp = rng.compareEndPoints("StartToStart", nodeRange); + var endComp = rng.compareEndPoints("EndToEnd", nodeRange); + if (startComp >= 0 && endComp <= 0) + { + var index = 0; + if (startComp > 0) + { + index = 1; + } + return { + node: n, + index: index, + maxIndex: 1 + }; + } + else if (endComp > 0) + { + if (i > elemBelow) + { + elemBelow = i; + rangeWithin.setEndPoint("StartToEnd", nodeRange); + } + } + else if (startComp < 0) + { + if (i < elemAbove) + { + elemAbove = i; + rangeWithin.setEndPoint("EndToStart", nodeRange); + } + } + } + } + if ((elemAbove - elemBelow) == 1) + { + if (elemBelow >= 0) + { + return { + node: parNode.childNodes.item(elemBelow), + index: 1, + maxIndex: 1 + }; + } + else + { + return { + node: parNode.childNodes.item(elemAbove), + index: 0, + maxIndex: 1 + }; + } + } + var idx = 0; + var r = rng.duplicate(); + // infinite stateful binary search! call function for values 0 to inf, + // expecting the answer to be about 40. return index of smallest + // true value. + var indexIntoRange = binarySearchInfinite(40, function(i) + { + // the search algorithm whips the caret back and forth, + // though it has to be moved relatively and may hit + // the end of the buffer + var delta = i - idx; + var moved = Math.abs(r.move("character", -delta)); + // next line is work-around for fact that when moving left, the beginning + // of a text node is considered to be after the start of the parent element: + if (r.move("character", -1)) r.move("character", 1); + if (delta < 0) idx -= moved; + else idx += moved; + return (r.compareEndPoints("StartToStart", rangeWithin) <= 0); + }); + // iterate over consecutive text nodes, point is in one of them + var textNode = elemBelow + 1; + var indexLeft = indexIntoRange; + while (textNode < elemAbove) + { + var tn = parNode.childNodes.item(textNode); + if (indexLeft <= tn.nodeValue.length) + { + return { + node: tn, + index: indexLeft, + maxIndex: tn.nodeValue.length + }; + } + indexLeft -= tn.nodeValue.length; + textNode++; + } + var tn = parNode.childNodes.item(textNode - 1); + return { + node: tn, + index: tn.nodeValue.length, + maxIndex: tn.nodeValue.length + }; } var selection = {}; - if (origSelectionRange.compareEndPoints("StartToEnd", origSelectionRange) == 0) { - // collapsed - var pnt = pointFromCollapsedRange(origSelectionRange); - selection.startPoint = pnt; - selection.endPoint = {node:pnt.node, index:pnt.index, maxIndex:pnt.maxIndex}; - } - else { - var start = origSelectionRange.duplicate(); - start.collapse(true); - var end = origSelectionRange.duplicate(); - end.collapse(false); - selection.startPoint = pointFromCollapsedRange(start); - selection.endPoint = pointFromCollapsedRange(end); - /*if ((!selection.startPoint.node.isText) && (!selection.endPoint.node.isText)) { + if (origSelectionRange.compareEndPoints("StartToEnd", origSelectionRange) == 0) + { + // collapsed + var pnt = pointFromCollapsedRange(origSelectionRange); + selection.startPoint = pnt; + selection.endPoint = { + node: pnt.node, + index: pnt.index, + maxIndex: pnt.maxIndex + }; + } + else + { + var start = origSelectionRange.duplicate(); + start.collapse(true); + var end = origSelectionRange.duplicate(); + end.collapse(false); + selection.startPoint = pointFromCollapsedRange(start); + selection.endPoint = pointFromCollapsedRange(end); +/*if ((!selection.startPoint.node.isText) && (!selection.endPoint.node.isText)) { console.log(selection.startPoint.node.uniqueId()+","+ selection.startPoint.index+" / "+ selection.endPoint.node.uniqueId()+","+ @@ -3412,274 +4247,371 @@ function OUTER(gscope) { } return selection; } - else { + else + { // non-IE browser var browserSelection = window.getSelection(); - if (browserSelection && browserSelection.type != "None" && - browserSelection.rangeCount !== 0) { - var range = browserSelection.getRangeAt(0); - function isInBody(n) { - while (n && ! (n.tagName && n.tagName.toLowerCase() == "body")) { - n = n.parentNode; - } - return !!n; - } - function pointFromRangeBound(container, offset) { - if (! isInBody(container)) { - // command-click in Firefox selects whole document, HEAD and BODY! - return {node:root, index:0, maxIndex:1}; - } - var n = container; - var childCount = n.childNodes.length; - if (isNodeText(n)) { - return {node:n, index:offset, maxIndex:n.nodeValue.length}; - } - else if (childCount == 0) { - return {node:n, index:0, maxIndex:1}; - } - // treat point between two nodes as BEFORE the second (rather than after the first) - // if possible; this way point at end of a line block-element is treated as - // at beginning of next line - else if (offset == childCount) { - var nd = n.childNodes.item(childCount-1); - var max = nodeMaxIndex(nd); - return {node:nd, index:max, maxIndex:max}; - } - else { - var nd = n.childNodes.item(offset); - var max = nodeMaxIndex(nd); - return {node:nd, index:0, maxIndex:max}; - } - } - var selection = {}; - selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset); - selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset); - selection.focusAtStart = (((range.startContainer != range.endContainer) || - (range.startOffset != range.endOffset)) && - browserSelection.anchorNode && - (browserSelection.anchorNode == range.endContainer) && - (browserSelection.anchorOffset == range.endOffset)); - return selection; + if (browserSelection && browserSelection.type != "None" && browserSelection.rangeCount !== 0) + { + var range = browserSelection.getRangeAt(0); + + function isInBody(n) + { + while (n && !(n.tagName && n.tagName.toLowerCase() == "body")) + { + n = n.parentNode; + } + return !!n; + } + + function pointFromRangeBound(container, offset) + { + if (!isInBody(container)) + { + // command-click in Firefox selects whole document, HEAD and BODY! + return { + node: root, + index: 0, + maxIndex: 1 + }; + } + var n = container; + var childCount = n.childNodes.length; + if (isNodeText(n)) + { + return { + node: n, + index: offset, + maxIndex: n.nodeValue.length + }; + } + else if (childCount == 0) + { + return { + node: n, + index: 0, + maxIndex: 1 + }; + } + // treat point between two nodes as BEFORE the second (rather than after the first) + // if possible; this way point at end of a line block-element is treated as + // at beginning of next line + else if (offset == childCount) + { + var nd = n.childNodes.item(childCount - 1); + var max = nodeMaxIndex(nd); + return { + node: nd, + index: max, + maxIndex: max + }; + } + else + { + var nd = n.childNodes.item(offset); + var max = nodeMaxIndex(nd); + return { + node: nd, + index: 0, + maxIndex: max + }; + } + } + var selection = {}; + selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset); + selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset); + selection.focusAtStart = (((range.startContainer != range.endContainer) || (range.startOffset != range.endOffset)) && browserSelection.anchorNode && (browserSelection.anchorNode == range.endContainer) && (browserSelection.anchorOffset == range.endOffset)); + return selection; } else return null; } } - function setSelection(selection) { - function copyPoint(pt) { - return {node:pt.node, index:pt.index, maxIndex:pt.maxIndex}; + function setSelection(selection) + { + function copyPoint(pt) + { + return { + node: pt.node, + index: pt.index, + maxIndex: pt.maxIndex + }; } - if (browser.msie) { + if (browser.msie) + { // Oddly enough, accessing scrollHeight fixes return key handling on IE 8, // presumably by forcing some kind of internal DOM update. doc.body.scrollHeight; - function moveToElementText(s, n) { - while (n.firstChild && ! isNodeText(n.firstChild)) { + function moveToElementText(s, n) + { + while (n.firstChild && !isNodeText(n.firstChild)) + { n = n.firstChild; } s.moveToElementText(n); } - function newRange() { - return doc.body.createTextRange(); - } - function setCollapsedBefore(s, n) { - // s is an IE TextRange, n is a dom node - if (isNodeText(n)) { - // previous node should not also be text, but prevent inf recurs - if (n.previousSibling && ! isNodeText(n.previousSibling)) { - setCollapsedAfter(s, n.previousSibling); - } - else { - setCollapsedBefore(s, n.parentNode); - } - } - else { - moveToElementText(s, n); + + function newRange() + { + return doc.body.createTextRange(); + } + + function setCollapsedBefore(s, n) + { + // s is an IE TextRange, n is a dom node + if (isNodeText(n)) + { + // previous node should not also be text, but prevent inf recurs + if (n.previousSibling && !isNodeText(n.previousSibling)) + { + setCollapsedAfter(s, n.previousSibling); + } + else + { + setCollapsedBefore(s, n.parentNode); + } + } + else + { + moveToElementText(s, n); // work around for issue that caret at beginning of line // somehow ends up at end of previous line - if (s.move('character', 1)) { + if (s.move('character', 1)) + { s.move('character', -1); } - s.collapse(true); // to start - } - } - function setCollapsedAfter(s, n) { - // s is an IE TextRange, n is a magicdom node - if (isNodeText(n)) { - // can't use end of container when no nextSibling (could be on next line), - // so use previousSibling or start of container and move forward. - setCollapsedBefore(s, n); - s.move("character", n.nodeValue.length); - } - else { - moveToElementText(s, n); - s.collapse(false); // to end - } - } - function getPointRange(point) { - var s = newRange(); - var n = point.node; - if (isNodeText(n)) { - setCollapsedBefore(s, n); - s.move("character", point.index); - } - else if (point.index == 0) { - setCollapsedBefore(s, n); - } - else { - setCollapsedAfter(s, n); - } - return s; - } - - if (selection) { - if (! hasIESelection()) { - return; // don't steal focus - } - - var startPoint = copyPoint(selection.startPoint); - var endPoint = copyPoint(selection.endPoint); - - // fix issue where selection can't be extended past end of line - // with shift-rightarrow or shift-downarrow - if (endPoint.index == endPoint.maxIndex && endPoint.node.nextSibling) { - endPoint.node = endPoint.node.nextSibling; - endPoint.index = 0; - endPoint.maxIndex = nodeMaxIndex(endPoint.node); - } - var range = getPointRange(startPoint); - range.setEndPoint("EndToEnd", getPointRange(endPoint)); - - // setting the selection in IE causes everything to scroll - // so that the selection is visible. if setting the selection - // definitely accomplishes nothing, don't do it. - function isEqualToDocumentSelection(rng) { - var browserSelection; - try { browserSelection = doc.selection; } catch (e) {} - if (! browserSelection) return false; - var rng2 = browserSelection.createRange(); - if (rng2.parentElement().ownerDocument != doc) return false; - if (rng.compareEndPoints("StartToStart", rng2) !== 0) return false; - if (rng.compareEndPoints("EndToEnd", rng2) !== 0) return false; - return true; - } - if (! isEqualToDocumentSelection(range)) { - //dmesg(toSource(selection)); - //dmesg(escapeHTML(doc.body.innerHTML)); - range.select(); - } - } - else { - try { doc.selection.empty(); } catch (e) {} - } - } - else { + s.collapse(true); // to start + } + } + + function setCollapsedAfter(s, n) + { + // s is an IE TextRange, n is a magicdom node + if (isNodeText(n)) + { + // can't use end of container when no nextSibling (could be on next line), + // so use previousSibling or start of container and move forward. + setCollapsedBefore(s, n); + s.move("character", n.nodeValue.length); + } + else + { + moveToElementText(s, n); + s.collapse(false); // to end + } + } + + function getPointRange(point) + { + var s = newRange(); + var n = point.node; + if (isNodeText(n)) + { + setCollapsedBefore(s, n); + s.move("character", point.index); + } + else if (point.index == 0) + { + setCollapsedBefore(s, n); + } + else + { + setCollapsedAfter(s, n); + } + return s; + } + + if (selection) + { + if (!hasIESelection()) + { + return; // don't steal focus + } + + var startPoint = copyPoint(selection.startPoint); + var endPoint = copyPoint(selection.endPoint); + + // fix issue where selection can't be extended past end of line + // with shift-rightarrow or shift-downarrow + if (endPoint.index == endPoint.maxIndex && endPoint.node.nextSibling) + { + endPoint.node = endPoint.node.nextSibling; + endPoint.index = 0; + endPoint.maxIndex = nodeMaxIndex(endPoint.node); + } + var range = getPointRange(startPoint); + range.setEndPoint("EndToEnd", getPointRange(endPoint)); + + // setting the selection in IE causes everything to scroll + // so that the selection is visible. if setting the selection + // definitely accomplishes nothing, don't do it. + + + function isEqualToDocumentSelection(rng) + { + var browserSelection; + try + { + browserSelection = doc.selection; + } + catch (e) + {} + if (!browserSelection) return false; + var rng2 = browserSelection.createRange(); + if (rng2.parentElement().ownerDocument != doc) return false; + if (rng.compareEndPoints("StartToStart", rng2) !== 0) return false; + if (rng.compareEndPoints("EndToEnd", rng2) !== 0) return false; + return true; + } + if (!isEqualToDocumentSelection(range)) + { + //dmesg(toSource(selection)); + //dmesg(escapeHTML(doc.body.innerHTML)); + range.select(); + } + } + else + { + try + { + doc.selection.empty(); + } + catch (e) + {} + } + } + else + { // non-IE browser var isCollapsed; - function pointToRangeBound(pt) { - var p = copyPoint(pt); - // Make sure Firefox cursor is deep enough; fixes cursor jumping when at top level, - // and also problem where cut/copy of a whole line selected with fake arrow-keys - // copies the next line too. - if (isCollapsed) { - function diveDeep() { - while (p.node.childNodes.length > 0) { - //&& (p.node == root || p.node.parentNode == root)) { - if (p.index == 0) { - p.node = p.node.firstChild; - p.maxIndex = nodeMaxIndex(p.node); - } - else if (p.index == p.maxIndex) { - p.node = p.node.lastChild; - p.maxIndex = nodeMaxIndex(p.node); - p.index = p.maxIndex; - } - else break; - } - } - // now fix problem where cursor at end of text node at end of span-like element - // with background doesn't seem to show up... - if (isNodeText(p.node) && p.index == p.maxIndex) { - var n = p.node; - while ((! n.nextSibling) && (n != root) && (n.parentNode != root)) { - n = n.parentNode; - } - if (n.nextSibling && - (! ((typeof n.nextSibling.tagName) == "string" && - n.nextSibling.tagName.toLowerCase() == "br")) && - (n != p.node) && (n != root) && (n.parentNode != root)) { - // found a parent, go to next node and dive in - p.node = n.nextSibling; - p.maxIndex = nodeMaxIndex(p.node); - p.index = 0; - diveDeep(); - } - } - // try to make sure insertion point is styled; + + function pointToRangeBound(pt) + { + var p = copyPoint(pt); + // Make sure Firefox cursor is deep enough; fixes cursor jumping when at top level, + // and also problem where cut/copy of a whole line selected with fake arrow-keys + // copies the next line too. + if (isCollapsed) + { + function diveDeep() + { + while (p.node.childNodes.length > 0) + { + //&& (p.node == root || p.node.parentNode == root)) { + if (p.index == 0) + { + p.node = p.node.firstChild; + p.maxIndex = nodeMaxIndex(p.node); + } + else if (p.index == p.maxIndex) + { + p.node = p.node.lastChild; + p.maxIndex = nodeMaxIndex(p.node); + p.index = p.maxIndex; + } + else break; + } + } + // now fix problem where cursor at end of text node at end of span-like element + // with background doesn't seem to show up... + if (isNodeText(p.node) && p.index == p.maxIndex) + { + var n = p.node; + while ((!n.nextSibling) && (n != root) && (n.parentNode != root)) + { + n = n.parentNode; + } + if (n.nextSibling && (!((typeof n.nextSibling.tagName) == "string" && n.nextSibling.tagName.toLowerCase() == "br")) && (n != p.node) && (n != root) && (n.parentNode != root)) + { + // found a parent, go to next node and dive in + p.node = n.nextSibling; + p.maxIndex = nodeMaxIndex(p.node); + p.index = 0; + diveDeep(); + } + } + // try to make sure insertion point is styled; // also fixes other FF problems - if (! isNodeText(p.node)) { - diveDeep(); - } - } - if (isNodeText(p.node)) { - return { container: p.node, offset: p.index }; - } - else { - // p.index in {0,1} - return { container: p.node.parentNode, offset: childIndex(p.node) + p.index }; - } + if (!isNodeText(p.node)) + { + diveDeep(); + } + } + if (isNodeText(p.node)) + { + return { + container: p.node, + offset: p.index + }; + } + else + { + // p.index in {0,1} + return { + container: p.node.parentNode, + offset: childIndex(p.node) + p.index + }; + } } var browserSelection = window.getSelection(); - if (browserSelection) { - browserSelection.removeAllRanges(); - if (selection) { - isCollapsed = (selection.startPoint.node === selection.endPoint.node && - selection.startPoint.index === selection.endPoint.index); - var start = pointToRangeBound(selection.startPoint); - var end = pointToRangeBound(selection.endPoint); - - if ((!isCollapsed) && selection.focusAtStart && browserSelection.collapse && browserSelection.extend) { - // can handle "backwards"-oriented selection, shift-arrow-keys move start - // of selection - browserSelection.collapse(end.container, end.offset); - //console.trace(); - //console.log(htmlPrettyEscape(rep.alltext)); - //console.log("%o %o", rep.selStart, rep.selEnd); - //console.log("%o %d", start.container, start.offset); - browserSelection.extend(start.container, start.offset); - } - else { - var range = doc.createRange(); - range.setStart(start.container, start.offset); - range.setEnd(end.container, end.offset); - browserSelection.removeAllRanges(); - browserSelection.addRange(range); - } - } - } - } - } - - function childIndex(n) { + if (browserSelection) + { + browserSelection.removeAllRanges(); + if (selection) + { + isCollapsed = (selection.startPoint.node === selection.endPoint.node && selection.startPoint.index === selection.endPoint.index); + var start = pointToRangeBound(selection.startPoint); + var end = pointToRangeBound(selection.endPoint); + + if ((!isCollapsed) && selection.focusAtStart && browserSelection.collapse && browserSelection.extend) + { + // can handle "backwards"-oriented selection, shift-arrow-keys move start + // of selection + browserSelection.collapse(end.container, end.offset); + //console.trace(); + //console.log(htmlPrettyEscape(rep.alltext)); + //console.log("%o %o", rep.selStart, rep.selEnd); + //console.log("%o %d", start.container, start.offset); + browserSelection.extend(start.container, start.offset); + } + else + { + var range = doc.createRange(); + range.setStart(start.container, start.offset); + range.setEnd(end.container, end.offset); + browserSelection.removeAllRanges(); + browserSelection.addRange(range); + } + } + } + } + } + + function childIndex(n) + { var idx = 0; - while (n.previousSibling) { + while (n.previousSibling) + { idx++; n = n.previousSibling; } return idx; } - function fixView() { + function fixView() + { // calling this method repeatedly should be fast - - if (getInnerWidth() == 0 || getInnerHeight() == 0) { + if (getInnerWidth() == 0 || getInnerHeight() == 0) + { return; } - function setIfNecessary(obj, prop, value) { - if (obj[prop] != value) { - obj[prop] = value; + function setIfNecessary(obj, prop, value) + { + if (obj[prop] != value) + { + obj[prop] = value; } } @@ -3688,47 +4620,55 @@ function OUTER(gscope) { if (newSideDivWidth < MIN_LINEDIV_WIDTH) newSideDivWidth = MIN_LINEDIV_WIDTH; iframePadLeft = EDIT_BODY_PADDING_LEFT; if (hasLineNumbers) iframePadLeft += newSideDivWidth + LINE_NUMBER_PADDING_RIGHT; - setIfNecessary(iframe.style, "left", iframePadLeft+"px"); - setIfNecessary(sideDiv.style, "width", newSideDivWidth+"px"); + setIfNecessary(iframe.style, "left", iframePadLeft + "px"); + setIfNecessary(sideDiv.style, "width", newSideDivWidth + "px"); - for(var i=0;i<2;i++) { + for (var i = 0; i < 2; i++) + { var newHeight = root.clientHeight; var newWidth = (browser.msie ? root.createTextRange().boundingWidth : root.clientWidth); var viewHeight = getInnerHeight() - iframePadBottom - iframePadTop; var viewWidth = getInnerWidth() - iframePadLeft - iframePadRight; - if (newHeight < viewHeight) { - newHeight = viewHeight; - if (browser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); + if (newHeight < viewHeight) + { + newHeight = viewHeight; + if (browser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'auto'); } - else { - if (browser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); + else + { + if (browser.msie) setIfNecessary(outerWin.document.documentElement.style, 'overflowY', 'scroll'); } - if (doesWrap) { - newWidth = viewWidth; + if (doesWrap) + { + newWidth = viewWidth; } - else { - if (newWidth < viewWidth) newWidth = viewWidth; + else + { + if (newWidth < viewWidth) newWidth = viewWidth; } if (newHeight > 32000) newHeight = 32000; if (newWidth > 32000) newWidth = 32000; - setIfNecessary(iframe.style, "height", newHeight+"px"); - setIfNecessary(iframe.style, "width", newWidth+"px"); - setIfNecessary(sideDiv.style, "height", newHeight+"px"); - } - if (browser.mozilla) { - if (! doesWrap) { - // the body:display:table-cell hack makes mozilla do scrolling - // correctly by shrinking the <body> to fit around its content, - // but mozilla won't act on clicks below the body. We keep the - // style.height property set to the viewport height (editor height - // not including scrollbar), so it will never shrink so that part of - // the editor isn't clickable. - var body = root; - var styleHeight = viewHeight+"px"; - setIfNecessary(body.style, "height", styleHeight); - } - else { - setIfNecessary(root.style, "height", ""); + setIfNecessary(iframe.style, "height", newHeight + "px"); + setIfNecessary(iframe.style, "width", newWidth + "px"); + setIfNecessary(sideDiv.style, "height", newHeight + "px"); + } + if (browser.mozilla) + { + if (!doesWrap) + { + // the body:display:table-cell hack makes mozilla do scrolling + // correctly by shrinking the <body> to fit around its content, + // but mozilla won't act on clicks below the body. We keep the + // style.height property set to the viewport height (editor height + // not including scrollbar), so it will never shrink so that part of + // the editor isn't clickable. + var body = root; + var styleHeight = viewHeight + "px"; + setIfNecessary(body.style, "height", styleHeight); + } + else + { + setIfNecessary(root.style, "height", ""); } } // if near edge, scroll to edge @@ -3742,81 +4682,110 @@ function OUTER(gscope) { addClass(sideDiv, 'sidedivdelayed'); } - function getScrollXY() { + function getScrollXY() + { var win = outerWin; var odoc = outerWin.document; - if (typeof(win.pageYOffset) == "number") { - return {x: win.pageXOffset, y: win.pageYOffset}; + if (typeof(win.pageYOffset) == "number") + { + return { + x: win.pageXOffset, + y: win.pageYOffset + }; } var docel = odoc.documentElement; - if (docel && typeof(docel.scrollTop) == "number") { - return {x:docel.scrollLeft, y:docel.scrollTop}; + if (docel && typeof(docel.scrollTop) == "number") + { + return { + x: docel.scrollLeft, + y: docel.scrollTop + }; } } - function getScrollX() { + function getScrollX() + { return getScrollXY().x; } - function getScrollY() { + function getScrollY() + { return getScrollXY().y; } - function setScrollX(x) { + function setScrollX(x) + { outerWin.scrollTo(x, getScrollY()); } - function setScrollY(y) { + function setScrollY(y) + { outerWin.scrollTo(getScrollX(), y); } - function setScrollXY(x, y) { + function setScrollXY(x, y) + { outerWin.scrollTo(x, y); } var _teardownActions = []; - function teardown() { - forEach(_teardownActions, function (a) { a(); }); + + function teardown() + { + forEach(_teardownActions, function(a) + { + a(); + }); } bindEventHandler(window, "load", setup); - function setDesignMode(newVal) { - try { - function setIfNecessary(target, prop, val) { - if (String(target[prop]).toLowerCase() != val) { - target[prop] = val; - return true; - } - return false; - } - if (browser.msie || browser.safari) { - setIfNecessary(root, 'contentEditable', (newVal ? 'true' : 'false')); - } - else { - var wasSet = setIfNecessary(doc, 'designMode', (newVal ? 'on' : 'off')); - if (wasSet && newVal && browser.opera) { - // turning on designMode clears event handlers - bindTheEventHandlers(); - } + function setDesignMode(newVal) + { + try + { + function setIfNecessary(target, prop, val) + { + if (String(target[prop]).toLowerCase() != val) + { + target[prop] = val; + return true; + } + return false; + } + if (browser.msie || browser.safari) + { + setIfNecessary(root, 'contentEditable', (newVal ? 'true' : 'false')); + } + else + { + var wasSet = setIfNecessary(doc, 'designMode', (newVal ? 'on' : 'off')); + if (wasSet && newVal && browser.opera) + { + // turning on designMode clears event handlers + bindTheEventHandlers(); + } } return true; } - catch (e) { + catch (e) + { return false; } } var iePastedLines = null; - function handleIEPaste(evt) { + + function handleIEPaste(evt) + { // Pasting in IE loses blank lines in a way that loses information; // "one\n\ntwo\nthree" becomes "<p>one</p><p>two</p><p>three</p>", // which becomes "one\ntwo\nthree". We can get the correct text // from the clipboard directly, but we still have to let the paste // happen to get the style information. - var clipText = window.clipboardData && window.clipboardData.getData("Text"); - if (clipText && doc.selection) { + if (clipText && doc.selection) + { // this "paste" event seems to mess with the selection whether we try to // stop it or not, so can't really do document-level manipulation now // or in an idle call-stack. instead, use IE native manipulation @@ -3826,110 +4795,147 @@ function OUTER(gscope) { //var newHTML = map(clipText.replace(/\r/g,'').split('\n'), escapeLine).join('<br>'); //doc.selection.createRange().pasteHTML(newHTML); //evt.preventDefault(); - //iePastedLines = map(clipText.replace(/\r/g,'').split('\n'), textify); } } var inInternationalComposition = false; - function handleCompositionEvent(evt) { + function handleCompositionEvent(evt) + { // international input events, fired in FF3, at least; allow e.g. Japanese input - if (evt.type == "compositionstart") { + if (evt.type == "compositionstart") + { inInternationalComposition = true; } - else if (evt.type == "compositionend") { + else if (evt.type == "compositionend") + { inInternationalComposition = false; } } - function bindTheEventHandlers() { + function bindTheEventHandlers() + { bindEventHandler(window, "unload", teardown); bindEventHandler(document, "keydown", handleKeyEvent); bindEventHandler(document, "keypress", handleKeyEvent); bindEventHandler(document, "keyup", handleKeyEvent); bindEventHandler(document, "click", handleClick); bindEventHandler(root, "blur", handleBlur); - if (browser.msie) { + if (browser.msie) + { bindEventHandler(document, "click", handleIEOuterClick); } if (browser.msie) bindEventHandler(root, "paste", handleIEPaste); - if ((! browser.msie) && document.documentElement) { + if ((!browser.msie) && document.documentElement) + { bindEventHandler(document.documentElement, "compositionstart", handleCompositionEvent); bindEventHandler(document.documentElement, "compositionend", handleCompositionEvent); } } - function handleIEOuterClick(evt) { - if ((evt.target.tagName||'').toLowerCase() != "html") { + function handleIEOuterClick(evt) + { + if ((evt.target.tagName || '').toLowerCase() != "html") + { return; } - if (!(evt.pageY > root.clientHeight)) { + if (!(evt.pageY > root.clientHeight)) + { return; } // click below the body - inCallStack("handleOuterClick", function() { + inCallStack("handleOuterClick", function() + { // put caret at bottom of doc fastIncorp(11); - if (isCaret()) { // don't interfere with drag - var lastLine = rep.lines.length()-1; - var lastCol = rep.lines.atIndex(lastLine).text.length; - performSelectionChange([lastLine,lastCol],[lastLine,lastCol]); + if (isCaret()) + { // don't interfere with drag + var lastLine = rep.lines.length() - 1; + var lastCol = rep.lines.atIndex(lastLine).text.length; + performSelectionChange([lastLine, lastCol], [lastLine, lastCol]); } }); } - function getClassArray(elem, optFilter) { + function getClassArray(elem, optFilter) + { var bodyClasses = []; - (elem.className || '').replace(/\S+/g, function (c) { - if ((! optFilter) || (optFilter(c))) { - bodyClasses.push(c); + (elem.className || '').replace(/\S+/g, function(c) + { + if ((!optFilter) || (optFilter(c))) + { + bodyClasses.push(c); } }); return bodyClasses; } - function setClassArray(elem, array) { + + function setClassArray(elem, array) + { elem.className = array.join(' '); } - function addClass(elem, className) { + + function addClass(elem, className) + { var seen = false; - var cc = getClassArray(elem, function(c) { if (c == className) seen = true; return true; }); - if (! seen) { + var cc = getClassArray(elem, function(c) + { + if (c == className) seen = true; + return true; + }); + if (!seen) + { cc.push(className); setClassArray(elem, cc); } } - function removeClass(elem, className) { + + function removeClass(elem, className) + { var seen = false; - var cc = getClassArray(elem, function(c) { - if (c == className) { seen = true; return false; } return true; }); - if (seen) { + var cc = getClassArray(elem, function(c) + { + if (c == className) + { + seen = true; + return false; + } + return true; + }); + if (seen) + { setClassArray(elem, cc); } } - function setClassPresence(elem, className, present) { + + function setClassPresence(elem, className, present) + { if (present) addClass(elem, className); else removeClass(elem, className); } - function setup() { + function setup() + { doc = document; // defined as a var in scope outside - inCallStack("setup", function() { + inCallStack("setup", function() + { var body = doc.getElementById("innerdocbody"); root = body; // defined as a var in scope outside - if (browser.mozilla) addClass(root, "mozilla"); if (browser.safari) addClass(root, "safari"); if (browser.msie) addClass(root, "msie"); - if (browser.msie) { - // cache CSS background images - try { - doc.execCommand("BackgroundImageCache", false, true); - } - catch (e) { - /* throws an error in some IE 6 but not others! */ - } + if (browser.msie) + { + // cache CSS background images + try + { + doc.execCommand("BackgroundImageCache", false, true); + } + catch (e) + { /* throws an error in some IE 6 but not others! */ + } } setClassPresence(root, "authorColors", true); setClassPresence(root, "doesWrap", doesWrap); @@ -3944,25 +4950,29 @@ function OUTER(gscope) { doRepLineSplice(0, rep.lines.length(), [oneEntry]); insertDomLines(null, [oneEntry.domInfo], null); rep.alines = Changeset.splitAttributionLines( - Changeset.makeAttribution("\n"), "\n"); + Changeset.makeAttribution("\n"), "\n"); bindTheEventHandlers(); }); - scheduler.setTimeout(function() { + scheduler.setTimeout(function() + { parent.readyFunc(); // defined in code that sets up the inner iframe }, 0); isSetUp = true; } - function focus() { + function focus() + { window.focus(); } - function handleBlur(evt) { - if (browser.msie) { + function handleBlur(evt) + { + if (browser.msie) + { // a fix: in IE, clicking on a control like a button outside the // iframe can "blur" the editor, causing it to stop getting // events, though typing still affects it(!). @@ -3970,60 +4980,73 @@ function OUTER(gscope) { } } - function bindEventHandler(target, type, func) { + function bindEventHandler(target, type, func) + { var handler; - if ((typeof func._wrapper) != "function") { - func._wrapper = function(event) { - func(fixEvent(event || window.event || {})); + if ((typeof func._wrapper) != "function") + { + func._wrapper = function(event) + { + func(fixEvent(event || window.event || {})); } } var handler = func._wrapper; - if (target.addEventListener) - target.addEventListener(type, handler, false); - else - target.attachEvent("on" + type, handler); - _teardownActions.push(function() { + if (target.addEventListener) target.addEventListener(type, handler, false); + else target.attachEvent("on" + type, handler); + _teardownActions.push(function() + { unbindEventHandler(target, type, func); }); } - function unbindEventHandler(target, type, func) { + function unbindEventHandler(target, type, func) + { var handler = func._wrapper; - if (target.removeEventListener) - target.removeEventListener(type, handler, false); - else - target.detachEvent("on" + type, handler); + if (target.removeEventListener) target.removeEventListener(type, handler, false); + else target.detachEvent("on" + type, handler); } - function getSelectionPointX(point) { + function getSelectionPointX(point) + { // doesn't work in wrap-mode var node = point.node; var index = point.index; - function leftOf(n) { return n.offsetLeft; } - function rightOf(n) { return n.offsetLeft + n.offsetWidth; } - if (! isNodeText(node)) { + + function leftOf(n) + { + return n.offsetLeft; + } + + function rightOf(n) + { + return n.offsetLeft + n.offsetWidth; + } + if (!isNodeText(node)) + { if (index == 0) return leftOf(node); else return rightOf(node); } - else { + else + { // we can get bounds of element nodes, so look for those. // allow consecutive text nodes for robustness. var charsToLeft = index; var charsToRight = node.nodeValue.length - index; var n; - for(n = node.previousSibling; n && isNodeText(n); n = n.previousSibling) - charsToLeft += n.nodeValue; + for (n = node.previousSibling; n && isNodeText(n); n = n.previousSibling) + charsToLeft += n.nodeValue; var leftEdge = (n ? rightOf(n) : leftOf(node.parentNode)); - for(n = node.nextSibling; n && isNodeText(n); n = n.nextSibling) - charsToRight += n.nodeValue; + for (n = node.nextSibling; n && isNodeText(n); n = n.nextSibling) + charsToRight += n.nodeValue; var rightEdge = (n ? leftOf(n) : rightOf(node.parentNode)); var frac = (charsToLeft / (charsToLeft + charsToRight)); - var pixLoc = leftEdge + frac*(rightEdge - leftEdge); + var pixLoc = leftEdge + frac * (rightEdge - leftEdge); return Math.round(pixLoc); } } - function getPageHeight() { + function getPageHeight() + { var win = outerWin; var odoc = win.document; if (win.innerHeight && win.scrollMaxY) return win.innerHeight + win.scrollMaxY; @@ -4031,7 +5054,8 @@ function OUTER(gscope) { else return odoc.body.offsetHeight; } - function getPageWidth() { + function getPageWidth() + { var win = outerWin; var odoc = win.document; if (win.innerWidth && win.scrollMaxX) return win.innerWidth + win.scrollMaxX; @@ -4039,7 +5063,8 @@ function OUTER(gscope) { else return odoc.body.offsetWidth; } - function getInnerHeight() { + function getInnerHeight() + { var win = outerWin; var odoc = win.document; var h; @@ -4049,138 +5074,166 @@ function OUTER(gscope) { // deal with case where iframe is hidden, hope that // style.height of iframe container is set in px - return Number(editorInfo.frame.parentNode.style.height.replace(/[^0-9]/g,'') - || 0); + return Number(editorInfo.frame.parentNode.style.height.replace(/[^0-9]/g, '') || 0); } - function getInnerWidth() { + function getInnerWidth() + { var win = outerWin; var odoc = win.document; return odoc.documentElement.clientWidth; } - function scrollNodeVerticallyIntoView(node) { + function scrollNodeVerticallyIntoView(node) + { // requires element (non-text) node; // if node extends above top of viewport or below bottom of viewport (or top of scrollbar), // scroll it the minimum distance needed to be completely in view. var win = outerWin; var odoc = outerWin.document; var distBelowTop = node.offsetTop + iframePadTop - win.scrollY; - var distAboveBottom = win.scrollY + getInnerHeight() - - (node.offsetTop +iframePadTop + node.offsetHeight); + var distAboveBottom = win.scrollY + getInnerHeight() - (node.offsetTop + iframePadTop + node.offsetHeight); - if (distBelowTop < 0) { + if (distBelowTop < 0) + { win.scrollBy(0, distBelowTop); } - else if (distAboveBottom < 0) { + else if (distAboveBottom < 0) + { win.scrollBy(0, -distAboveBottom); } } - function scrollXHorizontallyIntoView(pixelX) { + function scrollXHorizontallyIntoView(pixelX) + { var win = outerWin; var odoc = outerWin.document; pixelX += iframePadLeft; var distInsideLeft = pixelX - win.scrollX; var distInsideRight = win.scrollX + getInnerWidth() - pixelX; - if (distInsideLeft < 0) { + if (distInsideLeft < 0) + { win.scrollBy(distInsideLeft, 0); } - else if (distInsideRight < 0) { - win.scrollBy(-distInsideRight+1, 0); + else if (distInsideRight < 0) + { + win.scrollBy(-distInsideRight + 1, 0); } } - function scrollSelectionIntoView() { - if (! rep.selStart) return; + function scrollSelectionIntoView() + { + if (!rep.selStart) return; fixView(); var focusLine = (rep.selFocusAtStart ? rep.selStart[0] : rep.selEnd[0]); scrollNodeVerticallyIntoView(rep.lines.atIndex(focusLine).lineNode); - if (! doesWrap) { + if (!doesWrap) + { var browserSelection = getSelection(); - if (browserSelection) { - var focusPoint = (browserSelection.focusAtStart ? browserSelection.startPoint : - browserSelection.endPoint); - var selectionPointX = getSelectionPointX(focusPoint); - scrollXHorizontallyIntoView(selectionPointX); - fixView(); + if (browserSelection) + { + var focusPoint = (browserSelection.focusAtStart ? browserSelection.startPoint : browserSelection.endPoint); + var selectionPointX = getSelectionPointX(focusPoint); + scrollXHorizontallyIntoView(selectionPointX); + fixView(); } } } - function getLineListType(lineNum) { + function getLineListType(lineNum) + { // get "list" attribute of first char of line var aline = rep.alines[lineNum]; - if (aline) { + if (aline) + { var opIter = Changeset.opIterator(aline); - if (opIter.hasNext()) { + if (opIter.hasNext()) + { return Changeset.opAttributeValue(opIter.next(), 'list', rep.apool) || ''; } } return ''; } - function setLineListType(lineNum, listType) { - setLineListTypes([[lineNum, listType]]); + function setLineListType(lineNum, listType) + { + setLineListTypes([ + [lineNum, listType] + ]); } - function setLineListTypes(lineNumTypePairsInOrder) { - var loc = [0,0]; + function setLineListTypes(lineNumTypePairsInOrder) + { + var loc = [0, 0]; var builder = Changeset.builder(rep.lines.totalWidth()); - for(var i=0;i<lineNumTypePairsInOrder.length;i++) { + for (var i = 0; i < lineNumTypePairsInOrder.length; i++) + { var pair = lineNumTypePairsInOrder[i]; var lineNum = pair[0]; var listType = pair[1]; - buildKeepRange(builder, loc, (loc = [lineNum,0])); - if (getLineListType(lineNum)) { + buildKeepRange(builder, loc, (loc = [lineNum, 0])); + if (getLineListType(lineNum)) + { // already a line marker - if (listType) { + if (listType) + { // make different list type - buildKeepRange(builder, loc, (loc = [lineNum,1]), - [['list',listType]], rep.apool); + buildKeepRange(builder, loc, (loc = [lineNum, 1]), [ + ['list', listType] + ], rep.apool); } - else { + else + { // remove list marker - buildRemoveRange(builder, loc, (loc = [lineNum,1])); + buildRemoveRange(builder, loc, (loc = [lineNum, 1])); } } - else { + else + { // currently no line marker - if (listType) { + if (listType) + { // add a line marker - builder.insert('*', [['author', thisAuthor], - ['insertorder', 'first'], - ['list', listType]], rep.apool); + builder.insert('*', [ + ['author', thisAuthor], + ['insertorder', 'first'], + ['list', listType] + ], rep.apool); } } } var cs = builder.toString(); - if (! Changeset.isIdentity(cs)) { + if (!Changeset.isIdentity(cs)) + { performDocumentApplyChangeset(cs); } } - function doInsertUnorderedList() { - if (! (rep.selStart && rep.selEnd)) { + function doInsertUnorderedList() + { + if (!(rep.selStart && rep.selEnd)) + { return; } var firstLine, lastLine; firstLine = rep.selStart[0]; - lastLine = Math.max(firstLine, - rep.selEnd[0] - ((rep.selEnd[1] == 0) ? 1 : 0)); + lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] == 0) ? 1 : 0)); var allLinesAreList = true; - for(var n=firstLine;n<=lastLine;n++) { - if (! getLineListType(n)) { + for (var n = firstLine; n <= lastLine; n++) + { + if (!getLineListType(n)) + { allLinesAreList = false; break; } } var mods = []; - for(var n=firstLine;n<=lastLine;n++) { + for (var n = firstLine; n <= lastLine; n++) + { var t = getLineListType(n); mods.push([n, allLinesAreList ? '' : (t ? t : 'bullet1')]); } @@ -4188,7 +5241,8 @@ function OUTER(gscope) { } editorInfo.ace_doInsertUnorderedList = doInsertUnorderedList; - var mozillaFakeArrows = (browser.mozilla && (function() { + var mozillaFakeArrows = (browser.mozilla && (function() + { // In Firefox 2, arrow keys are unstable while DOM-manipulating // operations are going on. Specifically, if an operation // (computation that ties up the event queue) is going on (in the @@ -4198,7 +5252,6 @@ function OUTER(gscope) { // of the document. If the operation also mutates the selection // range, the old selection or the new selection may be used, or // neither. - // As long as the arrow is pressed during the busy operation, it // doesn't seem to matter that the keydown and keypress events // aren't generated until afterwards, or that the arrow movement @@ -4213,15 +5266,12 @@ function OUTER(gscope) { // pattern. Who knows, it could have something to do with the // caret-blinking timer, or DOM changes not being applied // immediately. - // This problem, mercifully, does not show up at all in IE or // Safari. My solution is to have my own, full-featured arrow-key // implementation for Firefox. - // Note that the problem addressed here is potentially very subtle, // especially if the operation is quick and is timed to usually happen // when the user is idle. - // features: // - 'up' and 'down' arrows preserve column when passing through shorter lines // - shift-arrows extend the "focus" point, which may be start or end of range @@ -4232,354 +5282,406 @@ function OUTER(gscope) { // - takes wrapping into account when doesWrap is true, i.e. up-arrow and down-arrow move // between the virtual lines within a wrapped line; this was difficult, and unfortunately // requires mutating the DOM to get the necessary information - var savedFocusColumn = 0; // a value of 0 has no effect var updatingSelectionNow = false; - function getVirtualLineView(lineNum) { + function getVirtualLineView(lineNum) + { var lineNode = rep.lines.atIndex(lineNum).lineNode; - while (lineNode.firstChild && isBlockElement(lineNode.firstChild)) { + while (lineNode.firstChild && isBlockElement(lineNode.firstChild)) + { lineNode = lineNode.firstChild; } return makeVirtualLineView(lineNode); } - function markerlessLineAndChar(line, chr) { + function markerlessLineAndChar(line, chr) + { return [line, chr - rep.lines.atIndex(line).lineMarker]; } - function markerfulLineAndChar(line, chr) { + + function markerfulLineAndChar(line, chr) + { return [line, chr + rep.lines.atIndex(line).lineMarker]; } return { - notifySelectionChanged: function() { - if (! updatingSelectionNow) { - savedFocusColumn = 0; - } + notifySelectionChanged: function() + { + if (!updatingSelectionNow) + { + savedFocusColumn = 0; + } }, - handleKeyEvent: function(evt) { - // returns "true" if handled - if (evt.type != "keypress") return false; - var keyCode = evt.keyCode; - if (keyCode < 37 || keyCode > 40) return false; - incorporateUserChanges(); - - if (!(rep.selStart && rep.selEnd)) return true; - - // {byWord,toEnd,normal} - var moveMode = (evt.altKey ? "byWord" : - (evt.ctrlKey ? "byWord" : - (evt.metaKey ? "toEnd" : - "normal"))); - - var anchorCaret = - markerlessLineAndChar(rep.selStart[0], rep.selStart[1]); - var focusCaret = - markerlessLineAndChar(rep.selEnd[0], rep.selEnd[1]); - var wasCaret = isCaret(); - if (rep.selFocusAtStart) { - var tmp = anchorCaret; anchorCaret = focusCaret; focusCaret = tmp; - } - var K_UP = 38, K_DOWN = 40, K_LEFT = 37, K_RIGHT = 39; - var dontMove = false; - if (wasCaret && ! evt.shiftKey) { - // collapse, will mutate both together - anchorCaret = focusCaret; - } - else if ((! wasCaret) && (! evt.shiftKey)) { - if (keyCode == K_LEFT) { - // place caret at beginning - if (rep.selFocusAtStart) anchorCaret = focusCaret; - else focusCaret = anchorCaret; - if (moveMode == "normal") dontMove = true; - } - else if (keyCode == K_RIGHT) { - // place caret at end - if (rep.selFocusAtStart) focusCaret = anchorCaret; - else anchorCaret = focusCaret; - if (moveMode == "normal") dontMove = true; - } - else { - // collapse, will mutate both together - anchorCaret = focusCaret; - } - } - if (! dontMove) { - function lineLength(i) { + handleKeyEvent: function(evt) + { + // returns "true" if handled + if (evt.type != "keypress") return false; + var keyCode = evt.keyCode; + if (keyCode < 37 || keyCode > 40) return false; + incorporateUserChanges(); + + if (!(rep.selStart && rep.selEnd)) return true; + + // {byWord,toEnd,normal} + var moveMode = (evt.altKey ? "byWord" : (evt.ctrlKey ? "byWord" : (evt.metaKey ? "toEnd" : "normal"))); + + var anchorCaret = markerlessLineAndChar(rep.selStart[0], rep.selStart[1]); + var focusCaret = markerlessLineAndChar(rep.selEnd[0], rep.selEnd[1]); + var wasCaret = isCaret(); + if (rep.selFocusAtStart) + { + var tmp = anchorCaret; + anchorCaret = focusCaret; + focusCaret = tmp; + } + var K_UP = 38, + K_DOWN = 40, + K_LEFT = 37, + K_RIGHT = 39; + var dontMove = false; + if (wasCaret && !evt.shiftKey) + { + // collapse, will mutate both together + anchorCaret = focusCaret; + } + else if ((!wasCaret) && (!evt.shiftKey)) + { + if (keyCode == K_LEFT) + { + // place caret at beginning + if (rep.selFocusAtStart) anchorCaret = focusCaret; + else focusCaret = anchorCaret; + if (moveMode == "normal") dontMove = true; + } + else if (keyCode == K_RIGHT) + { + // place caret at end + if (rep.selFocusAtStart) focusCaret = anchorCaret; + else anchorCaret = focusCaret; + if (moveMode == "normal") dontMove = true; + } + else + { + // collapse, will mutate both together + anchorCaret = focusCaret; + } + } + if (!dontMove) + { + function lineLength(i) + { var entry = rep.lines.atIndex(i); return entry.text.length - entry.lineMarker; } - function lineText(i) { + + function lineText(i) + { var entry = rep.lines.atIndex(i); return entry.text.substring(entry.lineMarker); } - if (keyCode == K_UP || keyCode == K_DOWN) { - var up = (keyCode == K_UP); - var canChangeLines = ((up && focusCaret[0]) || - ((!up) && focusCaret[0] < rep.lines.length()-1)); - var virtualLineView, virtualLineSpot, canChangeVirtualLines = false; - if (doesWrap) { - virtualLineView = getVirtualLineView(focusCaret[0]); - virtualLineSpot = virtualLineView.getVLineAndOffsetForChar(focusCaret[1]); - canChangeVirtualLines = ((up && virtualLineSpot.vline > 0) || - ((!up) && virtualLineSpot.vline < ( - virtualLineView.getNumVirtualLines() - 1))); - } - var newColByVirtualLineChange; - if (moveMode == "toEnd") { - if (up) { - focusCaret[0] = 0; - focusCaret[1] = 0; - } - else { - focusCaret[0] = rep.lines.length()-1; - focusCaret[1] = lineLength(focusCaret[0]); - } - } - else if (moveMode == "byWord") { - // move by "paragraph", a feature that Firefox lacks but IE and Safari both have - if (up) { - if (focusCaret[1] == 0 && canChangeLines) { - focusCaret[0]--; - focusCaret[1] = 0; - } - else focusCaret[1] = 0; - } - else { - var lineLen = lineLength(focusCaret[0]); - if (browser.windows) { - if (canChangeLines) { - focusCaret[0]++; - focusCaret[1] = 0; - } - else { - focusCaret[1] = lineLen; - } - } - else { - if (focusCaret[1] == lineLen && canChangeLines) { - focusCaret[0]++; - focusCaret[1] = lineLength(focusCaret[0]); - } - else { - focusCaret[1] = lineLen; - } - } - } - savedFocusColumn = 0; - } - else if (canChangeVirtualLines) { - var vline = virtualLineSpot.vline; - var offset = virtualLineSpot.offset; - if (up) vline--; - else vline++; - if (savedFocusColumn > offset) offset = savedFocusColumn; - else { - savedFocusColumn = offset; - } - var newSpot = virtualLineView.getCharForVLineAndOffset(vline, offset); - focusCaret[1] = newSpot.lineChar; - } - else if (canChangeLines) { - if (up) focusCaret[0]--; - else focusCaret[0]++; - var offset = focusCaret[1]; - if (doesWrap) { - offset = virtualLineSpot.offset; - } - if (savedFocusColumn > offset) offset = savedFocusColumn; - else { - savedFocusColumn = offset; - } - if (doesWrap) { - var newLineView = getVirtualLineView(focusCaret[0]); - var vline = (up ? newLineView.getNumVirtualLines()-1 : 0); - var newSpot = newLineView.getCharForVLineAndOffset(vline, offset); - focusCaret[1] = newSpot.lineChar; - } - else { - var lineLen = lineLength(focusCaret[0]); - if (offset > lineLen) offset = lineLen; - focusCaret[1] = offset; - } - } - else { - if (up) focusCaret[1] = 0; - else focusCaret[1] = lineLength(focusCaret[0]); - savedFocusColumn = 0; - } - } - else if (keyCode == K_LEFT || keyCode == K_RIGHT) { - var left = (keyCode == K_LEFT); - if (left) { - if (moveMode == "toEnd") focusCaret[1] = 0; - else if (focusCaret[1] > 0) { - if (moveMode == "byWord") { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false); - } - else { - focusCaret[1]--; - } - } - else if (focusCaret[0] > 0) { - focusCaret[0]--; - focusCaret[1] = lineLength(focusCaret[0]); - if (moveMode == "byWord") { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false); - } - } - } - else { - var lineLen = lineLength(focusCaret[0]); - if (moveMode == "toEnd") focusCaret[1] = lineLen; - else if (focusCaret[1] < lineLen) { - if (moveMode == "byWord") { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true); - } - else { - focusCaret[1]++; - } - } - else if (focusCaret[0] < rep.lines.length()-1) { - focusCaret[0]++; - focusCaret[1] = 0; - if (moveMode == "byWord") { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true); - } - } - } - savedFocusColumn = 0; - } - } - - var newSelFocusAtStart = ((focusCaret[0] < anchorCaret[0]) || - (focusCaret[0] == anchorCaret[0] && - focusCaret[1] < anchorCaret[1])); - var newSelStart = (newSelFocusAtStart ? focusCaret : anchorCaret); - var newSelEnd = (newSelFocusAtStart ? anchorCaret : focusCaret); - updatingSelectionNow = true; - performSelectionChange(markerfulLineAndChar(newSelStart[0], - newSelStart[1]), - markerfulLineAndChar(newSelEnd[0], - newSelEnd[1]), - newSelFocusAtStart); - updatingSelectionNow = false; - currentCallStack.userChangedSelection = true; - return true; + if (keyCode == K_UP || keyCode == K_DOWN) + { + var up = (keyCode == K_UP); + var canChangeLines = ((up && focusCaret[0]) || ((!up) && focusCaret[0] < rep.lines.length() - 1)); + var virtualLineView, virtualLineSpot, canChangeVirtualLines = false; + if (doesWrap) + { + virtualLineView = getVirtualLineView(focusCaret[0]); + virtualLineSpot = virtualLineView.getVLineAndOffsetForChar(focusCaret[1]); + canChangeVirtualLines = ((up && virtualLineSpot.vline > 0) || ((!up) && virtualLineSpot.vline < ( + virtualLineView.getNumVirtualLines() - 1))); + } + var newColByVirtualLineChange; + if (moveMode == "toEnd") + { + if (up) + { + focusCaret[0] = 0; + focusCaret[1] = 0; + } + else + { + focusCaret[0] = rep.lines.length() - 1; + focusCaret[1] = lineLength(focusCaret[0]); + } + } + else if (moveMode == "byWord") + { + // move by "paragraph", a feature that Firefox lacks but IE and Safari both have + if (up) + { + if (focusCaret[1] == 0 && canChangeLines) + { + focusCaret[0]--; + focusCaret[1] = 0; + } + else focusCaret[1] = 0; + } + else + { + var lineLen = lineLength(focusCaret[0]); + if (browser.windows) + { + if (canChangeLines) + { + focusCaret[0]++; + focusCaret[1] = 0; + } + else + { + focusCaret[1] = lineLen; + } + } + else + { + if (focusCaret[1] == lineLen && canChangeLines) + { + focusCaret[0]++; + focusCaret[1] = lineLength(focusCaret[0]); + } + else + { + focusCaret[1] = lineLen; + } + } + } + savedFocusColumn = 0; + } + else if (canChangeVirtualLines) + { + var vline = virtualLineSpot.vline; + var offset = virtualLineSpot.offset; + if (up) vline--; + else vline++; + if (savedFocusColumn > offset) offset = savedFocusColumn; + else + { + savedFocusColumn = offset; + } + var newSpot = virtualLineView.getCharForVLineAndOffset(vline, offset); + focusCaret[1] = newSpot.lineChar; + } + else if (canChangeLines) + { + if (up) focusCaret[0]--; + else focusCaret[0]++; + var offset = focusCaret[1]; + if (doesWrap) + { + offset = virtualLineSpot.offset; + } + if (savedFocusColumn > offset) offset = savedFocusColumn; + else + { + savedFocusColumn = offset; + } + if (doesWrap) + { + var newLineView = getVirtualLineView(focusCaret[0]); + var vline = (up ? newLineView.getNumVirtualLines() - 1 : 0); + var newSpot = newLineView.getCharForVLineAndOffset(vline, offset); + focusCaret[1] = newSpot.lineChar; + } + else + { + var lineLen = lineLength(focusCaret[0]); + if (offset > lineLen) offset = lineLen; + focusCaret[1] = offset; + } + } + else + { + if (up) focusCaret[1] = 0; + else focusCaret[1] = lineLength(focusCaret[0]); + savedFocusColumn = 0; + } + } + else if (keyCode == K_LEFT || keyCode == K_RIGHT) + { + var left = (keyCode == K_LEFT); + if (left) + { + if (moveMode == "toEnd") focusCaret[1] = 0; + else if (focusCaret[1] > 0) + { + if (moveMode == "byWord") + { + focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false); + } + else + { + focusCaret[1]--; + } + } + else if (focusCaret[0] > 0) + { + focusCaret[0]--; + focusCaret[1] = lineLength(focusCaret[0]); + if (moveMode == "byWord") + { + focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false); + } + } + } + else + { + var lineLen = lineLength(focusCaret[0]); + if (moveMode == "toEnd") focusCaret[1] = lineLen; + else if (focusCaret[1] < lineLen) + { + if (moveMode == "byWord") + { + focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true); + } + else + { + focusCaret[1]++; + } + } + else if (focusCaret[0] < rep.lines.length() - 1) + { + focusCaret[0]++; + focusCaret[1] = 0; + if (moveMode == "byWord") + { + focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true); + } + } + } + savedFocusColumn = 0; + } + } + + var newSelFocusAtStart = ((focusCaret[0] < anchorCaret[0]) || (focusCaret[0] == anchorCaret[0] && focusCaret[1] < anchorCaret[1])); + var newSelStart = (newSelFocusAtStart ? focusCaret : anchorCaret); + var newSelEnd = (newSelFocusAtStart ? anchorCaret : focusCaret); + updatingSelectionNow = true; + performSelectionChange(markerfulLineAndChar(newSelStart[0], newSelStart[1]), markerfulLineAndChar(newSelEnd[0], newSelEnd[1]), newSelFocusAtStart); + updatingSelectionNow = false; + currentCallStack.userChangedSelection = true; + return true; } }; })()); // stolen from jquery-1.2.1 - function fixEvent(event) { + + + function fixEvent(event) + { // store a copy of the original event object // and clone to set read-only properties var originalEvent = event; - event = extend({}, originalEvent); + event = extend( + {}, originalEvent); // add preventDefault and stopPropagation since // they will not work on the clone - event.preventDefault = function() { + event.preventDefault = function() + { // if preventDefault exists run it on the original event - if (originalEvent.preventDefault) - originalEvent.preventDefault(); + if (originalEvent.preventDefault) originalEvent.preventDefault(); // otherwise set the returnValue property of the original event to false (IE) originalEvent.returnValue = false; }; - event.stopPropagation = function() { + event.stopPropagation = function() + { // if stopPropagation exists run it on the original event - if (originalEvent.stopPropagation) - originalEvent.stopPropagation(); + if (originalEvent.stopPropagation) originalEvent.stopPropagation(); // otherwise set the cancelBubble property of the original event to true (IE) originalEvent.cancelBubble = true; }; // Fix target property, if necessary - if ( !event.target && event.srcElement ) - event.target = event.srcElement; + if (!event.target && event.srcElement) event.target = event.srcElement; // check if target is a textnode (safari) - if (browser.safari && event.target.nodeType == 3) - event.target = originalEvent.target.parentNode; + if (browser.safari && event.target.nodeType == 3) event.target = originalEvent.target.parentNode; // Add relatedTarget, if necessary - if ( !event.relatedTarget && event.fromElement ) - event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement; + if (!event.relatedTarget && event.fromElement) event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement; // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && event.clientX != null ) { - var e = document.documentElement, b = document.body; + if (event.pageX == null && event.clientX != null) + { + var e = document.documentElement, + b = document.body; event.pageX = event.clientX + (e && e.scrollLeft || b.scrollLeft || 0); event.pageY = event.clientY + (e && e.scrollTop || b.scrollTop || 0); } // Add which for key events - if ( !event.which && (event.charCode || event.keyCode) ) - event.which = event.charCode || event.keyCode; + if (!event.which && (event.charCode || event.keyCode)) event.which = event.charCode || event.keyCode; // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) - if ( !event.metaKey && event.ctrlKey ) - event.metaKey = event.ctrlKey; + if (!event.metaKey && event.ctrlKey) event.metaKey = event.ctrlKey; // Add which for click: 1 == left; 2 == middle; 3 == right // Note: button is not normalized, so don't use it - if ( !event.which && event.button ) - event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + if (!event.which && event.button) event.which = (event.button & 1 ? 1 : (event.button & 2 ? 3 : (event.button & 4 ? 2 : 0))); return event; } var lineNumbersShown; var sideDivInner; - function initLineNumbers() { + + function initLineNumbers() + { lineNumbersShown = 1; - sideDiv.innerHTML = - '<table border="0" cellpadding="0" cellspacing="0" align="right">'+ - '<tr><td id="sidedivinner"><div>1</div></td></tr></table>'; + sideDiv.innerHTML = '<table border="0" cellpadding="0" cellspacing="0" align="right">' + '<tr><td id="sidedivinner"><div>1</div></td></tr></table>'; sideDivInner = outerWin.document.getElementById("sidedivinner"); } - function updateLineNumbers() { + function updateLineNumbers() + { var newNumLines = rep.lines.length(); if (newNumLines < 1) newNumLines = 1; - if (newNumLines != lineNumbersShown) { + if (newNumLines != lineNumbersShown) + { var container = sideDivInner; var odoc = outerWin.document; - while (lineNumbersShown < newNumLines) { - lineNumbersShown++; - var n = lineNumbersShown; - var div = odoc.createElement("DIV"); - div.appendChild(odoc.createTextNode(String(n))); - container.appendChild(div); + while (lineNumbersShown < newNumLines) + { + lineNumbersShown++; + var n = lineNumbersShown; + var div = odoc.createElement("DIV"); + div.appendChild(odoc.createTextNode(String(n))); + container.appendChild(div); } - while (lineNumbersShown > newNumLines) { - container.removeChild(container.lastChild); - lineNumbersShown--; + while (lineNumbersShown > newNumLines) + { + container.removeChild(container.lastChild); + lineNumbersShown--; } } - if (currentCallStack && currentCallStack.domClean) { + if (currentCallStack && currentCallStack.domClean) + { var a = sideDivInner.firstChild; var b = doc.body.firstChild; - while (a && b) { - var h = (b.clientHeight || b.offsetHeight); - if (b.nextSibling) { - // when text is zoomed in mozilla, divs have fractional - // heights (though the properties are always integers) - // and the line-numbers don't line up unless we pay - // attention to where the divs are actually placed... - // (also: padding on TTs/SPANs in IE...) - h = b.nextSibling.offsetTop - b.offsetTop; - } - if (h) { - var hpx = h+"px"; - if (a.style.height != hpx) - a.style.height = hpx; - } - a = a.nextSibling; - b = b.nextSibling; + while (a && b) + { + var h = (b.clientHeight || b.offsetHeight); + if (b.nextSibling) + { + // when text is zoomed in mozilla, divs have fractional + // heights (though the properties are always integers) + // and the line-numbers don't line up unless we pay + // attention to where the divs are actually placed... + // (also: padding on TTs/SPANs in IE...) + h = b.nextSibling.offsetTop - b.offsetTop; + } + if (h) + { + var hpx = h + "px"; + if (a.style.height != hpx) a.style.height = hpx; + } + a = a.nextSibling; + b = b.nextSibling; } } } diff --git a/static/js/broadcast.js b/static/js/broadcast.js index 9b25e3b8..c29c2447 100644 --- a/static/js/broadcast.js +++ b/static/js/broadcast.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. @@ -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) - { - 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); + 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); }, - 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++) { diff --git a/static/js/broadcast_revisions.js b/static/js/broadcast_revisions.js index 0fb8061c..82867773 100644 --- a/static/js/broadcast_revisions.js +++ b/static/js/broadcast_revisions.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. @@ -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 = []; diff --git a/static/js/broadcast_slider.js b/static/js/broadcast_slider.js index 3a645c9d..68f42449 100644 --- a/static/js/broadcast_slider.js +++ b/static/js/broadcast_slider.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,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"); }) diff --git a/static/js/changesettracker.js b/static/js/changesettracker.js index d6fe018c..09e413f3 100644 --- a/static/js/changesettracker.js +++ b/static/js/changesettracker.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. @@ -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))); } }; diff --git a/static/js/collab_client.js b/static/js/collab_client.js index 2e336a74..1abc8524 100644 --- a/static/js/collab_client.js +++ b/static/js/collab_client.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. @@ -14,14 +14,16 @@ * limitations under the License. */ -$(window).bind("load", function() { +$(window).bind("load", function() +{ getCollabClient.windowLoaded = true; }); /** Call this when the document is ready, and a new Ace2Editor() has been created and inited. ACE's ready callback does not need to have fired yet. "serverVars" are from calling doc.getCollabClientVars() on the server. */ -function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { +function getCollabClient(ace2editor, serverVars, initialUserInfo, options) +{ var editor = ace2editor; var rev = serverVars.rev; @@ -53,133 +55,161 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { tellAceActiveAuthorInfo(initialUserInfo); var callbacks = { - onUserJoin: function() {}, - onUserLeave: function() {}, - onUpdateUserInfo: function() {}, - onChannelStateChange: function() {}, - onClientMessage: function() {}, - onInternalAction: function() {}, - onConnectionTrouble: function() {}, - onServerMessage: function() {} + onUserJoin: function() + {}, + onUserLeave: function() + {}, + onUpdateUserInfo: function() + {}, + onChannelStateChange: function() + {}, + onClientMessage: function() + {}, + onInternalAction: function() + {}, + onConnectionTrouble: function() + {}, + onServerMessage: function() + {} }; - $(window).bind("unload", function() { - if (socket) { - /*socket.onclosed = function() {}; + $(window).bind("unload", function() + { + if (socket) + { +/*socket.onclosed = function() {}; socket.onhiccup = function() {}; socket.disconnect(true);*/ socket.disconnect(); } }); - if ($.browser.mozilla) { + if ($.browser.mozilla) + { // Prevent "escape" from taking effect and canceling a comet connection; // doesn't work if focus is on an iframe. - $(window).bind("keydown", function(evt) { if (evt.which == 27) { evt.preventDefault() } }); + $(window).bind("keydown", function(evt) + { + if (evt.which == 27) + { + evt.preventDefault() + } + }); } editor.setProperty("userAuthor", userId); editor.setBaseAttributedText(serverVars.initialAttributedText, serverVars.apool); editor.setUserChangeNotificationCallback(wrapRecordingErrors("handleUserChanges", handleUserChanges)); - function abandonConnection(reason) { - if (socket) { - /*socket.onclosed = function() {}; + function abandonConnection(reason) + { + if (socket) + { +/*socket.onclosed = function() {}; socket.onhiccup = function() {};*/ - socket.disconnect(); + socket.disconnect(); } socket = null; setChannelState("DISCONNECTED", reason); } - function dmesg(str) { - if (typeof window.ajlog == "string") window.ajlog += str+'\n'; + function dmesg(str) + { + if (typeof window.ajlog == "string") window.ajlog += str + '\n'; debugMessages.push(str); } - function handleUserChanges() { - if ((! socket) || channelState == "CONNECTING") { - if (channelState == "CONNECTING" && (((+new Date()) - initialStartConnectTime) > 20000)) { + function handleUserChanges() + { + if ((!socket) || channelState == "CONNECTING") + { + if (channelState == "CONNECTING" && (((+new Date()) - initialStartConnectTime) > 20000)) + { abandonConnection("initsocketfail"); // give up } - else { + else + { // check again in a bit - setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), - 1000); + setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), 1000); } return; } var t = (+new Date()); - if (state != "IDLE") { - if (state == "COMMITTING" && (t - lastCommitTime) > 20000) { + if (state != "IDLE") + { + if (state == "COMMITTING" && (t - lastCommitTime) > 20000) + { // a commit is taking too long appLevelDisconnectReason = "slowcommit"; socket.disconnect(); } - else if (state == "COMMITTING" && (t - lastCommitTime) > 5000) { + else if (state == "COMMITTING" && (t - lastCommitTime) > 5000) + { callbacks.onConnectionTrouble("SLOW"); } - else { + else + { // run again in a few seconds, to detect a disconnect - setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), - 3000); + setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), 3000); } return; } var earliestCommit = lastCommitTime + 500; - if (t < earliestCommit) { - setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), - earliestCommit - t); + if (t < earliestCommit) + { + setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), earliestCommit - t); return; } var sentMessage = false; var userChangesData = editor.prepareUserChangeset(); - if (userChangesData.changeset) { + if (userChangesData.changeset) + { lastCommitTime = t; state = "COMMITTING"; - stateMessage = {type:"USER_CHANGES", baseRev:rev, - changeset:userChangesData.changeset, - apool: userChangesData.apool }; + stateMessage = { + type: "USER_CHANGES", + baseRev: rev, + changeset: userChangesData.changeset, + apool: userChangesData.apool + }; stateMessageSocketId = socketId; sendMessage(stateMessage); sentMessage = true; callbacks.onInternalAction("commitPerformed"); } - if (sentMessage) { + if (sentMessage) + { // run again in a few seconds, to detect a disconnect - setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), - 3000); + setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), 3000); } } - function getStats() { + function getStats() + { var stats = {}; - stats.screen = [$(window).width(), $(window).height(), - window.screen.availWidth, window.screen.availHeight, - window.screen.width, window.screen.height].join(','); + stats.screen = [$(window).width(), $(window).height(), window.screen.availWidth, window.screen.availHeight, window.screen.width, window.screen.height].join(','); stats.ip = serverVars.clientIp; stats.useragent = serverVars.clientAgent; return stats; } - function setUpSocket() + function setUpSocket() { //oldSocketId = String(Math.floor(Math.random()*1e12)); //socketId = String(Math.floor(Math.random()*1e12)); - - /*socket = new io.Socket(); +/*socket = new io.Socket(); socket.connect();*/ - + //socket.on('connect', function(){ - hiccupCount = 0; - setChannelState("CONNECTED"); - /*var msg = { type:"CLIENT_READY", roomType:'padpage', + hiccupCount = 0; + setChannelState("CONNECTED"); +/*var msg = { type:"CLIENT_READY", roomType:'padpage', roomName:'padpage/'+globalPadId, data: { lastRev:rev, @@ -190,22 +220,22 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { msg.data.isCommitPending = (state == "COMMITTING"); } sendMessage(msg);*/ - doDeferredActions(); - - initialStartConnectTime = +new Date(); - // }); - - /*socket.on('message', function(obj){ + doDeferredActions(); + + initialStartConnectTime = +new Date(); + // }); +/*socket.on('message', function(obj){ if(window.console) console.log(obj); handleMessageFromServer(obj); });*/ - - socket.on('disconnect', function(obj){ + + socket.on('disconnect', function(obj) + { handleSocketClosed(true); }); - - /*var success = false; + +/*var success = false; callCatchingErrors("setUpSocket", function() { appLevelDisconnectReason = null; @@ -242,43 +272,62 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { abandonConnection("initsocketfail"); }*/ } - function setUpSocketWhenWindowLoaded() { - if (getCollabClient.windowLoaded) { + + function setUpSocketWhenWindowLoaded() + { + if (getCollabClient.windowLoaded) + { setUpSocket(); } - else { + else + { setTimeout(setUpSocketWhenWindowLoaded, 200); } } setTimeout(setUpSocketWhenWindowLoaded, 0); var hiccupCount = 0; - function handleCometHiccup(params) { - dmesg("HICCUP (connected:"+(!!params.connected)+")"); + + function handleCometHiccup(params) + { + dmesg("HICCUP (connected:" + ( !! params.connected) + ")"); var connectedNow = params.connected; - if (! connectedNow) { + if (!connectedNow) + { hiccupCount++; // skip first "cut off from server" notification - if (hiccupCount > 1) { + if (hiccupCount > 1) + { setChannelState("RECONNECTING"); } } - else { + else + { hiccupCount = 0; setChannelState("CONNECTED"); } } - function sendMessage(msg) { - socket.json.send({type: "COLLABROOM", component: "pad", data: msg}); + function sendMessage(msg) + { + socket.json.send( + { + type: "COLLABROOM", + component: "pad", + data: msg + }); } - function wrapRecordingErrors(catcher, func) { - return function() { - try { + function wrapRecordingErrors(catcher, func) + { + return function() + { + try + { return func.apply(this, Array.prototype.slice.call(arguments)); } - catch (e) { + catch (e) + { caughtErrors.push(e); caughtErrorCatchers.push(catcher); caughtErrorTimes.push(+new Date()); @@ -288,147 +337,195 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { }; } - function callCatchingErrors(catcher, func) { - try { + function callCatchingErrors(catcher, func) + { + try + { wrapRecordingErrors(catcher, func)(); } - catch (e) { /*absorb*/ } + catch (e) + { /*absorb*/ + } } - function handleMessageFromServer(evt) { - if(window.console) - console.log(evt); - - if (! socket) return; - if (! evt.data) return; + function handleMessageFromServer(evt) + { + if (window.console) console.log(evt); + + if (!socket) return; + if (!evt.data) return; var wrapper = evt; - if(wrapper.type != "COLLABROOM") return; + if (wrapper.type != "COLLABROOM") return; var msg = wrapper.data; - if (msg.type == "NEW_CHANGES") { + if (msg.type == "NEW_CHANGES") + { var newRev = msg.newRev; var changeset = msg.changeset; var author = (msg.author || ''); var apool = msg.apool; - if (newRev != (rev+1)) { - dmesg("bad message revision on NEW_CHANGES: "+newRev+" not "+(rev+1)); + if (newRev != (rev + 1)) + { + dmesg("bad message revision on NEW_CHANGES: " + newRev + " not " + (rev + 1)); socket.disconnect(); return; } rev = newRev; editor.applyChangesToBase(changeset, author, apool); } - else if (msg.type == "ACCEPT_COMMIT") { + else if (msg.type == "ACCEPT_COMMIT") + { var newRev = msg.newRev; - if (newRev != (rev+1)) { - dmesg("bad message revision on ACCEPT_COMMIT: "+newRev+" not "+(rev+1)); + if (newRev != (rev + 1)) + { + dmesg("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (rev + 1)); socket.disconnect(); return; } rev = newRev; editor.applyPreparedChangesetToBase(); setStateIdle(); - callCatchingErrors("onInternalAction", function() { + callCatchingErrors("onInternalAction", function() + { callbacks.onInternalAction("commitAcceptedByServer"); }); - callCatchingErrors("onConnectionTrouble", function() { + callCatchingErrors("onConnectionTrouble", function() + { callbacks.onConnectionTrouble("OK"); }); handleUserChanges(); } - else if (msg.type == "NO_COMMIT_PENDING") { - if (state == "COMMITTING") { + else if (msg.type == "NO_COMMIT_PENDING") + { + if (state == "COMMITTING") + { // server missed our commit message; abort that commit setStateIdle(); handleUserChanges(); } } - else if (msg.type == "USER_NEWINFO") { + else if (msg.type == "USER_NEWINFO") + { var userInfo = msg.userInfo; var id = userInfo.userId; - if (userSet[id]) { + if (userSet[id]) + { userSet[id] = userInfo; callbacks.onUpdateUserInfo(userInfo); dmesgUsers(); } - else { + else + { userSet[id] = userInfo; callbacks.onUserJoin(userInfo); dmesgUsers(); } tellAceActiveAuthorInfo(userInfo); } - else if (msg.type == "USER_LEAVE") { + else if (msg.type == "USER_LEAVE") + { var userInfo = msg.userInfo; var id = userInfo.userId; - if (userSet[id]) { + if (userSet[id]) + { delete userSet[userInfo.userId]; fadeAceAuthorInfo(userInfo); callbacks.onUserLeave(userInfo); dmesgUsers(); } } - else if (msg.type == "DISCONNECT_REASON") { + else if (msg.type == "DISCONNECT_REASON") + { appLevelDisconnectReason = msg.reason; } - else if (msg.type == "CLIENT_MESSAGE") { + else if (msg.type == "CLIENT_MESSAGE") + { callbacks.onClientMessage(msg.payload); } - else if (msg.type == "SERVER_MESSAGE") { + else if (msg.type == "SERVER_MESSAGE") + { callbacks.onServerMessage(msg.payload); } } - function updateUserInfo(userInfo) { + + function updateUserInfo(userInfo) + { userInfo.userId = userId; userSet[userId] = userInfo; tellAceActiveAuthorInfo(userInfo); - if (! socket) return; - sendMessage({type: "USERINFO_UPDATE", userInfo:userInfo}); + if (!socket) return; + sendMessage( + { + type: "USERINFO_UPDATE", + userInfo: userInfo + }); } - function tellAceActiveAuthorInfo(userInfo) { + function tellAceActiveAuthorInfo(userInfo) + { tellAceAuthorInfo(userInfo.userId, userInfo.colorId); } - function tellAceAuthorInfo(userId, colorId, inactive) { - if (colorId || (typeof colorId) == "number") { + + function tellAceAuthorInfo(userId, colorId, inactive) + { + if (colorId || (typeof colorId) == "number") + { colorId = Number(colorId); - if (options && options.colorPalette && options.colorPalette[colorId]) { + if (options && options.colorPalette && options.colorPalette[colorId]) + { var cssColor = options.colorPalette[colorId]; - if (inactive) { - editor.setAuthorInfo(userId, {bgcolor: cssColor, fade: 0.5}); + if (inactive) + { + editor.setAuthorInfo(userId, { + bgcolor: cssColor, + fade: 0.5 + }); } - else { - editor.setAuthorInfo(userId, {bgcolor: cssColor}); + else + { + editor.setAuthorInfo(userId, { + bgcolor: cssColor + }); } } } } - function fadeAceAuthorInfo(userInfo) { + + function fadeAceAuthorInfo(userInfo) + { tellAceAuthorInfo(userInfo.userId, userInfo.colorId, true); } - function getConnectedUsers() { + function getConnectedUsers() + { return valuesArray(userSet); } - function tellAceAboutHistoricalAuthors(hadata) { - for(var author in hadata) { + function tellAceAboutHistoricalAuthors(hadata) + { + for (var author in hadata) + { var data = hadata[author]; - if (! userSet[author]) { + if (!userSet[author]) + { tellAceAuthorInfo(author, data.colorId, true); } } } - function dmesgUsers() { + function dmesgUsers() + { //pad.dmesg($.map(getConnectedUsers(), function(u) { return u.userId.slice(-2); }).join(',')); } - function handleSocketClosed(params) { + function handleSocketClosed(params) + { socket = null; - $.each(keys(userSet), function() { + $.each(keys(userSet), function() + { var uid = String(this); - if (uid != userId) { + if (uid != userId) + { var userInfo = userSet[uid]; delete userSet[uid]; callbacks.onUserLeave(userInfo); @@ -438,120 +535,201 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { var reason = appLevelDisconnectReason || params.reason; var shouldReconnect = params.reconnect; - if (shouldReconnect) { + if (shouldReconnect) + { // determine if this is a tight reconnect loop due to weird connectivity problems reconnectTimes.push(+new Date()); var TOO_MANY_RECONNECTS = 8; var TOO_SHORT_A_TIME_MS = 10000; - if (reconnectTimes.length >= TOO_MANY_RECONNECTS && - ((+new Date()) - reconnectTimes[reconnectTimes.length-TOO_MANY_RECONNECTS]) < - TOO_SHORT_A_TIME_MS) { + if (reconnectTimes.length >= TOO_MANY_RECONNECTS && ((+new Date()) - reconnectTimes[reconnectTimes.length - TOO_MANY_RECONNECTS]) < TOO_SHORT_A_TIME_MS) + { setChannelState("DISCONNECTED", "looping"); } - else { + else + { setChannelState("RECONNECTING", reason); setUpSocket(); } } - else { + else + { setChannelState("DISCONNECTED", reason); } } - function setChannelState(newChannelState, moreInfo) { - if (newChannelState != channelState) { + function setChannelState(newChannelState, moreInfo) + { + if (newChannelState != channelState) + { channelState = newChannelState; callbacks.onChannelStateChange(channelState, moreInfo); } } - function keys(obj) { + function keys(obj) + { var array = []; - $.each(obj, function (k, v) { array.push(k); }); + $.each(obj, function(k, v) + { + array.push(k); + }); return array; } - function valuesArray(obj) { + + function valuesArray(obj) + { var array = []; - $.each(obj, function (k, v) { array.push(v); }); + $.each(obj, function(k, v) + { + array.push(v); + }); return array; } // We need to present a working interface even before the socket // is connected for the first time. var deferredActions = []; - function defer(func, tag) { - return function() { + + function defer(func, tag) + { + return function() + { var that = this; var args = arguments; - function action() { + + function action() + { func.apply(that, args); } action.tag = tag; - if (channelState == "CONNECTING") { + if (channelState == "CONNECTING") + { deferredActions.push(action); } - else { + else + { action(); } } } - function doDeferredActions(tag) { + + function doDeferredActions(tag) + { var newArray = []; - for(var i=0;i<deferredActions.length;i++) { + for (var i = 0; i < deferredActions.length; i++) + { var a = deferredActions[i]; - if ((!tag) || (tag == a.tag)) { + if ((!tag) || (tag == a.tag)) + { a(); } - else { + else + { newArray.push(a); } } deferredActions = newArray; } - function sendClientMessage(msg) { - sendMessage({ type: "CLIENT_MESSAGE", payload: msg }); + function sendClientMessage(msg) + { + sendMessage( + { + type: "CLIENT_MESSAGE", + payload: msg + }); } - function getCurrentRevisionNumber() { + function getCurrentRevisionNumber() + { return rev; } - function getDiagnosticInfo() { + function getDiagnosticInfo() + { var maxCaughtErrors = 3; var maxAceErrors = 3; var maxDebugMessages = 50; var longStringCutoff = 500; - function trunc(str) { + function trunc(str) + { return String(str).substring(0, longStringCutoff); } - var info = { errors: {length: 0} }; - function addError(e, catcher, time) { - var error = {catcher:catcher}; + var info = { + errors: { + length: 0 + } + }; + + function addError(e, catcher, time) + { + var error = { + catcher: catcher + }; if (time) error.time = time; // a little over-cautious? - try { if (e.description) error.description = e.description; } catch (x) {} - try { if (e.fileName) error.fileName = e.fileName; } catch (x) {} - try { if (e.lineNumber) error.lineNumber = e.lineNumber; } catch (x) {} - try { if (e.message) error.message = e.message; } catch (x) {} - try { if (e.name) error.name = e.name; } catch (x) {} - try { if (e.number) error.number = e.number; } catch (x) {} - try { if (e.stack) error.stack = trunc(e.stack); } catch (x) {} + try + { + if (e.description) error.description = e.description; + } + catch (x) + {} + try + { + if (e.fileName) error.fileName = e.fileName; + } + catch (x) + {} + try + { + if (e.lineNumber) error.lineNumber = e.lineNumber; + } + catch (x) + {} + try + { + if (e.message) error.message = e.message; + } + catch (x) + {} + try + { + if (e.name) error.name = e.name; + } + catch (x) + {} + try + { + if (e.number) error.number = e.number; + } + catch (x) + {} + try + { + if (e.stack) error.stack = trunc(e.stack); + } + catch (x) + {} info.errors[info.errors.length] = error; info.errors.length++; } - for(var i=0; ((i<caughtErrors.length) && (i<maxCaughtErrors)); i++) { + for (var i = 0; + ((i < caughtErrors.length) && (i < maxCaughtErrors)); i++) + { addError(caughtErrors[i], caughtErrorCatchers[i], caughtErrorTimes[i]); } - if (editor) { + if (editor) + { var aceErrors = editor.getUnhandledErrors(); - for(var i=0; ((i<aceErrors.length) && (i<maxAceErrors)) ;i++) { + for (var i = 0; + ((i < aceErrors.length) && (i < maxAceErrors)); i++) + { var errorRecord = aceErrors[i]; addError(errorRecord.error, "ACE", errorRecord.time); } @@ -564,21 +742,26 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { info.numSocketReconnects = reconnectTimes.length; info.userId = userId; info.currentRev = rev; - info.participants = (function() { + info.participants = (function() + { var pp = []; - for(var u in userSet) { + for (var u in userSet) + { pp.push(u); } return pp.join(','); })(); - if (debugMessages.length > maxDebugMessages) { - debugMessages = debugMessages.slice(debugMessages.length-maxDebugMessages, - debugMessages.length); + if (debugMessages.length > maxDebugMessages) + { + debugMessages = debugMessages.slice(debugMessages.length - maxDebugMessages, debugMessages.length); } - info.debugMessages = {length: 0}; - for(var i=0;i<debugMessages.length;i++) { + info.debugMessages = { + length: 0 + }; + for (var i = 0; i < debugMessages.length; i++) + { info.debugMessages[i] = trunc(debugMessages[i]); info.debugMessages.length++; } @@ -586,40 +769,50 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { return info; } - function getMissedChanges() { + function getMissedChanges() + { var obj = {}; obj.userInfo = userSet[userId]; obj.baseRev = rev; - if (state == "COMMITTING" && stateMessage) { + if (state == "COMMITTING" && stateMessage) + { obj.committedChangeset = stateMessage.changeset; obj.committedChangesetAPool = stateMessage.apool; obj.committedChangesetSocketId = stateMessageSocketId; editor.applyPreparedChangesetToBase(); } var userChangesData = editor.prepareUserChangeset(); - if (userChangesData.changeset) { + if (userChangesData.changeset) + { obj.furtherChangeset = userChangesData.changeset; obj.furtherChangesetAPool = userChangesData.apool; } return obj; } - function setStateIdle() { + function setStateIdle() + { state = "IDLE"; callbacks.onInternalAction("newlyIdle"); schedulePerhapsCallIdleFuncs(); } - function callWhenNotCommitting(func) { + function callWhenNotCommitting(func) + { idleFuncs.push(func); schedulePerhapsCallIdleFuncs(); } var idleFuncs = []; - function schedulePerhapsCallIdleFuncs() { - setTimeout(function() { - if (state == "IDLE") { - while (idleFuncs.length > 0) { + + function schedulePerhapsCallIdleFuncs() + { + setTimeout(function() + { + if (state == "IDLE") + { + while (idleFuncs.length > 0) + { var f = idleFuncs.shift(); f(); } @@ -629,14 +822,38 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { var self; return (self = { - setOnUserJoin: function(cb) { callbacks.onUserJoin = cb; }, - setOnUserLeave: function(cb) { callbacks.onUserLeave = cb; }, - setOnUpdateUserInfo: function(cb) { callbacks.onUpdateUserInfo = cb; }, - setOnChannelStateChange: function(cb) { callbacks.onChannelStateChange = cb; }, - setOnClientMessage: function(cb) { callbacks.onClientMessage = cb; }, - setOnInternalAction: function(cb) { callbacks.onInternalAction = cb; }, - setOnConnectionTrouble: function(cb) { callbacks.onConnectionTrouble = cb; }, - setOnServerMessage: function(cb) { callbacks.onServerMessage = cb; }, + setOnUserJoin: function(cb) + { + callbacks.onUserJoin = cb; + }, + setOnUserLeave: function(cb) + { + callbacks.onUserLeave = cb; + }, + setOnUpdateUserInfo: function(cb) + { + callbacks.onUpdateUserInfo = cb; + }, + setOnChannelStateChange: function(cb) + { + callbacks.onChannelStateChange = cb; + }, + setOnClientMessage: function(cb) + { + callbacks.onClientMessage = cb; + }, + setOnInternalAction: function(cb) + { + callbacks.onInternalAction = cb; + }, + setOnConnectionTrouble: function(cb) + { + callbacks.onConnectionTrouble = cb; + }, + setOnServerMessage: function(cb) + { + callbacks.onServerMessage = cb; + }, updateUserInfo: defer(updateUserInfo), handleMessageFromServer: handleMessageFromServer, getConnectedUsers: getConnectedUsers, @@ -649,16 +866,21 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) { }); } -function selectElementContents(elem) { - if ($.browser.msie) { +function selectElementContents(elem) +{ + if ($.browser.msie) + { var range = document.body.createTextRange(); range.moveToElementText(elem); range.select(); } - else { - if (window.getSelection) { + else + { + if (window.getSelection) + { var browserSelection = window.getSelection(); - if (browserSelection) { + if (browserSelection) + { var range = document.createRange(); range.selectNodeContents(elem); browserSelection.removeAllRanges(); diff --git a/static/js/colorutils.js b/static/js/colorutils.js index 31c26c9a..03141271 100644 --- a/static/js/colorutils.js +++ b/static/js/colorutils.js @@ -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])]; } diff --git a/static/js/contentcollector.js b/static/js/contentcollector.js index 60091ad3..a776affe 100644 --- a/static/js/contentcollector.js +++ b/static/js/contentcollector.js @@ -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, ' ')); + str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' ')); } - function getAssoc(node, name) { - return dom.nodeProp(node, "_magicdom_"+name); + + function getAssoc(node, name) + { + return dom.nodeProp(node, "_magicdom_" + name); } - var lines = (function() { + 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; + }, + atColumnZero: function() + { + return textArray[textArray.length - 1] === ""; }, - startNew: function() { + 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; - - newStrings.unshift(i, 1); - lineStrings.splice.apply(lineStrings, newStrings); - newAttribStrings.unshift(i, 1); - lineAttribs.splice.apply(lineAttribs, newAttribStrings); - } + 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; + + 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; diff --git a/static/js/cssmanager.js b/static/js/cssmanager.js index a5c549ba..feaef67a 100644 --- a/static/js/cssmanager.js +++ b/static/js/cssmanager.js @@ -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; + } + }; } diff --git a/static/js/cssmanager_client.js b/static/js/cssmanager_client.js index 893790f1..2fa61027 100644 --- a/static/js/cssmanager_client.js +++ b/static/js/cssmanager_client.js @@ -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; } diff --git a/static/js/domline.js b/static/js/domline.js index f1d19e49..119b8378 100644 --- a/static/js/domline.js +++ b/static/js/domline.js @@ -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] = ' '; } } } diff --git a/static/js/domline_client.js b/static/js/domline_client.js index 24eb42c0..d4dd4606 100644 --- a/static/js/domline_client.js +++ b/static/js/domline_client.js @@ -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); }); diff --git a/static/js/draggable.js b/static/js/draggable.js index 7f7d9bb1..2f40c1c7 100644 --- a/static/js/draggable.js +++ b/static/js/draggable.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. @@ -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') { diff --git a/static/js/easysync2.js b/static/js/easysync2.js index efc5b997..7f5d0cf9 100644 --- a/static/js/easysync2.js +++ b/static/js/easysync2.js @@ -1,6 +1,5 @@ // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2 // %APPJET%: jimport("com.etherpad.Easysync2Support"); - /** * Copyright 2009 Google Inc. * @@ -20,61 +19,75 @@ //var _opt = (this.Easysync2Support || null); var _opt = null; // disable optimization for now -function AttribPool() { +function AttribPool() +{ var p = {}; p.numToAttrib = {}; // e.g. {0: ['foo','bar']} 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) { + if (str in p.attribToNum) + { return p.attribToNum[str]; } - if (dontAddIfAbsent) { + if (dontAddIfAbsent) + { return -1; } var num = p.nextNum++; p.attribToNum[str] = num; - p.numToAttrib[num] = [String(attrib[0]||''), - String(attrib[1]||'')]; + p.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')]; return num; }; - p.getAttrib = function(num) { + p.getAttrib = function(num) + { var pair = p.numToAttrib[num]; - if (! pair) return pair; + 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 ''; + if (!pair) return ''; return pair[0]; }; - p.getAttribValue = function(num) { + p.getAttribValue = function(num) + { var pair = p.numToAttrib[num]; - if (! pair) return ''; + if (!pair) return ''; return pair[1]; }; - p.eachAttrib = function(func) { - for(var n in p.numToAttrib) { + p.eachAttrib = function(func) + { + for (var n in p.numToAttrib) + { var pair = p.numToAttrib[n]; func(pair[0], pair[1]); } }; - p.toJsonable = function() { - return {numToAttrib: p.numToAttrib, nextNum: p.nextNum}; + p.toJsonable = function() + { + return { + numToAttrib: p.numToAttrib, + nextNum: p.nextNum + }; }; - p.fromJsonable = function(obj) { + p.fromJsonable = function(obj) + { p.numToAttrib = obj.numToAttrib; p.nextNum = obj.nextNum; p.attribToNum = {}; - for(var n in p.numToAttrib) { + for (var n in p.numToAttrib) + { p.attribToNum[String(p.numToAttrib[n])] = Number(n); } return p; @@ -85,54 +98,80 @@ function AttribPool() { var Changeset = {}; -Changeset.error = function error(msg) { var e = new Error(msg); e.easysync = true; throw e; }; -Changeset.assert = function assert(b, msgParts) { - if (! b) { +Changeset.error = function error(msg) +{ + var e = new Error(msg); + e.easysync = true; + throw e; +}; +Changeset.assert = function assert(b, msgParts) +{ + if (!b) + { var msg = Array.prototype.slice.call(arguments, 1).join(''); - Changeset.error("Changeset: "+msg); + Changeset.error("Changeset: " + msg); } }; -Changeset.parseNum = function(str) { return parseInt(str, 36); }; -Changeset.numToString = function(num) { return num.toString(36).toLowerCase(); }; -Changeset.toBaseTen = function(cs) { +Changeset.parseNum = function(str) +{ + return parseInt(str, 36); +}; +Changeset.numToString = function(num) +{ + return num.toString(36).toLowerCase(); +}; +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 String(Changeset.parseNum(s)); }) + fromDollar; + 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; var startIndex = (optStartIndex || 0); var curIndex = startIndex; var prevIndex = curIndex; - function nextRegexMatch() { + + function nextRegexMatch() + { prevIndex = curIndex; var result; - if (_opt) { + if (_opt) + { result = _opt.nextOpInString(opsStr, curIndex); - if (result) { - if (result.opcode() == '?') { + if (result) + { + if (result.opcode() == '?') + { Changeset.error("Hit error opcode in op stream"); } curIndex = result.lastIndex(); } } - else { + else + { regex.lastIndex = curIndex; result = regex.exec(opsStr); curIndex = regex.lastIndex; - if (result[0] == '?') { + if (result[0] == '?') + { Changeset.error("Hit error opcode in op stream"); } } @@ -140,63 +179,97 @@ Changeset.opIterator = function(opsStr, optStartIndex) { } var regexResult = nextRegexMatch(); var obj = Changeset.newOp(); - function next(optObj) { + + function next(optObj) + { var op = (optObj || obj); - if (_opt && regexResult) { + if (_opt && regexResult) + { op.attribs = regexResult.attribs(); op.lines = regexResult.lines(); op.chars = regexResult.chars(); op.opcode = regexResult.opcode(); regexResult = nextRegexMatch(); } - else if ((! _opt) && regexResult[0]) { + else if ((!_opt) && regexResult[0]) + { op.attribs = regexResult[1]; op.lines = Changeset.parseNum(regexResult[2] || 0); op.opcode = regexResult[3]; op.chars = Changeset.parseNum(regexResult[4]); regexResult = nextRegexMatch(); } - else { + else + { Changeset.clearOp(op); } return op; } - function hasNext() { return !! (_opt ? regexResult : regexResult[0]); } - function lastIndex() { return prevIndex; } - return {next: next, hasNext: hasNext, lastIndex: lastIndex}; + + function hasNext() + { + return !!(_opt ? regexResult : regexResult[0]); + } + + function lastIndex() + { + return prevIndex; + } + return { + next: next, + hasNext: hasNext, + lastIndex: lastIndex + }; }; -Changeset.clearOp = function(op) { +Changeset.clearOp = function(op) +{ op.opcode = ''; op.chars = 0; op.lines = 0; op.attribs = ''; }; -Changeset.newOp = function(optOpcode) { - return {opcode:(optOpcode || ''), chars:0, lines:0, attribs:''}; +Changeset.newOp = function(optOpcode) +{ + return { + opcode: (optOpcode || ''), + chars: 0, + lines: 0, + attribs: '' + }; }; -Changeset.cloneOp = function(op) { - return {opcode: op.opcode, chars: op.chars, lines: op.lines, attribs: op.attribs}; +Changeset.cloneOp = function(op) +{ + return { + opcode: op.opcode, + chars: op.chars, + lines: op.lines, + 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'; + if (!op.opcode) return 'null'; var assem = Changeset.opAssembler(); 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) var unpacked = Changeset.unpack(cs); @@ -210,34 +283,46 @@ Changeset.checkRep = function(cs) { var calcNewLen = 0; var numInserted = 0; var iter = Changeset.opIterator(ops); - while (iter.hasNext()) { + while (iter.hasNext()) + { var o = iter.next(); - switch (o.opcode) { - case '=': oldPos += o.chars; calcNewLen += o.chars; break; - case '-': oldPos += o.chars; Changeset.assert(oldPos < oldLen, oldPos," >= ",oldLen," in ",cs); break; - case '+': { - calcNewLen += o.chars; numInserted += o.chars; - Changeset.assert(calcNewLen < newLen, calcNewLen," >= ",newLen," in ",cs); + switch (o.opcode) + { + case '=': + oldPos += o.chars; + calcNewLen += o.chars; break; - } + case '-': + oldPos += o.chars; + Changeset.assert(oldPos < oldLen, oldPos, " >= ", oldLen, " in ", cs); + break; + case '+': + { + calcNewLen += o.chars; + numInserted += o.chars; + Changeset.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs); + break; + } } assem.append(o); } calcNewLen += oldLen - oldPos; charBank = charBank.substring(0, numInserted); - while (charBank.length < numInserted) { + while (charBank.length < numInserted) + { charBank += "?"; } assem.endDocument(); var normalized = Changeset.pack(oldLen, calcNewLen, assem.toString(), charBank); - Changeset.assert(normalized == cs, normalized,' != ',cs); + Changeset.assert(normalized == cs, normalized, ' != ', 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. // Specifically: @@ -245,7 +330,6 @@ Changeset.smartOpAssembler = function() { // - strips final "=" // - ignores 0-length changes // - reorders consecutive + and - (which margingOpAssembler doesn't do) - var minusAssem = Changeset.mergingOpAssembler(); var plusAssem = Changeset.mergingOpAssembler(); var keepAssem = Changeset.mergingOpAssembler(); @@ -253,71 +337,85 @@ Changeset.smartOpAssembler = function() { var lastOpcode = ''; var lengthChange = 0; - function flushKeeps() { + function flushKeeps() + { assem.append(keepAssem.toString()); keepAssem.clear(); } - function flushPlusMinus() { + function flushPlusMinus() + { assem.append(minusAssem.toString()); minusAssem.clear(); assem.append(plusAssem.toString()); plusAssem.clear(); } - function append(op) { - if (! op.opcode) return; - if (! op.chars) return; + function append(op) + { + if (!op.opcode) return; + if (!op.chars) return; - if (op.opcode == '-') { - if (lastOpcode == '=') { - flushKeeps(); + if (op.opcode == '-') + { + if (lastOpcode == '=') + { + flushKeeps(); } minusAssem.append(op); lengthChange -= op.chars; } - else if (op.opcode == '+') { - if (lastOpcode == '=') { - flushKeeps(); + else if (op.opcode == '+') + { + if (lastOpcode == '=') + { + flushKeeps(); } plusAssem.append(op); lengthChange += op.chars; } - else if (op.opcode == '=') { - if (lastOpcode != '=') { - flushPlusMinus(); + else if (op.opcode == '=') + { + if (lastOpcode != '=') + { + flushPlusMinus(); } keepAssem.append(op); } lastOpcode = op.opcode; } - function appendOpWithText(opcode, text, attribs, pool) { + function appendOpWithText(opcode, text, attribs, pool) + { var op = Changeset.newOp(opcode); op.attribs = Changeset.makeAttribsString(opcode, attribs, pool); var lastNewlinePos = text.lastIndexOf('\n'); - if (lastNewlinePos < 0) { + if (lastNewlinePos < 0) + { op.chars = text.length; op.lines = 0; append(op); } - else { - op.chars = lastNewlinePos+1; + else + { + op.chars = lastNewlinePos + 1; op.lines = text.match(/\n/g).length; append(op); - op.chars = text.length - (lastNewlinePos+1); + op.chars = text.length - (lastNewlinePos + 1); op.lines = 0; append(op); } } - function toString() { + function toString() + { flushPlusMinus(); flushKeeps(); return assem.toString(); } - function clear() { + function clear() + { minusAssem.clear(); plusAssem.clear(); keepAssem.clear(); @@ -325,40 +423,64 @@ Changeset.smartOpAssembler = function() { lengthChange = 0; } - function endDocument() { + function endDocument() + { keepAssem.endDocument(); } - function getLengthChange() { + function getLengthChange() + { return lengthChange; } - return {append: append, toString: toString, clear: clear, endDocument: endDocument, - appendOpWithText: appendOpWithText, getLengthChange: getLengthChange }; + return { + append: append, + toString: toString, + clear: clear, + endDocument: endDocument, + appendOpWithText: appendOpWithText, + getLengthChange: getLengthChange + }; }; -if (_opt) { - Changeset.mergingOpAssembler = function() { +if (_opt) +{ + Changeset.mergingOpAssembler = function() + { var assem = _opt.mergingOpAssembler(); - function append(op) { + function append(op) + { assem.append(op.opcode, op.chars, op.lines, op.attribs); } - function toString() { + + function toString() + { return assem.toString(); } - function clear() { + + function clear() + { assem.clear(); } - function endDocument() { + + function endDocument() + { assem.endDocument(); } - return {append: append, toString: toString, clear: clear, endDocument: endDocument}; + return { + append: append, + toString: toString, + clear: clear, + endDocument: endDocument + }; }; } -else { - Changeset.mergingOpAssembler = function() { +else +{ + Changeset.mergingOpAssembler = function() + { // This assembler can be used in production; it efficiently // merges consecutive operations that are mergeable, ignores // no-ops, and drops final pure "keeps". It does not re-order @@ -372,14 +494,19 @@ else { // ops immediately after it. var bufOpAdditionalCharsAfterNewline = 0; - function flush(isEndDocument) { - if (bufOp.opcode) { - if (isEndDocument && bufOp.opcode == '=' && ! bufOp.attribs) { + function flush(isEndDocument) + { + if (bufOp.opcode) + { + if (isEndDocument && bufOp.opcode == '=' && !bufOp.attribs) + { // final merged keep, leave it implicit } - else { + else + { assem.append(bufOp); - if (bufOpAdditionalCharsAfterNewline) { + if (bufOpAdditionalCharsAfterNewline) + { bufOp.chars = bufOpAdditionalCharsAfterNewline; bufOp.lines = 0; assem.append(bufOp); @@ -389,122 +516,193 @@ else { bufOp.opcode = ''; } } - function append(op) { - if (op.chars > 0) { - if (bufOp.opcode == op.opcode && bufOp.attribs == op.attribs) { - if (op.lines > 0) { + + function append(op) + { + if (op.chars > 0) + { + if (bufOp.opcode == op.opcode && bufOp.attribs == op.attribs) + { + if (op.lines > 0) + { // bufOp and additional chars are all mergeable into a multi-line op bufOp.chars += bufOpAdditionalCharsAfterNewline + op.chars; bufOp.lines += op.lines; bufOpAdditionalCharsAfterNewline = 0; } - else if (bufOp.lines == 0) { + else if (bufOp.lines == 0) + { // both bufOp and op are in-line bufOp.chars += op.chars; } - else { + else + { // append in-line text to multi-line bufOp bufOpAdditionalCharsAfterNewline += op.chars; } } - else { + else + { flush(); Changeset.copyOp(op, bufOp); } } } - function endDocument() { + + function endDocument() + { flush(true); } - function toString() { + + function toString() + { flush(); return assem.toString(); } - function clear() { + + function clear() + { assem.clear(); Changeset.clearOp(bufOp); } - return {append: append, toString: toString, clear: clear, endDocument: endDocument}; + return { + append: append, + toString: toString, + clear: clear, + endDocument: endDocument + }; }; } -if (_opt) { - Changeset.opAssembler = function() { +if (_opt) +{ + Changeset.opAssembler = function() + { var assem = _opt.opAssembler(); // this function allows op to be mutated later (doesn't keep a ref) - function append(op) { + + + function append(op) + { assem.append(op.opcode, op.chars, op.lines, op.attribs); } - function toString() { + + function toString() + { return assem.toString(); } - function clear() { + + function clear() + { assem.clear(); } - return {append: append, toString: toString, clear: clear}; + return { + append: append, + toString: toString, + clear: clear + }; }; } -else { - Changeset.opAssembler = function() { +else +{ + Changeset.opAssembler = function() + { var pieces = []; // this function allows op to be mutated later (doesn't keep a ref) - function append(op) { + + + function append(op) + { pieces.push(op.attribs); - if (op.lines) { + if (op.lines) + { pieces.push('|', Changeset.numToString(op.lines)); } pieces.push(op.opcode); pieces.push(Changeset.numToString(op.chars)); } - function toString() { + + function toString() + { return pieces.join(''); } - function clear() { + + function clear() + { pieces.length = 0; } - return {append: append, toString: toString, clear: clear}; + return { + append: append, + toString: toString, + clear: clear + }; }; } -Changeset.stringIterator = function(str) { +Changeset.stringIterator = function(str) +{ var curIndex = 0; - function assertRemaining(n) { - Changeset.assert(n <= remaining(), "!(",n," <= ",remaining(),")"); + + function assertRemaining(n) + { + Changeset.assert(n <= remaining(), "!(", n, " <= ", remaining(), ")"); } - function take(n) { + + function take(n) + { assertRemaining(n); var s = str.substr(curIndex, n); curIndex += n; return s; } - function peek(n) { + + function peek(n) + { assertRemaining(n); var s = str.substr(curIndex, n); return s; } - function skip(n) { + + function skip(n) + { assertRemaining(n); curIndex += n; } - function remaining() { + + function remaining() + { return str.length - curIndex; } - return {take:take, skip:skip, remaining:remaining, peek:peek}; + return { + take: take, + skip: skip, + remaining: remaining, + peek: peek + }; }; -Changeset.stringAssembler = function() { +Changeset.stringAssembler = function() +{ var pieces = []; - function append(x) { + + function append(x) + { pieces.push(String(x)); } - function toString() { + + function toString() + { return pieces.join(''); } - return {append: append, toString: toString}; + return { + append: append, + toString: toString + }; }; // "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 // with respect to newlines, but not the other additional constraints @@ -513,260 +711,335 @@ Changeset.textLinesMutator = function(lines) { // is not actually a newline, but for the purposes of N and L values, // the caller should pretend it is, and for things to work right in that case, the input // to insert() should be a single line with no newlines. - - var curSplice = [0,0]; + var curSplice = [0, 0]; var inSplice = false; // position in document after curSplice is applied: - var curLine = 0, curCol = 0; + var curLine = 0, + curCol = 0; // invariant: if (inSplice) then (curLine is in curSplice[0] + curSplice.length - {2,3}) && // curLine >= curSplice[0] // invariant: if (inSplice && (curLine >= curSplice[0] + curSplice.length - 2)) then // curCol == 0 - function lines_applySplice(s) { + function lines_applySplice(s) + { lines.splice.apply(lines, s); } - function lines_toSource() { + + function lines_toSource() + { return lines.toSource(); } - function lines_get(idx) { - if (lines.get) { + + function lines_get(idx) + { + if (lines.get) + { return lines.get(idx); } - else { + else + { return lines[idx]; } } // can be unimplemented if removeLines's return value not needed - function lines_slice(start, end) { - if (lines.slice) { + + + function lines_slice(start, end) + { + if (lines.slice) + { return lines.slice(start, end); } - else { + else + { return []; } } - function lines_length() { - if ((typeof lines.length) == "number") { + + function lines_length() + { + if ((typeof lines.length) == "number") + { return lines.length; } - else { + else + { return lines.length(); } } - function enterSplice() { + function enterSplice() + { curSplice[0] = curLine; curSplice[1] = 0; - if (curCol > 0) { + if (curCol > 0) + { putCurLineInSplice(); } inSplice = true; } - function leaveSplice() { + + function leaveSplice() + { lines_applySplice(curSplice); curSplice.length = 2; curSplice[0] = curSplice[1] = 0; inSplice = false; } - function isCurLineInSplice() { + + function isCurLineInSplice() + { return (curLine - curSplice[0] < (curSplice.length - 2)); } - function debugPrint(typ) { - print(typ+": "+curSplice.toSource()+" / "+curLine+","+curCol+" / "+lines_toSource()); + + function debugPrint(typ) + { + print(typ + ": " + curSplice.toSource() + " / " + curLine + "," + curCol + " / " + lines_toSource()); } - function putCurLineInSplice() { - if (! isCurLineInSplice()) { + + function putCurLineInSplice() + { + if (!isCurLineInSplice()) + { curSplice.push(lines_get(curSplice[0] + curSplice[1])); curSplice[1]++; } return 2 + curLine - curSplice[0]; } - function skipLines(L, includeInSplice) { - if (L) { - if (includeInSplice) { - if (! inSplice) { - enterSplice(); - } - for(var i=0;i<L;i++) { - curCol = 0; - putCurLineInSplice(); - curLine++; - } - } - else { - if (inSplice) { - if (L > 1) { - leaveSplice(); - } - else { - putCurLineInSplice(); - } - } - curLine += L; - curCol = 0; + function skipLines(L, includeInSplice) + { + if (L) + { + if (includeInSplice) + { + if (!inSplice) + { + enterSplice(); + } + for (var i = 0; i < L; i++) + { + curCol = 0; + putCurLineInSplice(); + curLine++; + } + } + else + { + if (inSplice) + { + if (L > 1) + { + leaveSplice(); + } + else + { + putCurLineInSplice(); + } + } + curLine += L; + curCol = 0; } //print(inSplice+" / "+isCurLineInSplice()+" / "+curSplice[0]+" / "+curSplice[1]+" / "+lines.length); - /*if (inSplice && (! isCurLineInSplice()) && (curSplice[0] + curSplice[1] < lines.length)) { +/*if (inSplice && (! isCurLineInSplice()) && (curSplice[0] + curSplice[1] < lines.length)) { print("BLAH"); putCurLineInSplice(); - }*/ // tests case foo in remove(), which isn't otherwise covered in current impl + }*/ + // tests case foo in remove(), which isn't otherwise covered in current impl } //debugPrint("skip"); } - function skip(N, L, includeInSplice) { - if (N) { - if (L) { - skipLines(L, includeInSplice); + function skip(N, L, includeInSplice) + { + if (N) + { + if (L) + { + skipLines(L, includeInSplice); } - else { - if (includeInSplice && ! inSplice) { - enterSplice(); - } - if (inSplice) { - putCurLineInSplice(); - } - curCol += N; - //debugPrint("skip"); + else + { + if (includeInSplice && !inSplice) + { + enterSplice(); + } + if (inSplice) + { + putCurLineInSplice(); + } + curCol += N; + //debugPrint("skip"); } } } - function removeLines(L) { + function removeLines(L) + { var removed = ''; - if (L) { - if (! inSplice) { - enterSplice(); - } - function nextKLinesText(k) { - var m = curSplice[0] + curSplice[1]; - return lines_slice(m, m+k).join(''); - } - if (isCurLineInSplice()) { - //print(curCol); - if (curCol == 0) { - removed = curSplice[curSplice.length-1]; - // print("FOO"); // case foo - curSplice.length--; - removed += nextKLinesText(L-1); - curSplice[1] += L-1; - } - else { - removed = nextKLinesText(L-1); - curSplice[1] += L-1; - var sline = curSplice.length - 1; - removed = curSplice[sline].substring(curCol) + removed; - curSplice[sline] = curSplice[sline].substring(0, curCol) + - lines_get(curSplice[0] + curSplice[1]); - curSplice[1] += 1; - } - } - else { - removed = nextKLinesText(L); - curSplice[1] += L; + if (L) + { + if (!inSplice) + { + enterSplice(); + } + + function nextKLinesText(k) + { + var m = curSplice[0] + curSplice[1]; + return lines_slice(m, m + k).join(''); + } + if (isCurLineInSplice()) + { + //print(curCol); + if (curCol == 0) + { + removed = curSplice[curSplice.length - 1]; + // print("FOO"); // case foo + curSplice.length--; + removed += nextKLinesText(L - 1); + curSplice[1] += L - 1; + } + else + { + removed = nextKLinesText(L - 1); + curSplice[1] += L - 1; + var sline = curSplice.length - 1; + removed = curSplice[sline].substring(curCol) + removed; + curSplice[sline] = curSplice[sline].substring(0, curCol) + lines_get(curSplice[0] + curSplice[1]); + curSplice[1] += 1; + } + } + else + { + removed = nextKLinesText(L); + curSplice[1] += L; } //debugPrint("remove"); } return removed; } - function remove(N, L) { + function remove(N, L) + { var removed = ''; - if (N) { - if (L) { - return removeLines(L); + if (N) + { + if (L) + { + return removeLines(L); } - else { - if (! inSplice) { - enterSplice(); - } - var sline = putCurLineInSplice(); - removed = curSplice[sline].substring(curCol, curCol+N); - curSplice[sline] = curSplice[sline].substring(0, curCol) + - curSplice[sline].substring(curCol+N); - //debugPrint("remove"); + else + { + if (!inSplice) + { + enterSplice(); + } + var sline = putCurLineInSplice(); + removed = curSplice[sline].substring(curCol, curCol + N); + curSplice[sline] = curSplice[sline].substring(0, curCol) + curSplice[sline].substring(curCol + N); + //debugPrint("remove"); } } return removed; } - function insert(text, L) { - if (text) { - if (! inSplice) { - enterSplice(); - } - if (L) { - var newLines = Changeset.splitTextLines(text); - if (isCurLineInSplice()) { - //if (curCol == 0) { - //curSplice.length--; - //curSplice[1]--; - //Array.prototype.push.apply(curSplice, newLines); - //curLine += newLines.length; - //} - //else { - var sline = curSplice.length - 1; - var theLine = curSplice[sline]; - var lineCol = curCol; - curSplice[sline] = theLine.substring(0, lineCol) + newLines[0]; - curLine++; - newLines.splice(0, 1); - Array.prototype.push.apply(curSplice, newLines); - curLine += newLines.length; - curSplice.push(theLine.substring(lineCol)); - curCol = 0; - //} - } - else { - Array.prototype.push.apply(curSplice, newLines); - curLine += newLines.length; - } - } - else { - var sline = putCurLineInSplice(); - curSplice[sline] = curSplice[sline].substring(0, curCol) + - text + curSplice[sline].substring(curCol); - curCol += text.length; + function insert(text, L) + { + if (text) + { + if (!inSplice) + { + enterSplice(); + } + if (L) + { + var newLines = Changeset.splitTextLines(text); + if (isCurLineInSplice()) + { + //if (curCol == 0) { + //curSplice.length--; + //curSplice[1]--; + //Array.prototype.push.apply(curSplice, newLines); + //curLine += newLines.length; + //} + //else { + var sline = curSplice.length - 1; + var theLine = curSplice[sline]; + var lineCol = curCol; + curSplice[sline] = theLine.substring(0, lineCol) + newLines[0]; + curLine++; + newLines.splice(0, 1); + Array.prototype.push.apply(curSplice, newLines); + curLine += newLines.length; + curSplice.push(theLine.substring(lineCol)); + curCol = 0; + //} + } + else + { + Array.prototype.push.apply(curSplice, newLines); + curLine += newLines.length; + } + } + else + { + var sline = putCurLineInSplice(); + curSplice[sline] = curSplice[sline].substring(0, curCol) + text + curSplice[sline].substring(curCol); + curCol += text.length; } //debugPrint("insert"); } } - function hasMore() { + function hasMore() + { //print(lines.length+" / "+inSplice+" / "+(curSplice.length - 2)+" / "+curSplice[1]); var docLines = lines_length(); - if (inSplice) { + if (inSplice) + { docLines += curSplice.length - 2 - curSplice[1]; } return curLine < docLines; } - function close() { - if (inSplice) { + function close() + { + if (inSplice) + { leaveSplice(); } //debugPrint("close"); } - var self = {skip:skip, remove:remove, insert:insert, close:close, hasMore:hasMore, - removeLines:removeLines, skipLines: skipLines}; + var self = { + skip: skip, + remove: remove, + insert: insert, + close: close, + hasMore: hasMore, + removeLines: removeLines, + skipLines: skipLines + }; 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); var assem = Changeset.smartOpAssembler(); var op1 = Changeset.newOp(); var op2 = Changeset.newOp(); var opOut = Changeset.newOp(); - while (op1.opcode || iter1.hasNext() || op2.opcode || iter2.hasNext()) { - if ((! op1.opcode) && iter1.hasNext()) iter1.next(op1); - if ((! op2.opcode) && iter2.hasNext()) iter2.next(op2); + while (op1.opcode || iter1.hasNext() || op2.opcode || iter2.hasNext()) + { + if ((!op1.opcode) && iter1.hasNext()) iter1.next(op1); + if ((!op2.opcode) && iter2.hasNext()) iter2.next(op2); func(op1, op2, opOut); - if (opOut.opcode) { + if (opOut.opcode) + { //print(opOut.toSource()); assem.append(opOut); opOut.opcode = ''; @@ -776,72 +1049,94 @@ 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); - if ((! headerMatch) || (! headerMatch[0])) { - Changeset.error("Not a changeset: "+cs); + if ((!headerMatch) || (!headerMatch[0])) + { + Changeset.error("Not a changeset: " + cs); } var oldLen = Changeset.parseNum(headerMatch[1]); var changeSign = (headerMatch[2] == '>') ? 1 : -1; var changeMag = Changeset.parseNum(headerMatch[3]); - var newLen = oldLen + changeSign*changeMag; + var newLen = oldLen + changeSign * changeMag; var opsStart = headerMatch[0].length; var opsEnd = cs.indexOf("$"); if (opsEnd < 0) opsEnd = cs.length; - return {oldLen: oldLen, newLen: newLen, ops: cs.substring(opsStart, opsEnd), - charBank: cs.substring(opsEnd+1)}; + return { + oldLen: oldLen, + newLen: newLen, + ops: cs.substring(opsStart, opsEnd), + charBank: cs.substring(opsEnd + 1) + }; }; -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)); + var lenDiffStr = (lenDiff >= 0 ? '>' + Changeset.numToString(lenDiff) : '<' + Changeset.numToString(-lenDiff)); var a = []; a.push('Z:', Changeset.numToString(oldLen), lenDiffStr, 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); + Changeset.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen); var csIter = Changeset.opIterator(unpacked.ops); var bankIter = Changeset.stringIterator(unpacked.charBank); var strIter = Changeset.stringIterator(str); var assem = Changeset.stringAssembler(); - while (csIter.hasNext()) { + while (csIter.hasNext()) + { var op = csIter.next(); - switch(op.opcode) { - case '+': assem.append(bankIter.take(op.chars)); break; - case '-': strIter.skip(op.chars); break; - case '=': assem.append(strIter.take(op.chars)); break; + switch (op.opcode) + { + case '+': + assem.append(bankIter.take(op.chars)); + break; + case '-': + strIter.skip(op.chars); + break; + case '=': + assem.append(strIter.take(op.chars)); + break; } } assem.append(strIter.take(strIter.remaining())); return assem.toString(); }; -Changeset.mutateTextLines = function(cs, lines) { +Changeset.mutateTextLines = function(cs, lines) +{ var unpacked = Changeset.unpack(cs); var csIter = Changeset.opIterator(unpacked.ops); var bankIter = Changeset.stringIterator(unpacked.charBank); var mut = Changeset.textLinesMutator(lines); - while (csIter.hasNext()) { + while (csIter.hasNext()) + { var op = csIter.next(); - switch(op.opcode) { - case '+': mut.insert(bankIter.take(op.chars), op.lines); break; - case '-': mut.remove(op.chars, op.lines); break; - case '=': mut.skip(op.chars, op.lines, (!! op.attribs)); break; + switch (op.opcode) + { + case '+': + mut.insert(bankIter.take(op.chars), op.lines); + break; + case '-': + mut.remove(op.chars, op.lines); + break; + case '=': + mut.skip(op.chars, op.lines, ( !! op.attribs)); + break; } } 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 // information, while other times they are treated as operations that // mutate a set of attributes, and this affects whether an empty value @@ -853,46 +1148,53 @@ Changeset.composeAttributes = function(att1, att2, resultIsMutation, pool) { // ([], [(bold, true)], false) -> [(bold, true)] // ([(bold, true)], [(bold, )], true) -> [(bold, )] // ([(bold, true)], [(bold, )], false) -> [] - // pool can be null if att2 has no attributes. - - if ((! att1) && resultIsMutation) { + if ((!att1) && resultIsMutation) + { // In the case of a mutation (i.e. composing two changesets), // an att2 composed with an empy att1 is just att2. If att1 // is part of an attribution string, then att2 may remove // attributes that are already gone, so don't do this optimization. return att2; } - if (! att2) return att1; + 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; - for(var i=0;i<atts.length;i++) { + for (var i = 0; i < atts.length; i++) + { var oldPair = atts[i]; - if (oldPair[0] == pair[0]) { - if (pair[1] || resultIsMutation) { - oldPair[1] = pair[1]; - } - else { - atts.splice(i, 1); - } - found = true; - break; - } - } - if ((! found) && (pair[1] || resultIsMutation)) { + if (oldPair[0] == pair[0]) + { + if (pair[1] || resultIsMutation) + { + oldPair[1] = pair[1]; + } + else + { + atts.splice(i, 1); + } + found = true; + break; + } + } + if ((!found) && (pair[1] || resultIsMutation)) + { atts.push(pair); } return ''; }); atts.sort(); var buf = Changeset.stringAssembler(); - for(var i=0;i<atts.length;i++) { + for (var i = 0; i < atts.length; i++) + { buf.append('*'); buf.append(Changeset.numToString(pool.putAttrib(atts[i]))); } @@ -900,99 +1202,115 @@ 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. // pool can be null if definitely not needed. - //print(csOp.toSource()+" "+attOp.toSource()+" "+opOut.toSource()); - if (attOp.opcode == '-') { + if (attOp.opcode == '-') + { Changeset.copyOp(attOp, opOut); attOp.opcode = ''; } - else if (! attOp.opcode) { + else if (!attOp.opcode) + { Changeset.copyOp(csOp, opOut); csOp.opcode = ''; } - else { - switch (csOp.opcode) { - case '-': { - if (csOp.chars <= attOp.chars) { - // delete or delete part - if (attOp.opcode == '=') { - opOut.opcode = '-'; - opOut.chars = csOp.chars; - opOut.lines = csOp.lines; - opOut.attribs = ''; - } - attOp.chars -= csOp.chars; - attOp.lines -= csOp.lines; - csOp.opcode = ''; - if (! attOp.chars) { - attOp.opcode = ''; - } - } - else { - // delete and keep going - if (attOp.opcode == '=') { - opOut.opcode = '-'; - opOut.chars = attOp.chars; - opOut.lines = attOp.lines; - opOut.attribs = ''; - } - csOp.chars -= attOp.chars; - csOp.lines -= attOp.lines; - attOp.opcode = ''; + else + { + switch (csOp.opcode) + { + case '-': + { + if (csOp.chars <= attOp.chars) + { + // delete or delete part + if (attOp.opcode == '=') + { + opOut.opcode = '-'; + opOut.chars = csOp.chars; + opOut.lines = csOp.lines; + opOut.attribs = ''; + } + attOp.chars -= csOp.chars; + attOp.lines -= csOp.lines; + csOp.opcode = ''; + if (!attOp.chars) + { + attOp.opcode = ''; + } + } + else + { + // delete and keep going + if (attOp.opcode == '=') + { + opOut.opcode = '-'; + opOut.chars = attOp.chars; + opOut.lines = attOp.lines; + opOut.attribs = ''; + } + csOp.chars -= attOp.chars; + csOp.lines -= attOp.lines; + attOp.opcode = ''; + } + break; } - break; - } - case '+': { - // insert - Changeset.copyOp(csOp, opOut); - csOp.opcode = ''; - break; - } - case '=': { - if (csOp.chars <= attOp.chars) { - // keep or keep part - opOut.opcode = attOp.opcode; - opOut.chars = csOp.chars; - opOut.lines = csOp.lines; - opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs, - attOp.opcode == '=', pool); - csOp.opcode = ''; - attOp.chars -= csOp.chars; - attOp.lines -= csOp.lines; - if (! attOp.chars) { - attOp.opcode = ''; - } - } - else { - // keep and keep going - opOut.opcode = attOp.opcode; - opOut.chars = attOp.chars; - opOut.lines = attOp.lines; - opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs, - attOp.opcode == '=', pool); - attOp.opcode = ''; - csOp.chars -= attOp.chars; - csOp.lines -= attOp.lines; + case '+': + { + // insert + Changeset.copyOp(csOp, opOut); + csOp.opcode = ''; + break; + } + case '=': + { + if (csOp.chars <= attOp.chars) + { + // keep or keep part + opOut.opcode = attOp.opcode; + opOut.chars = csOp.chars; + opOut.lines = csOp.lines; + opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); + csOp.opcode = ''; + attOp.chars -= csOp.chars; + attOp.lines -= csOp.lines; + if (!attOp.chars) + { + attOp.opcode = ''; + } + } + else + { + // keep and keep going + opOut.opcode = attOp.opcode; + opOut.chars = attOp.chars; + opOut.lines = attOp.lines; + opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); + attOp.opcode = ''; + csOp.chars -= attOp.chars; + csOp.lines -= attOp.lines; + } + break; + } + case '': + { + Changeset.copyOp(attOp, opOut); + attOp.opcode = ''; + break; } - break; - } - case '': { - Changeset.copyOp(attOp, opOut); - attOp.opcode = ''; - break; - } } } }; -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); }); }; @@ -1003,10 +1321,10 @@ Changeset.applyToAttribution = function(cs, astr, pool) { };*/ -Changeset.mutateAttributionLines = function(cs, lines, pool) { +Changeset.mutateAttributionLines = function(cs, lines, pool) +{ //dmesg(cs); //dmesg(lines.toSource()+" ->"); - var unpacked = Changeset.unpack(cs); var csIter = Changeset.opIterator(unpacked.ops); var csBank = unpacked.charBank; @@ -1015,30 +1333,41 @@ Changeset.mutateAttributionLines = function(cs, lines, pool) { var mut = Changeset.textLinesMutator(lines); var lineIter = null; - function isNextMutOp() { + + function isNextMutOp() + { return (lineIter && lineIter.hasNext()) || mut.hasMore(); } - function nextMutOp(destOp) { - if ((!(lineIter && lineIter.hasNext())) && mut.hasMore()) { + + function nextMutOp(destOp) + { + if ((!(lineIter && lineIter.hasNext())) && mut.hasMore()) + { var line = mut.removeLines(1); lineIter = Changeset.opIterator(line); } - if (lineIter && lineIter.hasNext()) { + if (lineIter && lineIter.hasNext()) + { lineIter.next(destOp); } - else { + else + { destOp.opcode = ''; } } var lineAssem = null; - function outputMutOp(op) { + + function outputMutOp(op) + { //print("outputMutOp: "+op.toSource()); - if (! lineAssem) { + if (!lineAssem) + { lineAssem = Changeset.mergingOpAssembler(); } lineAssem.append(op); - if (op.lines > 0) { - Changeset.assert(op.lines == 1, "Can't have op.lines of ",op.lines," in attribution lines"); + if (op.lines > 0) + { + Changeset.assert(op.lines == 1, "Can't have op.lines of ", op.lines, " in attribution lines"); // ship it to the mut mut.insert(lineAssem.toString(), 1); lineAssem = null; @@ -1048,93 +1377,109 @@ Changeset.mutateAttributionLines = function(cs, lines, pool) { var csOp = Changeset.newOp(); var attOp = Changeset.newOp(); var opOut = Changeset.newOp(); - while (csOp.opcode || csIter.hasNext() || attOp.opcode || isNextMutOp()) { - if ((! csOp.opcode) && csIter.hasNext()) { + while (csOp.opcode || csIter.hasNext() || attOp.opcode || isNextMutOp()) + { + if ((!csOp.opcode) && csIter.hasNext()) + { csIter.next(csOp); } //print(csOp.toSource()+" "+attOp.toSource()+" "+opOut.toSource()); //print(csOp.opcode+"/"+csOp.lines+"/"+csOp.attribs+"/"+lineAssem+"/"+lineIter+"/"+(lineIter?lineIter.hasNext():null)); //print("csOp: "+csOp.toSource()); - if ((! csOp.opcode) && (! attOp.opcode) && - (! lineAssem) && (! (lineIter && lineIter.hasNext()))) { + if ((!csOp.opcode) && (!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) + { break; // done } - else if (csOp.opcode == '=' && csOp.lines > 0 && (! csOp.attribs) && (! attOp.opcode) && - (! lineAssem) && (! (lineIter && lineIter.hasNext()))) { + else if (csOp.opcode == '=' && csOp.lines > 0 && (!csOp.attribs) && (!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) + { // skip multiple lines; this is what makes small changes not order of the document size mut.skipLines(csOp.lines); //print("skipped: "+csOp.lines); csOp.opcode = ''; } - else if (csOp.opcode == '+') { - if (csOp.lines > 1) { - var firstLineLen = csBank.indexOf('\n', csBankIndex) + 1 - csBankIndex; - Changeset.copyOp(csOp, opOut); - csOp.chars -= firstLineLen; - csOp.lines--; - opOut.lines = 1; - opOut.chars = firstLineLen; + else if (csOp.opcode == '+') + { + if (csOp.lines > 1) + { + var firstLineLen = csBank.indexOf('\n', csBankIndex) + 1 - csBankIndex; + Changeset.copyOp(csOp, opOut); + csOp.chars -= firstLineLen; + csOp.lines--; + opOut.lines = 1; + opOut.chars = firstLineLen; } - else { - Changeset.copyOp(csOp, opOut); - csOp.opcode = ''; + else + { + Changeset.copyOp(csOp, opOut); + csOp.opcode = ''; } outputMutOp(opOut); csBankIndex += opOut.chars; opOut.opcode = ''; } - else { - if ((! attOp.opcode) && isNextMutOp()) { - nextMutOp(attOp); + else + { + if ((!attOp.opcode) && isNextMutOp()) + { + nextMutOp(attOp); } //print("attOp: "+attOp.toSource()); Changeset._slicerZipperFunc(attOp, csOp, opOut, pool); - if (opOut.opcode) { - outputMutOp(opOut); - opOut.opcode = ''; + if (opOut.opcode) + { + outputMutOp(opOut); + opOut.opcode = ''; } } } - Changeset.assert(! lineAssem, "line assembler not finished"); + Changeset.assert(!lineAssem, "line assembler not finished"); mut.close(); //dmesg("-> "+lines.toSource()); }; -Changeset.joinAttributionLines = function(theAlines) { +Changeset.joinAttributionLines = function(theAlines) +{ var assem = Changeset.mergingOpAssembler(); - for(var i=0;i<theAlines.length;i++) { + for (var i = 0; i < theAlines.length; i++) + { var aline = theAlines[i]; var iter = Changeset.opIterator(aline); - while (iter.hasNext()) { + while (iter.hasNext()) + { assem.append(iter.next()); } } return assem.toString(); }; -Changeset.splitAttributionLines = function(attrOps, text) { +Changeset.splitAttributionLines = function(attrOps, text) +{ var iter = Changeset.opIterator(attrOps); var assem = Changeset.mergingOpAssembler(); var lines = []; var pos = 0; - function appendOp(op) { + function appendOp(op) + { assem.append(op); - if (op.lines > 0) { + if (op.lines > 0) + { lines.push(assem.toString()); assem.clear(); } pos += op.chars; } - while (iter.hasNext()) { + while (iter.hasNext()) + { var op = iter.next(); var numChars = op.chars; var numLines = op.lines; - while (numLines > 1) { - var newlineEnd = text.indexOf('\n', pos)+1; + while (numLines > 1) + { + var newlineEnd = text.indexOf('\n', pos) + 1; Changeset.assert(newlineEnd > 0, "newlineEnd <= 0 in splitAttributionLines"); op.chars = newlineEnd - pos; op.lines = 1; @@ -1142,7 +1487,8 @@ Changeset.splitAttributionLines = function(attrOps, text) { numChars -= op.chars; numLines -= op.lines; } - if (numLines == 1) { + if (numLines == 1) + { op.chars = numChars; op.lines = 1; } @@ -1152,11 +1498,13 @@ 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); var len1 = unpacked1.oldLen; @@ -1167,25 +1515,29 @@ 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)); //debugBuilder.append(','); //debugBuilder.append(Changeset.opString(op2)); //debugBuilder.append(' / '); - var op1code = op1.opcode; var op2code = op2.opcode; - if (op1code == '+' && op2code == '-') { + if (op1code == '+' && op2code == '-') + { bankIter1.skip(Math.min(op1.chars, op2.chars)); } Changeset._slicerZipperFunc(op1, op2, opOut, pool); - if (opOut.opcode == '+') { - if (op2code == '+') { - bankAssem.append(bankIter2.take(opOut.chars)); + if (opOut.opcode == '+') + { + if (op2code == '+') + { + bankAssem.append(bankIter2.take(opOut.chars)); } - else { - bankAssem.append(bankIter1.take(opOut.chars)); + else + { + bankAssem.append(bankIter1.take(opOut.chars)); } } @@ -1200,41 +1552,53 @@ 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 // is already present in the pool. - if (! pool) { + if (!pool) + { return never; } var attribNum = pool.putAttrib(attribPair, true); - if (attribNum < 0) { + if (attribNum < 0) + { return never; } - else { - var re = new RegExp('\\*'+Changeset.numToString(attribNum)+ - '(?!\\w)'); - return function(attribs) { + else + { + var re = new RegExp('\\*' + Changeset.numToString(attribNum) + '(?!\\w)'); + return function(attribs) + { return re.test(attribs); }; } - function never(attribs) { return false; } + + function never(attribs) + { + return false; + } }; -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; - if (spliceStart >= oldLen) { + if (spliceStart >= oldLen) + { spliceStart = oldLen - 1; } - if (numRemoved > oldFullText.length - spliceStart - 1) { + if (numRemoved > oldFullText.length - spliceStart - 1) + { numRemoved = oldFullText.length - spliceStart - 1; } - var oldText = oldFullText.substring(spliceStart, spliceStart+numRemoved); + var oldText = oldFullText.substring(spliceStart, spliceStart + numRemoved); var newLen = oldLen + newText.length - oldText.length; var assem = Changeset.smartOpAssembler(); @@ -1245,9 +1609,9 @@ Changeset.makeSplice = function(oldFullText, spliceStart, numRemoved, newText, o 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); var splices = []; @@ -1255,23 +1619,29 @@ Changeset.toSplices = function(cs) { var iter = Changeset.opIterator(unpacked.ops); var charIter = Changeset.stringIterator(unpacked.charBank); var inSplice = false; - while (iter.hasNext()) { + while (iter.hasNext()) + { var op = iter.next(); - if (op.opcode == '=') { + if (op.opcode == '=') + { oldPos += op.chars; inSplice = false; } - else { - if (! inSplice) { - splices.push([oldPos, oldPos, ""]); - inSplice = true; + else + { + if (!inSplice) + { + splices.push([oldPos, oldPos, ""]); + inSplice = true; } - if (op.opcode == '-') { - oldPos += op.chars; - splices[splices.length-1][1] += op.chars; + if (op.opcode == '-') + { + oldPos += op.chars; + splices[splices.length - 1][1] += op.chars; } - else if (op.opcode == '+') { - splices[splices.length-1][2] += charIter.take(op.chars); + else if (op.opcode == '+') + { + splices[splices.length - 1][2] += charIter.take(op.chars); } } } @@ -1279,46 +1649,56 @@ 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; var splices = Changeset.toSplices(cs); var lengthChangeSoFar = 0; - for(var i=0;i<splices.length;i++) { + for (var i = 0; i < splices.length; i++) + { var splice = splices[i]; var spliceStart = splice[0] + lengthChangeSoFar; var spliceEnd = splice[1] + lengthChangeSoFar; var newTextLength = splice[2].length; var thisLengthChange = newTextLength - (spliceEnd - spliceStart); - if (spliceStart <= newStartChar && spliceEnd >= newEndChar) { + if (spliceStart <= newStartChar && spliceEnd >= newEndChar) + { // splice fully replaces/deletes range // (also case that handles insertion at a collapsed selection) - if (insertionsAfter) { - newStartChar = newEndChar = spliceStart; + if (insertionsAfter) + { + newStartChar = newEndChar = spliceStart; } - else { - newStartChar = newEndChar = spliceStart + newTextLength; + else + { + newStartChar = newEndChar = spliceStart + newTextLength; } } - else if (spliceEnd <= newStartChar) { + else if (spliceEnd <= newStartChar) + { // splice is before range newStartChar += thisLengthChange; newEndChar += thisLengthChange; } - else if (spliceStart >= newEndChar) { + else if (spliceStart >= newEndChar) + { // splice is after range } - else if (spliceStart >= newStartChar && spliceEnd <= newEndChar) { + else if (spliceStart >= newStartChar && spliceEnd <= newEndChar) + { // splice is inside range newEndChar += thisLengthChange; } - else if (spliceEnd < newEndChar) { + else if (spliceEnd < newEndChar) + { // splice overlaps beginning of range newStartChar = spliceStart + newTextLength; newEndChar += thisLengthChange; } - else { + else + { // splice overlaps end of range newEndChar = spliceStart; } @@ -1329,38 +1709,45 @@ Changeset.characterRangeFollow = function(cs, startChar, endChar, insertionsAfte 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('$'); - if (dollarPos < 0) { + if (dollarPos < 0) + { dollarPos = cs.length; } 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); var newNum = newPool.putAttrib(pair); - return '*'+Changeset.numToString(newNum); + return '*' + Changeset.numToString(newNum); }) + fromDollar; }; -Changeset.makeAttribution = function(text) { +Changeset.makeAttribution = function(text) +{ var assem = Changeset.smartOpAssembler(); assem.appendOpWithText('+', text); return assem.toString(); }; // 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) { + if (dollarPos < 0) + { dollarPos = cs.length; } 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 ''; }); @@ -1368,26 +1755,33 @@ 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) { + if (dollarPos < 0) + { dollarPos = cs.length; } 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) { + if (n === true) + { return s; } - else if ((typeof n) === "number") { - return '*'+Changeset.numToString(n); + else if ((typeof n) === "number") + { + return '*' + Changeset.numToString(n); } - else { + else + { return ''; } }); @@ -1395,79 +1789,108 @@ Changeset.mapAttribNumbers = function(cs, func) { return newUpToDollar + cs.substring(dollarPos); }; -Changeset.makeAText = function(text, attribs) { - return { text: text, attribs: (attribs || Changeset.makeAttribution(text)) }; +Changeset.makeAText = function(text, attribs) +{ + return { + text: text, + attribs: (attribs || Changeset.makeAttribution(text)) + }; }; -Changeset.applyToAText = function(cs, atext, pool) { - return { text: Changeset.applyToText(cs, atext.text), - attribs: Changeset.applyToAttribution(cs, atext.attribs, pool) }; +Changeset.applyToAText = function(cs, atext, pool) +{ + return { + text: Changeset.applyToText(cs, atext.text), + attribs: Changeset.applyToAttribution(cs, atext.attribs, pool) + }; }; -Changeset.cloneAText = function(atext) { - return { text: atext.text, attribs: atext.attribs }; +Changeset.cloneAText = function(atext) +{ + return { + text: atext.text, + attribs: atext.attribs + }; }; -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); var op = Changeset.newOp(); - while (iter.hasNext()) { + while (iter.hasNext()) + { iter.next(op); - if (! iter.hasNext()) { + if (!iter.hasNext()) + { // last op, exclude final newline - if (op.lines <= 1) { - op.lines = 0; - op.chars--; - if (op.chars) { - assem.append(op); - } - } - else { - var nextToLastNewlineEnd = - atext.text.lastIndexOf('\n', atext.text.length-2) + 1; - var lastLineLength = atext.text.length - nextToLastNewlineEnd - 1; - op.lines--; - op.chars -= (lastLineLength + 1); - assem.append(op); - op.lines = 0; - op.chars = lastLineLength; - if (op.chars) { - assem.append(op); - } - } - } - else { + if (op.lines <= 1) + { + op.lines = 0; + op.chars--; + if (op.chars) + { + assem.append(op); + } + } + else + { + var nextToLastNewlineEnd = atext.text.lastIndexOf('\n', atext.text.length - 2) + 1; + var lastLineLength = atext.text.length - nextToLastNewlineEnd - 1; + op.lines--; + op.chars -= (lastLineLength + 1); + assem.append(op); + op.lines = 0; + op.chars = lastLineLength; + if (op.chars) + { + assem.append(op); + } + } + } + else + { assem.append(op); } } }; -Changeset.prepareForWire = function(cs, pool) { +Changeset.prepareForWire = function(cs, pool) +{ var newPool = new AttribPool(); var newCs = Changeset.moveOpsToNewPool(cs, pool, newPool); - return {translated: newCs, pool: newPool}; + return { + translated: newCs, + pool: newPool + }; }; -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) { - if (pool.getAttribKey(n) == key) { + if (attribs) + { + Changeset.eachAttribNumber(attribs, function(n) + { + if (pool.getAttribKey(n) == key) + { value = pool.getAttribValue(n); } }); @@ -1475,32 +1898,36 @@ Changeset.attribsAttributeValue = function(attribs, key, pool) { return value; }; -Changeset.builder = function(oldLen) { +Changeset.builder = function(oldLen) +{ var assem = Changeset.smartOpAssembler(); var o = Changeset.newOp(); var charBank = Changeset.stringAssembler(); 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)) || ''; + o.attribs = (attribs && Changeset.makeAttribsString('=', attribs, pool)) || ''; o.chars = N; o.lines = (L || 0); 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 = ''; o.chars = N; @@ -1508,35 +1935,42 @@ Changeset.builder = function(oldLen) { assem.append(o); return self; }, - toString: function() { + toString: function() + { assem.endDocument(); var newLen = oldLen + assem.getLengthChange(); - return Changeset.pack(oldLen, newLen, assem.toString(), - charBank.toString()); + return Changeset.pack(oldLen, newLen, assem.toString(), charBank.toString()); } }; 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) { + if (!attribs) + { return ''; } - else if ((typeof attribs) == "string") { + else if ((typeof attribs) == "string") + { return attribs; } - else if (pool && attribs && attribs.length) { - if (attribs.length > 1) { + else if (pool && attribs && attribs.length) + { + if (attribs.length > 1) + { attribs = attribs.slice(); attribs.sort(); } var result = []; - for(var i=0;i<attribs.length;i++) { + for (var i = 0; i < attribs.length; i++) + { var pair = attribs[i]; - if (opcode == '=' || (opcode == '+' && pair[1])) { - result.push('*'+Changeset.numToString(pool.putAttrib(pair))); + if (opcode == '=' || (opcode == '+' && pair[1])) + { + result.push('*' + Changeset.numToString(pool.putAttrib(pair))); } } return result.join(''); @@ -1544,28 +1978,33 @@ 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(); var attOp = Changeset.newOp(); var csOp = Changeset.newOp(); var opOut = Changeset.newOp(); - function doCsOp() { - if (csOp.chars) { - while (csOp.opcode && (attOp.opcode || iter.hasNext())) { - if (! attOp.opcode) iter.next(attOp); - - if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars && - attOp.lines > 0 && csOp.lines <= 0) { - csOp.lines++; - } + function doCsOp() + { + if (csOp.chars) + { + while (csOp.opcode && (attOp.opcode || iter.hasNext())) + { + if (!attOp.opcode) iter.next(attOp); + + if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars && attOp.lines > 0 && csOp.lines <= 0) + { + csOp.lines++; + } - Changeset._slicerZipperFunc(attOp, csOp, opOut, null); - if (opOut.opcode) { - assem.append(opOut); - opOut.opcode = ''; - } + Changeset._slicerZipperFunc(attOp, csOp, opOut, null); + if (opOut.opcode) + { + assem.append(opOut); + opOut.opcode = ''; + } } } } @@ -1575,16 +2014,20 @@ Changeset.subattribution = function(astr, start, optEnd) { doCsOp(); - if (optEnd === undefined) { - if (attOp.opcode) { + if (optEnd === undefined) + { + if (attOp.opcode) + { assem.append(attOp); } - while (iter.hasNext()) { + while (iter.hasNext()) + { iter.next(attOp); assem.append(attOp); } } - else { + else + { csOp.opcode = '='; csOp.chars = optEnd - start; doCsOp(); @@ -1593,39 +2036,57 @@ 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) { + + + function lines_get(idx) + { + if (lines.get) + { return lines.get(idx); } - else { + else + { return lines[idx]; } } - function lines_length() { - if ((typeof lines.length) == "number") { + + function lines_length() + { + if ((typeof lines.length) == "number") + { return lines.length; } - else { + else + { return lines.length(); } } - function alines_get(idx) { - if (alines.get) { + + function alines_get(idx) + { + if (alines.get) + { return alines.get(idx); } - else { + else + { return alines[idx]; } } - function alines_length() { - if ((typeof alines.length) == "number") { + + function alines_length() + { + if ((typeof alines.length) == "number") + { return alines.length; } - else { + else + { return alines.length(); } } @@ -1640,75 +2101,91 @@ Changeset.inverse = function(cs, lines, alines, pool) { var csIter = Changeset.opIterator(unpacked.ops); var builder = Changeset.builder(unpacked.newLen); - function consumeAttribRuns(numChars, func/*(len, attribs, endsLine)*/) { + function consumeAttribRuns(numChars, func /*(len, attribs, endsLine)*/ ) + { - if ((! curLineOpIter) || (curLineOpIterLine != curLine)) { + if ((!curLineOpIter) || (curLineOpIterLine != curLine)) + { // create curLineOpIter and advance it to curChar curLineOpIter = Changeset.opIterator(alines_get(curLine)); curLineOpIterLine = curLine; var indexIntoLine = 0; var done = false; - while (! done) { - curLineOpIter.next(curLineNextOp); - if (indexIntoLine + curLineNextOp.chars >= curChar) { - curLineNextOp.chars -= (curChar - indexIntoLine); - done = true; - } - else { - indexIntoLine += curLineNextOp.chars; - } + while (!done) + { + curLineOpIter.next(curLineNextOp); + if (indexIntoLine + curLineNextOp.chars >= curChar) + { + curLineNextOp.chars -= (curChar - indexIntoLine); + done = true; + } + else + { + indexIntoLine += curLineNextOp.chars; + } } } - while (numChars > 0) { - if ((! curLineNextOp.chars) && (! curLineOpIter.hasNext())) { - curLine++; - curChar = 0; - curLineOpIterLine = curLine; - curLineNextOp.chars = 0; - curLineOpIter = Changeset.opIterator(alines_get(curLine)); + while (numChars > 0) + { + if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) + { + curLine++; + curChar = 0; + curLineOpIterLine = curLine; + curLineNextOp.chars = 0; + curLineOpIter = Changeset.opIterator(alines_get(curLine)); } - if (! curLineNextOp.chars) { - curLineOpIter.next(curLineNextOp); + if (!curLineNextOp.chars) + { + curLineOpIter.next(curLineNextOp); } var charsToUse = Math.min(numChars, curLineNextOp.chars); - func(charsToUse, curLineNextOp.attribs, - charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0); + func(charsToUse, curLineNextOp.attribs, charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0); numChars -= charsToUse; curLineNextOp.chars -= charsToUse; curChar += charsToUse; } - if ((! curLineNextOp.chars) && (! curLineOpIter.hasNext())) { + if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) + { curLine++; curChar = 0; } } - function skip(N, L) { - if (L) { + function skip(N, L) + { + if (L) + { curLine += L; curChar = 0; } - else { - if (curLineOpIter && curLineOpIterLine == curLine) { - consumeAttribRuns(N, function() {}); + else + { + if (curLineOpIter && curLineOpIterLine == curLine) + { + consumeAttribRuns(N, function() + {}); } - else { - curChar += N; + else + { + curChar += N; } } } - function nextText(numChars) { + function nextText(numChars) + { var len = 0; var assem = Changeset.stringAssembler(); var firstString = lines_get(curLine).substring(curChar); len += firstString.length; assem.append(firstString); - var lineNum = curLine+1; - while (len < numChars) { + var lineNum = curLine + 1; + while (len < numChars) + { var nextString = lines_get(lineNum); len += nextString.length; assem.append(nextString); @@ -1718,11 +2195,14 @@ Changeset.inverse = function(cs, lines, alines, pool) { return assem.toString().substring(0, numChars); } - function cachedStrFunc(func) { + function cachedStrFunc(func) + { var cache = {}; - return function(s) { - if (! cache[s]) { - cache[s] = func(s); + return function(s) + { + if (!cache[s]) + { + cache[s] = func(s); } return cache[s]; }; @@ -1730,46 +2210,58 @@ Changeset.inverse = function(cs, lines, alines, pool) { var attribKeys = []; var attribValues = []; - while (csIter.hasNext()) { + while (csIter.hasNext()) + { var csOp = csIter.next(); - if (csOp.opcode == '=') { - if (csOp.attribs) { - attribKeys.length = 0; - attribValues.length = 0; - Changeset.eachAttribNumber(csOp.attribs, function(n) { - attribKeys.push(pool.getAttribKey(n)); - attribValues.push(pool.getAttribValue(n)); - }); - var undoBackToAttribs = cachedStrFunc(function(attribs) { - var backAttribs = []; - for(var i=0;i<attribKeys.length;i++) { - var appliedKey = attribKeys[i]; - var appliedValue = attribValues[i]; - var oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, pool); - if (appliedValue != oldValue) { - backAttribs.push([appliedKey, oldValue]); - } - } - return Changeset.makeAttribsString('=', backAttribs, pool); - }); - consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) { - builder.keep(len, endsLine ? 1 : 0, undoBackToAttribs(attribs)); - }); - } - else { - skip(csOp.chars, csOp.lines); - builder.keep(csOp.chars, csOp.lines); - } - } - else if (csOp.opcode == '+') { + if (csOp.opcode == '=') + { + if (csOp.attribs) + { + attribKeys.length = 0; + attribValues.length = 0; + Changeset.eachAttribNumber(csOp.attribs, function(n) + { + attribKeys.push(pool.getAttribKey(n)); + attribValues.push(pool.getAttribValue(n)); + }); + var undoBackToAttribs = cachedStrFunc(function(attribs) + { + var backAttribs = []; + for (var i = 0; i < attribKeys.length; i++) + { + var appliedKey = attribKeys[i]; + var appliedValue = attribValues[i]; + var oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, pool); + if (appliedValue != oldValue) + { + backAttribs.push([appliedKey, oldValue]); + } + } + return Changeset.makeAttribsString('=', backAttribs, pool); + }); + consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) + { + builder.keep(len, endsLine ? 1 : 0, undoBackToAttribs(attribs)); + }); + } + else + { + skip(csOp.chars, csOp.lines); + builder.keep(csOp.chars, csOp.lines); + } + } + else if (csOp.opcode == '+') + { builder.remove(csOp.chars, csOp.lines); } - else if (csOp.opcode == '-') { + else if (csOp.opcode == '-') + { var textBank = nextText(csOp.chars); var textBankIndex = 0; - consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) { - builder.insert(textBank.substr(textBankIndex, len), attribs); - textBankIndex += len; + consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) + { + builder.insert(textBank.substr(textBankIndex, len), attribs); + textBankIndex += len; }); } } @@ -1778,8 +2270,8 @@ Changeset.inverse = function(cs, lines, alines, pool) { }; // %CLIENT FILE ENDS HERE% - -Changeset.follow = function(cs1, cs2, reverseInsertOrder, pool) { +Changeset.follow = function(cs1, cs2, reverseInsertOrder, pool) +{ var unpacked1 = Changeset.unpack(cs1); var unpacked2 = Changeset.unpack(cs2); var len1 = unpacked1.oldLen; @@ -1792,137 +2284,174 @@ Changeset.follow = function(cs1, cs2, reverseInsertOrder, pool) { var oldPos = 0; var newLen = 0; - var hasInsertFirst = Changeset.attributeTester(['insertorder','first'], - pool); + var hasInsertFirst = Changeset.attributeTester(['insertorder', 'first'], pool); - var newOps = Changeset.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function(op1, op2, opOut) { - if (op1.opcode == '+' || op2.opcode == '+') { + var newOps = Changeset.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function(op1, op2, opOut) + { + if (op1.opcode == '+' || op2.opcode == '+') + { var whichToDo; - if (op2.opcode != '+') { - whichToDo = 1; + if (op2.opcode != '+') + { + whichToDo = 1; } - else if (op1.opcode != '+') { - whichToDo = 2; + else if (op1.opcode != '+') + { + whichToDo = 2; } - else { - // both + - var firstChar1 = chars1.peek(1); - var firstChar2 = chars2.peek(1); + else + { + // both + + var firstChar1 = chars1.peek(1); + var firstChar2 = chars2.peek(1); var insertFirst1 = hasInsertFirst(op1.attribs); var insertFirst2 = hasInsertFirst(op2.attribs); - if (insertFirst1 && ! insertFirst2) { + if (insertFirst1 && !insertFirst2) + { whichToDo = 1; } - else if (insertFirst2 && ! insertFirst1) { + else if (insertFirst2 && !insertFirst1) + { + whichToDo = 2; + } + // insert string that doesn't start with a newline first so as not to break up lines + else if (firstChar1 == '\n' && firstChar2 != '\n') + { whichToDo = 2; } - // insert string that doesn't start with a newline first so as not to break up lines - else if (firstChar1 == '\n' && firstChar2 != '\n') { - whichToDo = 2; - } - else if (firstChar1 != '\n' && firstChar2 == '\n') { - whichToDo = 1; - } - // break symmetry: - else if (reverseInsertOrder) { - whichToDo = 2; - } - else { - whichToDo = 1; - } - } - if (whichToDo == 1) { - chars1.skip(op1.chars); - opOut.opcode = '='; - opOut.lines = op1.lines; - opOut.chars = op1.chars; - opOut.attribs = ''; - op1.opcode = ''; - } - else { - // whichToDo == 2 - chars2.skip(op2.chars); - Changeset.copyOp(op2, opOut); - op2.opcode = ''; - } - } - else if (op1.opcode == '-') { - if (! op2.opcode) { - op1.opcode = ''; - } - else { - if (op1.chars <= op2.chars) { - op2.chars -= op1.chars; - op2.lines -= op1.lines; - op1.opcode = ''; - if (! op2.chars) { - op2.opcode = ''; - } - } - else { - op1.chars -= op2.chars; - op1.lines -= op2.lines; - op2.opcode = ''; - } - } - } - else if (op2.opcode == '-') { + else if (firstChar1 != '\n' && firstChar2 == '\n') + { + whichToDo = 1; + } + // break symmetry: + else if (reverseInsertOrder) + { + whichToDo = 2; + } + else + { + whichToDo = 1; + } + } + if (whichToDo == 1) + { + chars1.skip(op1.chars); + opOut.opcode = '='; + opOut.lines = op1.lines; + opOut.chars = op1.chars; + opOut.attribs = ''; + op1.opcode = ''; + } + else + { + // whichToDo == 2 + chars2.skip(op2.chars); + Changeset.copyOp(op2, opOut); + op2.opcode = ''; + } + } + else if (op1.opcode == '-') + { + if (!op2.opcode) + { + op1.opcode = ''; + } + else + { + if (op1.chars <= op2.chars) + { + op2.chars -= op1.chars; + op2.lines -= op1.lines; + op1.opcode = ''; + if (!op2.chars) + { + op2.opcode = ''; + } + } + else + { + op1.chars -= op2.chars; + op1.lines -= op2.lines; + op2.opcode = ''; + } + } + } + else if (op2.opcode == '-') + { Changeset.copyOp(op2, opOut); - if (! op1.opcode) { - op2.opcode = ''; - } - else if (op2.chars <= op1.chars) { - // delete part or all of a keep - op1.chars -= op2.chars; - op1.lines -= op2.lines; - op2.opcode = ''; - if (! op1.chars) { - op1.opcode = ''; - } - } - else { - // delete all of a keep, and keep going - opOut.lines = op1.lines; - opOut.chars = op1.chars; - op2.lines -= op1.lines; - op2.chars -= op1.chars; - op1.opcode = ''; - } - } - else if (! op1.opcode) { + if (!op1.opcode) + { + op2.opcode = ''; + } + else if (op2.chars <= op1.chars) + { + // delete part or all of a keep + op1.chars -= op2.chars; + op1.lines -= op2.lines; + op2.opcode = ''; + if (!op1.chars) + { + op1.opcode = ''; + } + } + else + { + // delete all of a keep, and keep going + opOut.lines = op1.lines; + opOut.chars = op1.chars; + op2.lines -= op1.lines; + op2.chars -= op1.chars; + op1.opcode = ''; + } + } + else if (!op1.opcode) + { Changeset.copyOp(op2, opOut); op2.opcode = ''; } - else if (! op2.opcode) { + else if (!op2.opcode) + { Changeset.copyOp(op1, opOut); op1.opcode = ''; } - else { + else + { // both keeps opOut.opcode = '='; opOut.attribs = Changeset.followAttributes(op1.attribs, op2.attribs, pool); - if (op1.chars <= op2.chars) { - opOut.chars = op1.chars; - opOut.lines = op1.lines; - op2.chars -= op1.chars; - op2.lines -= op1.lines; - op1.opcode = ''; - if (! op2.chars) { - op2.opcode = ''; - } - } - else { - opOut.chars = op2.chars; - opOut.lines = op2.lines; - op1.chars -= op2.chars; - op1.lines -= op2.lines; - op2.opcode = ''; - } - } - switch (opOut.opcode) { - case '=': oldPos += opOut.chars; newLen += opOut.chars; break; - case '-': oldPos += opOut.chars; break; - case '+': newLen += opOut.chars; break; + if (op1.chars <= op2.chars) + { + opOut.chars = op1.chars; + opOut.lines = op1.lines; + op2.chars -= op1.chars; + op2.lines -= op1.lines; + op1.opcode = ''; + if (!op2.chars) + { + op2.opcode = ''; + } + } + else + { + opOut.chars = op2.chars; + opOut.lines = op2.lines; + op1.chars -= op2.chars; + op1.lines -= op2.lines; + op2.opcode = ''; + } + } + switch (opOut.opcode) + { + case '=': + oldPos += opOut.chars; + newLen += opOut.chars; + break; + case '-': + oldPos += opOut.chars; + break; + case '+': + newLen += opOut.chars; + break; } }); newLen += oldLen - oldPos; @@ -1930,37 +2459,44 @@ Changeset.follow = function(cs1, cs2, reverseInsertOrder, pool) { return Changeset.pack(oldLen, newLen, newOps, unpacked2.charBank); }; -Changeset.followAttributes = function(att1, att2, pool) { +Changeset.followAttributes = function(att1, att2, pool) +{ // The merge of two sets of attribute changes to the same text // takes the lexically-earlier value if there are two values // for the same key. Otherwise, all key/value changes from // both attribute sets are taken. This operation is the "follow", // so a set of changes is produced that can be applied to att1 // to produce the merged set. - if ((! att2) || (! pool)) return ''; - if (! att1) return att2; + if ((!att2) || (!pool)) return ''; + if (!att1) return att2; var atts = []; - att2.replace(/\*([0-9a-z]+)/g, function(_, a) { + att2.replace(/\*([0-9a-z]+)/g, function(_, a) + { atts.push(pool.getAttrib(Changeset.parseNum(a))); return ''; }); - att1.replace(/\*([0-9a-z]+)/g, function(_, a) { + att1.replace(/\*([0-9a-z]+)/g, function(_, a) + { var pair1 = pool.getAttrib(Changeset.parseNum(a)); - for(var i=0;i<atts.length;i++) { + for (var i = 0; i < atts.length; i++) + { var pair2 = atts[i]; - if (pair1[0] == pair2[0]) { - if (pair1[1] <= pair2[1]) { - // winner of merge is pair1, delete this attribute - atts.splice(i, 1); - } - break; + if (pair1[0] == pair2[0]) + { + if (pair1[1] <= pair2[1]) + { + // winner of merge is pair1, delete this attribute + atts.splice(i, 1); + } + break; } } return ''; }); // we've only removed attributes, so they're already sorted var buf = Changeset.stringAssembler(); - for(var i=0;i<atts.length;i++) { + for (var i = 0; i < atts.length; i++) + { buf.append('*'); buf.append(Changeset.numToString(pool.putAttrib(atts[i]))); } diff --git a/static/js/easysync2_client.js b/static/js/easysync2_client.js index ca5ee3da..5d0e582d 100644 --- a/static/js/easysync2_client.js +++ b/static/js/easysync2_client.js @@ -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; diff --git a/static/js/json2.js b/static/js/json2.js index b4c02d3f..898e8369 100644 --- a/static/js/json2.js +++ b/static/js/json2.js @@ -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 f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; +(function() +{ + "use strict"; + + 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; - - - 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 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); + // What happens next depends on the value's type. + switch (typeof value) + { + case 'string': + return quote(value); + + case 'number': + + // JSON numbers must be finite. Encode non-finite numbers as null. + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + + // 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': + + // Due to a specification blunder in ECMAScript, typeof null is 'object', + // so watch out for that case. + if (!value) + { + return 'null'; + } + + // 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'; } -// What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - -// JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - -// 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': - -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. - - if (!value) { - return 'null'; - } - -// 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 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); } - -// 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'); + } + 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); } + } + } + } -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. - - return str('', {'': value}); - }; + // 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 JSON object does not yet have a parse method, give it one. - - if (typeof JSON.parse !== 'function') { - JSON.parse = function (text, reviver) { - -// 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; - - 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 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 + }); + }; + } + + + // If the JSON object does not yet have a parse method, give it one. + if (typeof JSON.parse !== 'function') + { + JSON.parse = function(text, reviver) + { + + // 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; + + 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]; + } } - -// If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - } + } + } + 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'); + }; + } }()); diff --git a/static/js/linestylefilter.js b/static/js/linestylefilter.js index 421220e4..76f115f4 100644 --- a/static/js/linestylefilter.js +++ b/static/js/linestylefilter.js @@ -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 (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 + }, " ", " ", ""); } - 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; - - 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]; - } + 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]; + } } 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, ''); }; diff --git a/static/js/linestylefilter_client.js b/static/js/linestylefilter_client.js index c8509367..b1bdae6e 100644 --- a/static/js/linestylefilter_client.js +++ b/static/js/linestylefilter_client.js @@ -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; diff --git a/static/js/pad2.js b/static/js/pad2.js index 7f9d3a73..40e97f01 100644 --- a/static/js/pad2.js +++ b/static/js/pad2.js @@ -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.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) + 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) + { + 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) { - 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; + socket.disconnect(); + padconnectionstatus.disconnected("userdup"); + return; } - //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); - }, - 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 { + 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); + } + 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(); } }; diff --git a/static/js/pad_connectionstatus.js b/static/js/pad_connectionstatus.js index 6fa9b376..e5f753aa 100644 --- a/static/js/pad_connectionstatus.js +++ b/static/js/pad_connectionstatus.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. @@ -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; }()); diff --git a/static/js/pad_cookie.js b/static/js/pad_cookie.js index 3cc31edf..5db6a2ca 100644 --- a/static/js/pad_cookie.js +++ b/static/js/pad_cookie.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. @@ -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; -}());
\ No newline at end of file +}()); diff --git a/static/js/pad_docbar.js b/static/js/pad_docbar.js index 586b20f0..6ee38f68 100644 --- a/static/js/pad_docbar.js +++ b/static/js/pad_docbar.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. @@ -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; diff --git a/static/js/pad_editbar.js b/static/js/pad_editbar.js index 8f410a53..27753eec 100644 --- a/static/js/pad_editbar.js +++ b/static/js/pad_editbar.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. @@ -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(); } } diff --git a/static/js/pad_editor.js b/static/js/pad_editor.js index 436df2db..93ef3629 100644 --- a/static/js/pad_editor.js +++ b/static/js/pad_editor.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. @@ -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; }()); - diff --git a/static/js/pad_impexp.js b/static/js/pad_impexp.js index e9283326..308a6d9d 100644 --- a/static/js/pad_impexp.js +++ b/static/js/pad_impexp.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. @@ -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); diff --git a/static/js/pad_modals.js b/static/js/pad_modals.js index 0d0f57f0..ccd4607e 100644 --- a/static/js/pad_modals.js +++ b/static/js/pad_modals.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. @@ -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(); } diff --git a/static/js/pad_savedrevs.js b/static/js/pad_savedrevs.js index 76b0649b..ae4220cd 100644 --- a/static/js/pad_savedrevs.js +++ b/static/js/pad_savedrevs.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. @@ -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; -}());
\ No newline at end of file +}()); diff --git a/static/js/pad_userlist.js b/static/js/pad_userlist.js index 9d2ff54d..2e6238d2 100644 --- a/static/js/pad_userlist.js +++ b/static/js/pad_userlist.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. @@ -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 + } + + guestPromptFlashState = 1 - guestPromptFlashState; + if (guestPromptFlashState) + { + prompts.css('background', '#ffa'); + } + else + { + prompts.css('background', '#ffe'); + } - return true; - }, 1000); + 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 - } + } } diff --git a/static/js/pad_utils.js b/static/js/pad_utils.js index d5ca1da7..fc88553d 100644 --- a/static/js/pad_utils.js +++ b/static/js/pad_utils.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. @@ -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; } }); diff --git a/static/js/plugins.js b/static/js/plugins.js index 0a020f09..2ebf98f3 100644 --- a/static/js/plugins.js +++ b/static/js/plugins.js @@ -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 || ""); } }; diff --git a/static/js/skiplist.js b/static/js/skiplist.js index e6c2e042..c2187388 100644 --- a/static/js/skiplist.js +++ b/static/js/skiplist.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,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; } diff --git a/static/js/undo-xpopup.js b/static/js/undo-xpopup.js index 89cfb4df..2fc0ee1a 100644 --- a/static/js/undo-xpopup.js +++ b/static/js/undo-xpopup.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. @@ -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; } diff --git a/static/js/undomodule.js b/static/js/undomodule.js index b8a56f91..b070ce25 100644 --- a/static/js/undomodule.js +++ b/static/js/undomodule.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. @@ -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 -})();
\ No newline at end of file + return { + clearHistory: clearHistory, + reportEvent: reportEvent, + reportExternalChange: reportExternalChange, + performUndo: performUndo, + performRedo: performRedo, + enabled: true, + apool: null + }; // apool is filled in by caller +})(); diff --git a/static/js/virtual_lines.js b/static/js/virtual_lines.js index 86e3deae..e27e4af4 100644 --- a/static/js/virtual_lines.js +++ b/static/js/virtual_lines.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. @@ -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; } |