diff options
Diffstat (limited to 'trunk/infrastructure/ace')
-rw-r--r-- | trunk/infrastructure/ace/README | 69 | ||||
-rwxr-xr-x | trunk/infrastructure/ace/bin/backup.sh | 17 | ||||
-rwxr-xr-x | trunk/infrastructure/ace/bin/make | 1 | ||||
-rwxr-xr-x | trunk/infrastructure/ace/bin/publish.sh | 19 | ||||
-rw-r--r-- | trunk/infrastructure/ace/notes.txt | 1 | ||||
-rw-r--r-- | trunk/infrastructure/ace/www/colorutils.js | 10 | ||||
-rw-r--r-- | trunk/infrastructure/ace/www/contentcollector.js | 12 | ||||
-rw-r--r-- | trunk/infrastructure/ace/www/cssmanager.js | 18 | ||||
-rw-r--r-- | trunk/infrastructure/ace/www/domline.js | 10 | ||||
-rw-r--r-- | trunk/infrastructure/ace/www/easy_sync.js | 74 | ||||
-rw-r--r-- | trunk/infrastructure/ace/www/easysync2.js | 12 | ||||
-rw-r--r-- | trunk/infrastructure/ace/www/easysync2_tests.js | 12 | ||||
-rw-r--r-- | trunk/infrastructure/ace/www/linestylefilter.js | 12 |
13 files changed, 151 insertions, 116 deletions
diff --git a/trunk/infrastructure/ace/README b/trunk/infrastructure/ace/README new file mode 100644 index 0000000..275684f --- /dev/null +++ b/trunk/infrastructure/ace/README @@ -0,0 +1,69 @@ +===== +ACE2 (originally AppJet Code Editor) +===== + +(This doc started Dec 2009 by dgreenspan.) + +ACE2 is EtherPad's editor, a content-editable-based rich text editor +that supports IE6+, FF(2?/)3+, Safari(3?/)4+. It supports +collaborative editing using operation transforms (easysync2), +undo/redo, copy/paste. + +The name "ACE2" is because this is a rewrite of aiba's original +content-editable AppJet Code Editor. + +== Building it + +In this directory, run `bin/make normal etherpad` (requires scala), +which generates `build/ace2.js` and copies this and other files into +the etherpad source tree. To have the script keep running and +automatically rebuild when source files change, run `bin/make auto +etherpad`. + +The original reason for the build process was that ACE needs to +construct two nested iframes on the client, and to do this without +incurring round-trips to the server, it programmatically loads code +into the outer iframe that loads code into the inner iframe; so the +bulk of ACE's code is compressed into a string literal (twice). Later +on, refactoring meant that the source is also divided into more than a +dozen files that the build script combines, and some are also +server-side EtherPad modules or client-side libraries (or both). + +The master copy of the operational transform (OT) library, easysync2, +lives here. + +In the early days it was possible to run ACE in a sort of development +mode, without the compression and iframe injection, by running it out +of the 'www' directory, but this is no longer possible. + +== Browser support + +We went out of our way to support IE6+. IE's design-mode is quite +robust, though there are some differences in manipulation of the +selection and insertion point and in the DOM representation we had to +use. + +We don't support Opera. Opera is a problematic case, because +apparently JS running in different iframes is run concurrently, not +linearized into a single event thread. This seems to be a rather +obscure fact and is almost difficult to believe. As if iframes don't +complicate scripting enough! + +== Syntax highlighting + +Though syntax highlighting predated rich text as the original +motivation for the design of ACE, support was eventually dropped in +EtherPad. At first the plan was to generalize it to other programming +languages, but the task was deprioritized (and difficult), and during +subsequent optimization and refactoring of ACE, calls to the +incremental lexer were stripped out because they complicated things. + +One plan for multi-language syntax highlighting, never implemented, +was to calculate syntax highlighting on the server as a sort of "style +overlay" and feed updates to the client along with updates to the +document text. + +== Changeset format + +See easysync-notes.txt for some notes on the changeset format, which +was redesigned with the advent of rich text. diff --git a/trunk/infrastructure/ace/bin/backup.sh b/trunk/infrastructure/ace/bin/backup.sh deleted file mode 100755 index 82b75cb..0000000 --- a/trunk/infrastructure/ace/bin/backup.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# 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. -# See the License for the specific language governing permissions and -# limitations under the License. - -cp -r .. ~/Dropbox/backups/ace2-`date '+%s'` #&& dropbox-backup.sh diff --git a/trunk/infrastructure/ace/bin/make b/trunk/infrastructure/ace/bin/make index 2237df0..dad11ff 100755 --- a/trunk/infrastructure/ace/bin/make +++ b/trunk/infrastructure/ace/bin/make @@ -7,6 +7,7 @@ import java.io._; def superpack(input: String): String = { // this function is self-contained; takes a string, returns an expression // that evaluates to that string + // XXX (This compresses well but decompression is too slow) // constraints on special chars: // - this string must be able to go in a character class diff --git a/trunk/infrastructure/ace/bin/publish.sh b/trunk/infrastructure/ace/bin/publish.sh deleted file mode 100755 index 2d442d0..0000000 --- a/trunk/infrastructure/ace/bin/publish.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# 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. -# See the License for the specific language governing permissions and -# limitations under the License. - -rm -rf ~/Dropbox/Public/ace2 -mkdir ~/Dropbox/Public/ace2 -cp -r ace2.js ace2_common.js ace2_outer.js dev.html editor.css index.html firebug jquery-1.2.1.js magicdom.js skiplist.js notes.txt ~/Dropbox/Public/ace2/ diff --git a/trunk/infrastructure/ace/notes.txt b/trunk/infrastructure/ace/notes.txt index e59cf0d..d9e1fda 100644 --- a/trunk/infrastructure/ace/notes.txt +++ b/trunk/infrastructure/ace/notes.txt @@ -1,3 +1,4 @@ +XXX This is a scratch file I used for notes; information may be out of date or random -- dgreenspan - rename ace2.js to ace.js diff --git a/trunk/infrastructure/ace/www/colorutils.js b/trunk/infrastructure/ace/www/colorutils.js index afee1d6..bb61de3 100644 --- a/trunk/infrastructure/ace/www/colorutils.js +++ b/trunk/infrastructure/ace/www/colorutils.js @@ -1,12 +1,14 @@ +// THIS FILE IS ALSO SERVED AS CLIENT-SIDE JS + /** * 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 +16,6 @@ * limitations under the License. */ -// THIS FILE IS ALSO SERVED AS CLIENT-SIDE JS - var colorutils = {}; // "#ffffff" or "#fff" or "ffffff" or "fff" to [1.0, 1.0, 1.0] diff --git a/trunk/infrastructure/ace/www/contentcollector.js b/trunk/infrastructure/ace/www/contentcollector.js index 8e094b2..c5d8ddb 100644 --- a/trunk/infrastructure/ace/www/contentcollector.js +++ b/trunk/infrastructure/ace/www/contentcollector.js @@ -1,12 +1,15 @@ +// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector +// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset") + /** * 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 +17,6 @@ * limitations under the License. */ -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector -// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset") - var _MAX_LIST_LEVEL = 8; function sanitizeUnicode(s) { diff --git a/trunk/infrastructure/ace/www/cssmanager.js b/trunk/infrastructure/ace/www/cssmanager.js index bdd6aef..a5c549b 100644 --- a/trunk/infrastructure/ace/www/cssmanager.js +++ b/trunk/infrastructure/ace/www/cssmanager.js @@ -1,12 +1,14 @@ + + /** * 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 +16,6 @@ * limitations under the License. */ - - function makeCSSManager(emptyStylesheetTitle) { function getSheetByTitle(title) { @@ -37,9 +37,9 @@ function makeCSSManager(emptyStylesheetTitle) { return t; } } - return null; + return null; }*/ - + var browserSheet = getSheetByTitle(emptyStylesheetTitle); //var browserTag = getSheetTagByTitle(emptyStylesheetTitle); function browserRules() { return (browserSheet.cssRules || browserSheet.rules); } @@ -59,7 +59,7 @@ function makeCSSManager(emptyStylesheetTitle) { return i; } } - return -1; + return -1; } function selectorStyle(selector) { @@ -80,7 +80,7 @@ function makeCSSManager(emptyStylesheetTitle) { selectorList.splice(i, 1); } } - + return {selectorStyle:selectorStyle, removeSelectorStyle:removeSelectorStyle, info: function() { return selectorList.length+":"+browserRules().length; diff --git a/trunk/infrastructure/ace/www/domline.js b/trunk/infrastructure/ace/www/domline.js index d66cecd..70f86cc 100644 --- a/trunk/infrastructure/ace/www/domline.js +++ b/trunk/infrastructure/ace/www/domline.js @@ -1,12 +1,14 @@ +// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline + /** * 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 +16,6 @@ * limitations under the License. */ -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline - var domline = {}; domline.noop = function() {}; domline.identity = function(x) { return x; }; diff --git a/trunk/infrastructure/ace/www/easy_sync.js b/trunk/infrastructure/ace/www/easy_sync.js index d4d309b..86a4327 100644 --- a/trunk/infrastructure/ace/www/easy_sync.js +++ b/trunk/infrastructure/ace/www/easy_sync.js @@ -1,12 +1,14 @@ +// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync1 + /** * 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 +16,6 @@ * limitations under the License. */ -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync1 - function Changeset(arg) { var array; @@ -38,7 +38,7 @@ function Changeset(arg) { else array = arg; array.isChangeset = true; - + // OOP style: attach generic methods to array object, hold no state in environment //function error(msg) { top.console.error(msg); top.console.trace(); } @@ -51,7 +51,7 @@ function Changeset(arg) { return this.length == 6 && this[1] == this[2] && this[3] == 0 && this[4] == this[1] && this[5] == ""; } - + array.eachStrip = function(func, thisObj) { // inside "func", the method receiver will be "this" by default, // or you can pass an object. @@ -66,7 +66,7 @@ function Changeset(arg) { array.numStrips = function() { return (this.length-3)/3; }; array.oldLen = function() { return this[1]; }; array.newLen = function() { return this[2]; }; - + array.checkRep = function() { assert(this[0] == Changeset.MAGIC, "bad magic"); assert(this[1] >= 0, "bad old text length"); @@ -97,7 +97,7 @@ function Changeset(arg) { }); assert(newLen == actualNewLen, "calculated new text length doesn't match"); } - + array.applyToText = function(text) { assert(text.length == this.oldLen(), "mismatched apply: "+text.length+" / "+this.oldLen()); var buf = []; @@ -139,7 +139,7 @@ function Changeset(arg) { C.authors = [_getNumInserted(C), author || '']; return C; } - + array.builder = function() { // normal pattern is Changeset(oldLength).builder().appendOldText(...). ... // builder methods mutate this! @@ -166,7 +166,7 @@ function Changeset(arg) { C.authors.push(str.length, a); } } - + return self; }, appendOldText: function(startIndex, numTaken) { @@ -193,7 +193,7 @@ function Changeset(arg) { array.authorSlicer = function(outputBuilder) { return _makeAuthorSlicer(this, outputBuilder); } - + function _makeAuthorSlicer(changesetOrAuthorsIn, builderOut) { // "builderOut" only needs to support appendNewText var authors; // considered immutable @@ -203,13 +203,13 @@ function Changeset(arg) { else { authors = changesetOrAuthorsIn; } - + // OOP style: state in environment var authorPtr = 0; var charIndex = 0; var charWithinAuthor = 0; // 0 <= charWithinAuthor <= authors[authorPtr]; max value iff atEnd var atEnd = false; - function curAuthor() { return authors[authorPtr+1]; } + function curAuthor() { return authors[authorPtr+1]; } function curAuthorWidth() { return authors[authorPtr]; } function assertNotAtEnd() { assert(! atEnd, "_authorSlicer: can't move past end"); } function forwardInAuthor(numChars) { @@ -225,7 +225,7 @@ function Changeset(arg) { atEnd = true; } } - + var self; return self = { skipChars: function(n) { @@ -278,11 +278,11 @@ function Changeset(arg) { } }; } - + function _makeSlicer(C, output) { // C: Changeset, output: builder from _makeBuilder // C is considered immutable, won't change or be changed - + // OOP style: state in environment var charIndex = 0; // 0 <= charIndex <= C.newLen(); maximum value iff atEnd var stripIndex = 0; // 0 <= stripIndex <= C.numStrips(); maximum value iff atEnd @@ -293,7 +293,7 @@ function Changeset(arg) { if (C.authors) { authorSlicer = _makeAuthorSlicer(C.authors, output); } - + var ptr = 3; function curStartIndex() { return C[ptr]; } function curNumTaken() { return C[ptr+1]; } @@ -327,7 +327,7 @@ function Changeset(arg) { if (e < s) return 0; return e-s; } - + var self; return self = { skipChars: function (n) { @@ -344,7 +344,7 @@ function Changeset(arg) { if (authorSlicer) authorSlicer.skipChars(curNumNewCharsInRange(charWithinStrip, charWithinStrip + leftInStrip)); - + leftToSkip -= leftInStrip; nextStrip(); } @@ -352,7 +352,7 @@ function Changeset(arg) { if (authorSlicer) authorSlicer.skipChars(curNumNewCharsInRange(charWithinStrip, charWithinStrip + leftToSkip)); - + forwardInStrip(leftToSkip); leftToSkip = 0; } @@ -379,9 +379,9 @@ function Changeset(arg) { charWithinStrip < curStripWidth()) { // at least one char to take from current strip's newText var leftInNewText = (curStripWidth() - charWithinStrip); - assert(leftInNewText > 0, "_slicer: should have leftInNewText > 0"); + assert(leftInNewText > 0, "_slicer: should have leftInNewText > 0"); var toTake = min(leftInNewText, leftToTake); - assert(toTake > 0, "_slicer: should have toTake > 0"); + assert(toTake > 0, "_slicer: should have toTake > 0"); var newText = curNewText().substr(charWithinStrip - curNumTaken(), toTake); if (authorSlicer) { authorSlicer.takeChars(newText.length, newText); @@ -407,7 +407,7 @@ function Changeset(arg) { array.slicer = function(outputBuilder) { return _makeSlicer(this, outputBuilder); } - + array.compose = function(next) { assert(next.oldLen() == this.newLen(), "mismatched composition"); @@ -418,7 +418,7 @@ function Changeset(arg) { if (next.authors) { authorSlicer = _makeAuthorSlicer(next.authors, builder); } - + next.eachStrip(function(s, t, n) { slicer.skipTo(s); slicer.takeChars(t); @@ -436,7 +436,7 @@ function Changeset(arg) { array.traverser = function() { return _makeTraverser(this); } - + function _makeTraverser(C) { var s = C[3], t = C[4], n = C[5]; var nextIndex = 6; @@ -446,7 +446,7 @@ function Changeset(arg) { if (C.authors) { authorSlicer = _makeAuthorSlicer(C.authors, null); } - + function advanceIfPossible() { if (t == 0 && n == "" && nextIndex < C.length) { s = C[nextIndex]; @@ -561,7 +561,7 @@ function Changeset(arg) { } return builder.toChangeset(); } - + array.encodeToString = function(asBinary) { var stringDataArray = []; var numsArray = []; @@ -585,7 +585,7 @@ function Changeset(arg) { return "\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4); }); } - + array.applyToAttributedText = Changeset.applyToAttributedText; function splicesFromChanges(c) { @@ -616,11 +616,11 @@ function Changeset(arg) { } return splices; } - + array.toSplices = function() { return splicesFromChanges(this); } - + array.characterRangeFollowThis = function(selStartChar, selEndChar, insertionsAfter) { var changeset = this; // represent the selection as a changeset that replaces the selection with some finite string. @@ -650,7 +650,7 @@ function Changeset(arg) { } return [selStartChar, selEndChar]; } - + return array; } @@ -660,7 +660,7 @@ Changeset.makeSplice = function(oldLength, spliceStart, numRemoved, stringInsert spliceStart = (spliceStart || 0); numRemoved = (numRemoved || 0); stringInserted = String(stringInserted || ""); - + var builder = Changeset(oldLength).builder(); builder.appendOldText(0, spliceStart); builder.appendNewText(stringInserted); @@ -688,7 +688,7 @@ Changeset.decodeFromString = function(str) { return String.fromCharCode(Number("0x"+seq.substring(2))); }); } - + var numData, stringData; var binary = false; var typ = str.charAt(0); @@ -765,7 +765,7 @@ Changeset.numberArrayFromString = function(str, startIndex) { } else { // legacy format - n = (((n & 0x1fff) << 16) | str.charCodeAt(strIndex++)); + n = (((n & 0x1fff) << 16) | str.charCodeAt(strIndex++)); } } return n; @@ -805,7 +805,7 @@ Changeset.numberArrayFromString = function(str, startIndex) { } return 0; } - + // emptyObj may be a StorableObject Changeset.initAttributedText = function(emptyObj, initialString, initialAuthor) { var obj = emptyObj; @@ -894,7 +894,7 @@ Changeset.numberArrayFromString = function(str, startIndex) { // call func(author, authorNum) for(var a in atObj.authorMap) { if (func(atObj.authorMap[a], Number(a))) break; - } + } }; Changeset.getAttributedTextAuthorByNum = function(atObj, n) { return atObj.authorMap[n]; diff --git a/trunk/infrastructure/ace/www/easysync2.js b/trunk/infrastructure/ace/www/easysync2.js index 7cd1917..efc5b99 100644 --- a/trunk/infrastructure/ace/www/easysync2.js +++ b/trunk/infrastructure/ace/www/easysync2.js @@ -1,12 +1,15 @@ +// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2 +// %APPJET%: jimport("com.etherpad.Easysync2Support"); + /** * 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 +17,6 @@ * limitations under the License. */ -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2 -// %APPJET%: jimport("com.etherpad.Easysync2Support"); - //var _opt = (this.Easysync2Support || null); var _opt = null; // disable optimization for now diff --git a/trunk/infrastructure/ace/www/easysync2_tests.js b/trunk/infrastructure/ace/www/easysync2_tests.js index 071a207..2fcf202 100644 --- a/trunk/infrastructure/ace/www/easysync2_tests.js +++ b/trunk/infrastructure/ace/www/easysync2_tests.js @@ -1,12 +1,15 @@ +// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2_tests +// %APPJET%: import("etherpad.collab.ace.easysync2.*") + /** * 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 +17,6 @@ * limitations under the License. */ -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2_tests -// %APPJET%: import("etherpad.collab.ace.easysync2.*") - function runTests() { function print(str) { diff --git a/trunk/infrastructure/ace/www/linestylefilter.js b/trunk/infrastructure/ace/www/linestylefilter.js index 69a0aac..0ac578b 100644 --- a/trunk/infrastructure/ace/www/linestylefilter.js +++ b/trunk/infrastructure/ace/www/linestylefilter.js @@ -1,12 +1,15 @@ +// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter +// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset"); + /** * 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 +17,6 @@ * limitations under the License. */ -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter -// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset"); - // requires: easysync2.Changeset var linestylefilter = {}; |