From 98e2821b38a775737e42a2479a6bc65107210859 Mon Sep 17 00:00:00 2001 From: Elliot Kroo Date: Thu, 11 Mar 2010 15:21:30 -0800 Subject: reorganizing the first level of folders (trunk/branch folders are not the git way :) --- trunk/infrastructure/ace/.gitignore | 1 - trunk/infrastructure/ace/README | 69 - trunk/infrastructure/ace/bin/make | 337 -- trunk/infrastructure/ace/bin/serve | 45 - trunk/infrastructure/ace/blog.txt | 390 -- trunk/infrastructure/ace/build/.gitignore | 2 - trunk/infrastructure/ace/build/index.html | 47 - trunk/infrastructure/ace/build/jquery-1.2.1.js | 2992 ------------ trunk/infrastructure/ace/build/testcode.js | 36 - trunk/infrastructure/ace/easysync-notes.txt | 129 - trunk/infrastructure/ace/lib/rhino-js-1.7r1.jar | 1 - .../ace/lib/yuicompressor-2.4-appjet.jar | 1 - trunk/infrastructure/ace/notes.txt | 195 - trunk/infrastructure/ace/www/ace2_common.js | 115 - trunk/infrastructure/ace/www/ace2_common_dev.js | 296 -- trunk/infrastructure/ace/www/ace2_inner.js | 4778 -------------------- trunk/infrastructure/ace/www/ace2_outer.js | 214 - trunk/infrastructure/ace/www/ace2_wrapper.js | 226 - trunk/infrastructure/ace/www/bbtree.js | 372 -- trunk/infrastructure/ace/www/changesettracker.js | 170 - trunk/infrastructure/ace/www/colorutils.js | 91 - trunk/infrastructure/ace/www/contentcollector.js | 509 --- trunk/infrastructure/ace/www/cssmanager.js | 88 - trunk/infrastructure/ace/www/dev.html | 39 - trunk/infrastructure/ace/www/domline.js | 210 - trunk/infrastructure/ace/www/easy_sync.js | 923 ---- trunk/infrastructure/ace/www/easysync2.js | 1968 -------- trunk/infrastructure/ace/www/easysync2_tests.js | 877 ---- trunk/infrastructure/ace/www/editor.css | 109 - trunk/infrastructure/ace/www/firebug/errorIcon.png | Bin 457 -> 0 bytes trunk/infrastructure/ace/www/firebug/firebug.css | 209 - trunk/infrastructure/ace/www/firebug/firebug.html | 23 - trunk/infrastructure/ace/www/firebug/firebug.js | 688 --- trunk/infrastructure/ace/www/firebug/firebugx.js | 26 - trunk/infrastructure/ace/www/firebug/infoIcon.png | Bin 524 -> 0 bytes .../infrastructure/ace/www/firebug/warningIcon.png | Bin 516 -> 0 bytes trunk/infrastructure/ace/www/index.html | 50 - trunk/infrastructure/ace/www/inner.css | 48 - trunk/infrastructure/ace/www/jquery-1.2.1.js | 2992 ------------ trunk/infrastructure/ace/www/lang_html.js | 1685 ------- trunk/infrastructure/ace/www/lang_js.js | 102 - trunk/infrastructure/ace/www/lexer_support.js | 1068 ----- trunk/infrastructure/ace/www/linestylefilter.js | 247 - trunk/infrastructure/ace/www/magicdom.js | 293 -- trunk/infrastructure/ace/www/multilang_lexer.js | 349 -- trunk/infrastructure/ace/www/processing.js | 1714 ------- trunk/infrastructure/ace/www/profiler.js | 117 - trunk/infrastructure/ace/www/skiplist.js | 347 -- trunk/infrastructure/ace/www/spanlist.js | 279 -- trunk/infrastructure/ace/www/syntax-new.css | 35 - trunk/infrastructure/ace/www/syntax.css | 32 - trunk/infrastructure/ace/www/test.html | 11 - trunk/infrastructure/ace/www/testcode.js | 36 - trunk/infrastructure/ace/www/toSource.js | 274 -- trunk/infrastructure/ace/www/undomodule.js | 258 -- trunk/infrastructure/ace/www/virtual_lines.js | 287 -- 56 files changed, 26400 deletions(-) delete mode 100644 trunk/infrastructure/ace/.gitignore delete mode 100644 trunk/infrastructure/ace/README delete mode 100755 trunk/infrastructure/ace/bin/make delete mode 100755 trunk/infrastructure/ace/bin/serve delete mode 100644 trunk/infrastructure/ace/blog.txt delete mode 100644 trunk/infrastructure/ace/build/.gitignore delete mode 100644 trunk/infrastructure/ace/build/index.html delete mode 100644 trunk/infrastructure/ace/build/jquery-1.2.1.js delete mode 100644 trunk/infrastructure/ace/build/testcode.js delete mode 100644 trunk/infrastructure/ace/easysync-notes.txt delete mode 120000 trunk/infrastructure/ace/lib/rhino-js-1.7r1.jar delete mode 120000 trunk/infrastructure/ace/lib/yuicompressor-2.4-appjet.jar delete mode 100644 trunk/infrastructure/ace/notes.txt delete mode 100644 trunk/infrastructure/ace/www/ace2_common.js delete mode 100644 trunk/infrastructure/ace/www/ace2_common_dev.js delete mode 100644 trunk/infrastructure/ace/www/ace2_inner.js delete mode 100644 trunk/infrastructure/ace/www/ace2_outer.js delete mode 100644 trunk/infrastructure/ace/www/ace2_wrapper.js delete mode 100644 trunk/infrastructure/ace/www/bbtree.js delete mode 100644 trunk/infrastructure/ace/www/changesettracker.js delete mode 100644 trunk/infrastructure/ace/www/colorutils.js delete mode 100644 trunk/infrastructure/ace/www/contentcollector.js delete mode 100644 trunk/infrastructure/ace/www/cssmanager.js delete mode 100644 trunk/infrastructure/ace/www/dev.html delete mode 100644 trunk/infrastructure/ace/www/domline.js delete mode 100644 trunk/infrastructure/ace/www/easy_sync.js delete mode 100644 trunk/infrastructure/ace/www/easysync2.js delete mode 100644 trunk/infrastructure/ace/www/easysync2_tests.js delete mode 100644 trunk/infrastructure/ace/www/editor.css delete mode 100644 trunk/infrastructure/ace/www/firebug/errorIcon.png delete mode 100644 trunk/infrastructure/ace/www/firebug/firebug.css delete mode 100644 trunk/infrastructure/ace/www/firebug/firebug.html delete mode 100644 trunk/infrastructure/ace/www/firebug/firebug.js delete mode 100644 trunk/infrastructure/ace/www/firebug/firebugx.js delete mode 100644 trunk/infrastructure/ace/www/firebug/infoIcon.png delete mode 100644 trunk/infrastructure/ace/www/firebug/warningIcon.png delete mode 100644 trunk/infrastructure/ace/www/index.html delete mode 100644 trunk/infrastructure/ace/www/inner.css delete mode 100644 trunk/infrastructure/ace/www/jquery-1.2.1.js delete mode 100644 trunk/infrastructure/ace/www/lang_html.js delete mode 100644 trunk/infrastructure/ace/www/lang_js.js delete mode 100644 trunk/infrastructure/ace/www/lexer_support.js delete mode 100644 trunk/infrastructure/ace/www/linestylefilter.js delete mode 100644 trunk/infrastructure/ace/www/magicdom.js delete mode 100644 trunk/infrastructure/ace/www/multilang_lexer.js delete mode 100644 trunk/infrastructure/ace/www/processing.js delete mode 100644 trunk/infrastructure/ace/www/profiler.js delete mode 100644 trunk/infrastructure/ace/www/skiplist.js delete mode 100644 trunk/infrastructure/ace/www/spanlist.js delete mode 100644 trunk/infrastructure/ace/www/syntax-new.css delete mode 100644 trunk/infrastructure/ace/www/syntax.css delete mode 100644 trunk/infrastructure/ace/www/test.html delete mode 100644 trunk/infrastructure/ace/www/testcode.js delete mode 100644 trunk/infrastructure/ace/www/toSource.js delete mode 100644 trunk/infrastructure/ace/www/undomodule.js delete mode 100644 trunk/infrastructure/ace/www/virtual_lines.js (limited to 'trunk/infrastructure/ace') diff --git a/trunk/infrastructure/ace/.gitignore b/trunk/infrastructure/ace/.gitignore deleted file mode 100644 index 4083037..0000000 --- a/trunk/infrastructure/ace/.gitignore +++ /dev/null @@ -1 +0,0 @@ -local diff --git a/trunk/infrastructure/ace/README b/trunk/infrastructure/ace/README deleted file mode 100644 index 275684f..0000000 --- a/trunk/infrastructure/ace/README +++ /dev/null @@ -1,69 +0,0 @@ -===== -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/make b/trunk/infrastructure/ace/bin/make deleted file mode 100755 index dad11ff..0000000 --- a/trunk/infrastructure/ace/bin/make +++ /dev/null @@ -1,337 +0,0 @@ -#!/bin/sh -exec scala -classpath lib/yuicompressor-2.4-appjet.jar:lib/rhino-js-1.7r1.jar $0 $@ -!# - -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 - // - each char must be able to go in single quotes - val specialChars = "-~@%$#*^_`()|abcdefghijklmnopqrstuvwxyz=!+,.;:?{}"; - val specialCharsSet:Set[Char] = Set(specialChars:_*); - def containsSpecialChar(str: String) = str.exists(specialCharsSet.contains(_)); - - val toks:Array[String] = ( - "@|[a-zA-Z0-9]+|[^@a-zA-Z0-9]{1,3}").r.findAllIn(input).collect.toArray; - - val stringCounts = { - val m = new scala.collection.mutable.HashMap[String,Int]; - def incrementCount(s: String) = { m(s) = m.getOrElse(s, 0) + 1; } - for(s <- toks) incrementCount(s); - m; - } - - val estimatedSavings = scala.util.Sorting.stableSort( - for((s,n) <- stringCounts.toArray; savings = s.length*n - if (savings > 8 || containsSpecialChar(s))) - yield (s,n,savings), - (x:(String,Int,Int))=> -x._3); - - def strLast(str: String, n: Int) = str.substring(str.length - n, str.length); - // order of encodeNames is very important! - val encodeNames = for(n <- 0 until (36*36); c <- specialChars) yield c.toString+strLast("0"+Integer.toString(n, 36).toUpperCase, 2); - - val thingsToReplace:Seq[String] = estimatedSavings.map(_._1); - assert(encodeNames.length >= thingsToReplace.length); - - val replacements = Map(thingsToReplace.elements.zipWithIndex.map({ - case (str, i) => (str, encodeNames(i)); - }).collect:_*); - def encode(tk: String) = if (replacements.contains(tk)) replacements(tk) else tk; - - val afterReplace = toks.map(encode(_)).mkString.replaceAll( - "(["+specialChars+"])(?=..[^0-9A-Z])(00|0)", "$1"); - - def makeSingleQuotedContents(str: String): String = { - str.replace("\\", "\\\\").replace("'", "\\'").replace("<", "\\x3c").replace("\n", "\\n"). - replace("\r", "\\n").replace("\t", "\\t"); - } - - val expansionMap = new scala.collection.mutable.HashMap[Char,scala.collection.mutable.ArrayBuffer[String]]; - for(i <- 0 until thingsToReplace.length; sc = encodeNames(i).charAt(0); - e = thingsToReplace(i)) { - expansionMap.getOrElseUpdate(sc, new scala.collection.mutable.ArrayBuffer[String]) += - (if (e == "@") "" else e); - } - val expansionMapLiteral = "{"+(for((sc,strs) <- expansionMap) yield { - "'"+sc+"':'"+makeSingleQuotedContents(strs.mkString("@"))+"'"; - }).mkString(",")+"}"; - - val expr = ("(function(m){m="+expansionMapLiteral+ - ";for(var k in m){if(m.hasOwnProperty(k))m[k]=m[k].split('@')};return '"+ - makeSingleQuotedContents(afterReplace)+ - "'.replace(/(["+specialChars+ - "])([0-9A-Z]{0,2})/g,function(a,b,c){return m[b][parseInt(c||'0',36)]||'@'})}())"); - /*val expr = ("(function(m){m="+expansionMapLiteral+ - ";for(var k in m){if(m.hasOwnProperty(k))m[k]=m[k].split('@')};"+ - "var result=[];var i=0;var s='"+makeSingleQuotedContents(afterReplace)+ - "';var len=s.length;while (i= 'A' && a <= 'Z' || a >= '0' && a <= '9')) {c=L[0];i++} else if (!(b >= 'A' && b <= 'Z' || b >= '0' && b <= '9')) {c = L[parseInt(a,36)]; i+=2} else {c = L[parseInt(a+b,36)]; i+=3}; result.push(c||'@'); } else {result.push(x); i++} }; return result.join(''); }())");*/ - - def evaluateString(js: String): String = { - import org.mozilla.javascript._; - ContextFactory.getGlobal.call(new ContextAction { - def run(cx: Context) = { - val scope = cx.initStandardObjects; - cx.evaluateString(scope, js, "", 1, null) } }).asInstanceOf[String]; - } - - def putFile(str: String, path: String): Unit = { - import java.io._; - val writer = new FileWriter(path); - writer.write(str); - writer.close; - } - - val exprOut = evaluateString(expr); - if (exprOut != input) { - putFile(input, "/tmp/superpack.input"); - putFile(expr, "/tmp/superpack.expr"); - putFile(exprOut, "/tmp/superpack.output"); - error("Superpacked string does not evaluate to original string; check /tmp/superpack.*"); - } - - val singleLiteral = "'"+makeSingleQuotedContents(input)+"'"; - if (singleLiteral.length < expr.length) { - singleLiteral; - } - else { - expr; - } -} - -def doMake { - - lazy val isEtherPad = (args.length >= 2 && args(1) == "etherpad"); - lazy val isNoHelma = (args.length >= 2 && args(1) == "nohelma"); - - def getFile(path:String): String = { - val builder = new StringBuilder(1000); - val reader = new BufferedReader(new FileReader(path)); - val buf = new Array[Char](1024); - var numRead = 0; - while({ numRead = reader.read(buf); numRead } != -1) { - builder.append(buf, 0, numRead); - } - reader.close; - return builder.toString; - } - - def putFile(str: String, path: String): Unit = { - val writer = new FileWriter(path); - writer.write(str); - writer.close; - } - - def writeToString(func:(Writer=>Unit)): String = { - val writer = new StringWriter; - func(writer); - return writer.toString; - } - - def compressJS(code: String, wrap: Boolean): String = { - import yuicompressor.org.mozilla.javascript.{ErrorReporter, EvaluatorException}; - object MyErrorReporter extends ErrorReporter { - def warning(message:String, sourceName:String, line:Int, lineSource:String, lineOffset:Int) { - if (message startsWith "Try to use a single 'var' statement per scope.") return; - if (line < 0) System.err.println("\n[WARNING] " + message); - else System.err.println("\n[WARNING] " + line + ':' + lineOffset + ':' + message); - } - def error(message:String, sourceName:String, line:Int, lineSource:String, lineOffset:Int) { - if (line < 0) System.err.println("\n[ERROR] " + message); - else System.err.println("\n[ERROR] " + line + ':' + lineOffset + ':' + message); - } - def runtimeError(message:String, sourceName:String, line:Int, lineSource:String, lineOffset:Int): EvaluatorException = { - error(message, sourceName, line, lineSource, lineOffset); - return new EvaluatorException(message); - } - } - - val munge = true; - val verbose = false; - val optimize = true; - val compressor = new com.yahoo.platform.yui.compressor.JavaScriptCompressor(new StringReader(code), MyErrorReporter); - return writeToString(compressor.compress(_, if (wrap) 100 else -1, munge, verbose, true, !optimize)); - } - - def compressCSS(code: String, wrap: Boolean): String = { - val compressor = new com.yahoo.platform.yui.compressor.CssCompressor(new StringReader(code)); - return writeToString(compressor.compress(_, if (wrap) 100 else -1)); - } - - import java.util.regex.{Pattern, Matcher, MatchResult}; - - def stringReplace(orig: String, regex: String, groupReferences:Boolean, func:(MatchResult=>String)): String = { - val buf = new StringBuffer; - val m = Pattern.compile(regex).matcher(orig); - while (m.find) { - var str = func(m); - if (! groupReferences) { - str = str.replace("\\", "\\\\").replace("$", "\\$"); - } - m.appendReplacement(buf, str); - } - m.appendTail(buf); - return buf.toString; - } - - def stringToExpression(str: String): String = { - var contents = str.replace("\\", "\\\\").replace("'", "\\'").replace("<", "\\x3c").replace("\n", "\\n"). - replace("\r", "\\n").replace("\t", "\\t"); - contents = contents.replace("\\/", "\\\\x2f"); // for Norton Internet Security - val result = "'"+contents+"'"; - result; - } - - val srcDir = "www"; - val destDir = "build"; - var code = getFile(srcDir+"/ace2_outer.js"); - - val useCompression = true; //if (isEtherPad) false else true; - - code = stringReplace(code, "\\$\\$INCLUDE_([A-Z_]+)\\([\"']([^\"']+)[\"']\\)", false, (m:MatchResult) => { - val includeType = m.group(1); - val paths = m.group(2); - val pathsArray = paths.replaceAll("""/\*.*?\*/""", "").split(" +").filter(_.length > 0); - def getSubcode = pathsArray.map(p => getFile(srcDir+"/"+p)).mkString("\n"); - val doPack = (stringToExpression _); - includeType match { - case "JS" => { - var subcode = getSubcode; - subcode = subcode.replaceAll("var DEBUG=true;//\\$\\$[^\n\r]*", "var DEBUG=false;"); - if (useCompression) subcode = compressJS(subcode, true); - "('\\x3cscript type=\"text/javascript\">//\\n')"; - } - case "CSS" => { - var subcode = getSubcode; - if (useCompression) subcode = compressCSS(subcode, false); - "('')"; - } - case "JS_Q" => { - var subcode = getSubcode - subcode = subcode.replaceAll("var DEBUG=true;//\\$\\$[^\n\r]*", "var DEBUG=false;"); - if (useCompression) subcode = compressJS(subcode, true); - "('(\\'\\\\x3cscript type=\"text/javascript\">//\\\\n\\\\x3c/script>\\')')"; - } - case "CSS_Q" => { - var subcode = getSubcode; - if (useCompression) subcode = compressCSS(subcode, false); - "('(\\' - - -
- - diff --git a/trunk/infrastructure/ace/build/jquery-1.2.1.js b/trunk/infrastructure/ace/build/jquery-1.2.1.js deleted file mode 100644 index b4eb132..0000000 --- a/trunk/infrastructure/ace/build/jquery-1.2.1.js +++ /dev/null @@ -1,2992 +0,0 @@ -(function(){ -/* - * jQuery 1.2.1 - New Wave Javascript - * - * Copyright (c) 2007 John Resig (jquery.com) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * $Date: 2007-09-16 23:42:06 -0400 (Sun, 16 Sep 2007) $ - * $Rev: 3353 $ - */ - -// Map over jQuery in case of overwrite -if ( typeof jQuery != "undefined" ) - var _jQuery = jQuery; - -var jQuery = window.jQuery = function(selector, context) { - // If the context is a namespace object, return a new object - return this instanceof jQuery ? - this.init(selector, context) : - new jQuery(selector, context); -}; - -// Map over the $ in case of overwrite -if ( typeof $ != "undefined" ) - var _$ = $; - -// Map the jQuery namespace to the '$' one -window.$ = jQuery; - -var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; - -jQuery.fn = jQuery.prototype = { - init: function(selector, context) { - // Make sure that a selection was provided - selector = selector || document; - - // Handle HTML strings - if ( typeof selector == "string" ) { - var m = quickExpr.exec(selector); - if ( m && (m[1] || !context) ) { - // HANDLE: $(html) -> $(array) - if ( m[1] ) - selector = jQuery.clean( [ m[1] ], context ); - - // HANDLE: $("#id") - else { - var tmp = document.getElementById( m[3] ); - if ( tmp ) - // Handle the case where IE and Opera return items - // by name instead of ID - if ( tmp.id != m[3] ) - return jQuery().find( selector ); - else { - this[0] = tmp; - this.length = 1; - return this; - } - else - selector = []; - } - - // HANDLE: $(expr) - } else - return new jQuery( context ).find( selector ); - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction(selector) ) - return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( selector ); - - return this.setArray( - // HANDLE: $(array) - selector.constructor == Array && selector || - - // HANDLE: $(arraylike) - // Watch for when an array-like object is passed as the selector - (selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) || - - // HANDLE: $(*) - [ selector ] ); - }, - - jquery: "1.2.1", - - size: function() { - return this.length; - }, - - length: 0, - - get: function( num ) { - return num == undefined ? - - // Return a 'clean' array - jQuery.makeArray( this ) : - - // Return just the object - this[num]; - }, - - pushStack: function( a ) { - var ret = jQuery(a); - ret.prevObject = this; - return ret; - }, - - setArray: function( a ) { - this.length = 0; - Array.prototype.push.apply( this, a ); - return this; - }, - - each: function( fn, args ) { - return jQuery.each( this, fn, args ); - }, - - index: function( obj ) { - var pos = -1; - this.each(function(i){ - if ( this == obj ) pos = i; - }); - return pos; - }, - - attr: function( key, value, type ) { - var obj = key; - - // Look for the case where we're accessing a style value - if ( key.constructor == String ) - if ( value == undefined ) - return this.length && jQuery[ type || "attr" ]( this[0], key ) || undefined; - else { - obj = {}; - obj[ key ] = value; - } - - // Check to see if we're setting style values - return this.each(function(index){ - // Set all the styles - for ( var prop in obj ) - jQuery.attr( - type ? this.style : this, - prop, jQuery.prop(this, obj[prop], type, index, prop) - ); - }); - }, - - css: function( key, value ) { - return this.attr( key, value, "curCSS" ); - }, - - text: function(e) { - if ( typeof e != "object" && e != null ) - return this.empty().append( document.createTextNode( e ) ); - - var t = ""; - jQuery.each( e || this, function(){ - jQuery.each( this.childNodes, function(){ - if ( this.nodeType != 8 ) - t += this.nodeType != 1 ? - this.nodeValue : jQuery.fn.text([ this ]); - }); - }); - return t; - }, - - wrapAll: function(html) { - if ( this[0] ) - // The elements to wrap the target around - jQuery(html, this[0].ownerDocument) - .clone() - .insertBefore(this[0]) - .map(function(){ - var elem = this; - while ( elem.firstChild ) - elem = elem.firstChild; - return elem; - }) - .append(this); - - return this; - }, - - wrapInner: function(html) { - return this.each(function(){ - jQuery(this).contents().wrapAll(html); - }); - }, - - wrap: function(html) { - return this.each(function(){ - jQuery(this).wrapAll(html); - }); - }, - - append: function() { - return this.domManip(arguments, true, 1, function(a){ - this.appendChild( a ); - }); - }, - - prepend: function() { - return this.domManip(arguments, true, -1, function(a){ - this.insertBefore( a, this.firstChild ); - }); - }, - - before: function() { - return this.domManip(arguments, false, 1, function(a){ - this.parentNode.insertBefore( a, this ); - }); - }, - - after: function() { - return this.domManip(arguments, false, -1, function(a){ - this.parentNode.insertBefore( a, this.nextSibling ); - }); - }, - - end: function() { - return this.prevObject || jQuery([]); - }, - - find: function(t) { - var data = jQuery.map(this, function(a){ return jQuery.find(t,a); }); - return this.pushStack( /[^+>] [^+>]/.test( t ) || t.indexOf("..") > -1 ? - jQuery.unique( data ) : data ); - }, - - clone: function(events) { - // Do the clone - var ret = this.map(function(){ - return this.outerHTML ? jQuery(this.outerHTML)[0] : this.cloneNode(true); - }); - - // Need to set the expando to null on the cloned set if it exists - // removeData doesn't work here, IE removes it from the original as well - // this is primarily for IE but the data expando shouldn't be copied over in any browser - var clone = ret.find("*").andSelf().each(function(){ - if ( this[ expando ] != undefined ) - this[ expando ] = null; - }); - - // Copy the events from the original to the clone - if (events === true) - this.find("*").andSelf().each(function(i) { - var events = jQuery.data(this, "events"); - for ( var type in events ) - for ( var handler in events[type] ) - jQuery.event.add(clone[i], type, events[type][handler], events[type][handler].data); - }); - - // Return the cloned set - return ret; - }, - - filter: function(t) { - return this.pushStack( - jQuery.isFunction( t ) && - jQuery.grep(this, function(el, index){ - return t.apply(el, [index]); - }) || - - jQuery.multiFilter(t,this) ); - }, - - not: function(t) { - return this.pushStack( - t.constructor == String && - jQuery.multiFilter(t, this, true) || - - jQuery.grep(this, function(a) { - return ( t.constructor == Array || t.jquery ) - ? jQuery.inArray( a, t ) < 0 - : a != t; - }) - ); - }, - - add: function(t) { - return this.pushStack( jQuery.merge( - this.get(), - t.constructor == String ? - jQuery(t).get() : - t.length != undefined && (!t.nodeName || jQuery.nodeName(t, "form")) ? - t : [t] ) - ); - }, - - is: function(expr) { - return expr ? jQuery.multiFilter(expr,this).length > 0 : false; - }, - - hasClass: function(expr) { - return this.is("." + expr); - }, - - val: function( val ) { - if ( val == undefined ) { - if ( this.length ) { - var elem = this[0]; - - // We need to handle select boxes special - if ( jQuery.nodeName(elem, "select") ) { - var index = elem.selectedIndex, - a = [], - options = elem.options, - one = elem.type == "select-one"; - - // Nothing was selected - if ( index < 0 ) - return null; - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[i]; - if ( option.selected ) { - // Get the specifc value for the option - var val = jQuery.browser.msie && !option.attributes["value"].specified ? option.text : option.value; - - // We don't need an array for one selects - if ( one ) - return val; - - // Multi-Selects return an array - a.push(val); - } - } - - return a; - - // Everything else, we just grab the value - } else - return this[0].value.replace(/\r/g, ""); - } - } else - return this.each(function(){ - if ( val.constructor == Array && /radio|checkbox/.test(this.type) ) - this.checked = (jQuery.inArray(this.value, val) >= 0 || - jQuery.inArray(this.name, val) >= 0); - else if ( jQuery.nodeName(this, "select") ) { - var tmp = val.constructor == Array ? val : [val]; - - jQuery("option", this).each(function(){ - this.selected = (jQuery.inArray(this.value, tmp) >= 0 || - jQuery.inArray(this.text, tmp) >= 0); - }); - - if ( !tmp.length ) - this.selectedIndex = -1; - } else - this.value = val; - }); - }, - - html: function( val ) { - return val == undefined ? - ( this.length ? this[0].innerHTML : null ) : - this.empty().append( val ); - }, - - replaceWith: function( val ) { - return this.after( val ).remove(); - }, - - eq: function(i){ - return this.slice(i, i+1); - }, - - slice: function() { - return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); - }, - - map: function(fn) { - return this.pushStack(jQuery.map( this, function(elem,i){ - return fn.call( elem, i, elem ); - })); - }, - - andSelf: function() { - return this.add( this.prevObject ); - }, - - domManip: function(args, table, dir, fn) { - var clone = this.length > 1, a; - - return this.each(function(){ - if ( !a ) { - a = jQuery.clean(args, this.ownerDocument); - if ( dir < 0 ) - a.reverse(); - } - - var obj = this; - - if ( table && jQuery.nodeName(this, "table") && jQuery.nodeName(a[0], "tr") ) - obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody")); - - jQuery.each( a, function(){ - var elem = clone ? this.cloneNode(true) : this; - if ( !evalScript(0, elem) ) - fn.call( obj, elem ); - }); - }); - } -}; - -function evalScript(i, elem){ - var script = jQuery.nodeName(elem, "script"); - - if ( script ) { - if ( elem.src ) - jQuery.ajax({ url: elem.src, async: false, dataType: "script" }); - else - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - - if ( elem.parentNode ) - elem.parentNode.removeChild(elem); - - } else if ( elem.nodeType == 1 ) - jQuery("script", elem).each(evalScript); - - return script; -} - -jQuery.extend = jQuery.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, a = 1, al = arguments.length, deep = false; - - // Handle a deep copy situation - if ( target.constructor == Boolean ) { - deep = target; - target = arguments[1] || {}; - } - - // extend jQuery itself if only one argument is passed - if ( al == 1 ) { - target = this; - a = 0; - } - - var prop; - - for ( ; a < al; a++ ) - // Only deal with non-null/undefined values - if ( (prop = arguments[a]) != null ) - // Extend the base object - for ( var i in prop ) { - // Prevent never-ending loop - if ( target == prop[i] ) - continue; - - // Recurse if we're merging object values - if ( deep && typeof prop[i] == 'object' && target[i] ) - jQuery.extend( target[i], prop[i] ); - - // Don't bring in undefined values - else if ( prop[i] != undefined ) - target[i] = prop[i]; - } - - // Return the modified object - return target; -}; - -var expando = "jQuery" + (new Date()).getTime(), uuid = 0, win = {}; - -jQuery.extend({ - noConflict: function(deep) { - window.$ = _$; - if ( deep ) - window.jQuery = _jQuery; - return jQuery; - }, - - // This may seem like some crazy code, but trust me when I say that this - // is the only cross-browser way to do this. --John - isFunction: function( fn ) { - return !!fn && typeof fn != "string" && !fn.nodeName && - fn.constructor != Array && /function/i.test( fn + "" ); - }, - - // check if an element is in a XML document - isXMLDoc: function(elem) { - return elem.documentElement && !elem.body || - elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; - }, - - // Evalulates a script in a global context - // Evaluates Async. in Safari 2 :-( - globalEval: function( data ) { - data = jQuery.trim( data ); - if ( data ) { - if ( window.execScript ) - window.execScript( data ); - else if ( jQuery.browser.safari ) - // safari doesn't provide a synchronous global eval - window.setTimeout( data, 0 ); - else - eval.call( window, data ); - } - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); - }, - - cache: {}, - - data: function( elem, name, data ) { - elem = elem == window ? win : elem; - - var id = elem[ expando ]; - - // Compute a unique ID for the element - if ( !id ) - id = elem[ expando ] = ++uuid; - - // Only generate the data cache if we're - // trying to access or manipulate it - if ( name && !jQuery.cache[ id ] ) - jQuery.cache[ id ] = {}; - - // Prevent overriding the named cache with undefined values - if ( data != undefined ) - jQuery.cache[ id ][ name ] = data; - - // Return the named cache data, or the ID for the element - return name ? jQuery.cache[ id ][ name ] : id; - }, - - removeData: function( elem, name ) { - elem = elem == window ? win : elem; - - var id = elem[ expando ]; - - // If we want to remove a specific section of the element's data - if ( name ) { - if ( jQuery.cache[ id ] ) { - // Remove the section of cache data - delete jQuery.cache[ id ][ name ]; - - // If we've removed all the data, remove the element's cache - name = ""; - for ( name in jQuery.cache[ id ] ) break; - if ( !name ) - jQuery.removeData( elem ); - } - - // Otherwise, we want to remove all of the element's data - } else { - // Clean up the element expando - try { - delete elem[ expando ]; - } catch(e){ - // IE has trouble directly removing the expando - // but it's ok with using removeAttribute - if ( elem.removeAttribute ) - elem.removeAttribute( expando ); - } - - // Completely remove the data cache - delete jQuery.cache[ id ]; - } - }, - - // args is for internal usage only - each: function( obj, fn, args ) { - if ( args ) { - if ( obj.length == undefined ) - for ( var i in obj ) - fn.apply( obj[i], args ); - else - for ( var i = 0, ol = obj.length; i < ol; i++ ) - if ( fn.apply( obj[i], args ) === false ) break; - - // A special, fast, case for the most common use of each - } else { - if ( obj.length == undefined ) - for ( var i in obj ) - fn.call( obj[i], i, obj[i] ); - else - for ( var i = 0, ol = obj.length, val = obj[0]; - i < ol && fn.call(val,i,val) !== false; val = obj[++i] ){} - } - - return obj; - }, - - prop: function(elem, value, type, index, prop){ - // Handle executable functions - if ( jQuery.isFunction( value ) ) - value = value.call( elem, [index] ); - - // exclude the following css properties to add px - var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i; - - // Handle passing in a number to a CSS property - return value && value.constructor == Number && type == "curCSS" && !exclude.test(prop) ? - value + "px" : - value; - }, - - className: { - // internal only, use addClass("class") - add: function( elem, c ){ - jQuery.each( (c || "").split(/\s+/), function(i, cur){ - if ( !jQuery.className.has( elem.className, cur ) ) - elem.className += ( elem.className ? " " : "" ) + cur; - }); - }, - - // internal only, use removeClass("class") - remove: function( elem, c ){ - elem.className = c != undefined ? - jQuery.grep( elem.className.split(/\s+/), function(cur){ - return !jQuery.className.has( c, cur ); - }).join(" ") : ""; - }, - - // internal only, use is(".class") - has: function( t, c ) { - return jQuery.inArray( c, (t.className || t).toString().split(/\s+/) ) > -1; - } - }, - - swap: function(e,o,f) { - for ( var i in o ) { - e.style["old"+i] = e.style[i]; - e.style[i] = o[i]; - } - f.apply( e, [] ); - for ( var i in o ) - e.style[i] = e.style["old"+i]; - }, - - css: function(e,p) { - if ( p == "height" || p == "width" ) { - var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"]; - - jQuery.each( d, function(){ - old["padding" + this] = 0; - old["border" + this + "Width"] = 0; - }); - - jQuery.swap( e, old, function() { - if ( jQuery(e).is(':visible') ) { - oHeight = e.offsetHeight; - oWidth = e.offsetWidth; - } else { - e = jQuery(e.cloneNode(true)) - .find(":radio").removeAttr("checked").end() - .css({ - visibility: "hidden", position: "absolute", display: "block", right: "0", left: "0" - }).appendTo(e.parentNode)[0]; - - var parPos = jQuery.css(e.parentNode,"position") || "static"; - if ( parPos == "static" ) - e.parentNode.style.position = "relative"; - - oHeight = e.clientHeight; - oWidth = e.clientWidth; - - if ( parPos == "static" ) - e.parentNode.style.position = "static"; - - e.parentNode.removeChild(e); - } - }); - - return p == "height" ? oHeight : oWidth; - } - - return jQuery.curCSS( e, p ); - }, - - curCSS: function(elem, prop, force) { - var ret, stack = [], swap = []; - - // A helper method for determining if an element's values are broken - function color(a){ - if ( !jQuery.browser.safari ) - return false; - - var ret = document.defaultView.getComputedStyle(a,null); - return !ret || ret.getPropertyValue("color") == ""; - } - - if (prop == "opacity" && jQuery.browser.msie) { - ret = jQuery.attr(elem.style, "opacity"); - return ret == "" ? "1" : ret; - } - - if (prop.match(/float/i)) - prop = styleFloat; - - if (!force && elem.style[prop]) - ret = elem.style[prop]; - - else if (document.defaultView && document.defaultView.getComputedStyle) { - - if (prop.match(/float/i)) - prop = "float"; - - prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase(); - var cur = document.defaultView.getComputedStyle(elem, null); - - if ( cur && !color(elem) ) - ret = cur.getPropertyValue(prop); - - // If the element isn't reporting its values properly in Safari - // then some display: none elements are involved - else { - // Locate all of the parent display: none elements - for ( var a = elem; a && color(a); a = a.parentNode ) - stack.unshift(a); - - // Go through and make them visible, but in reverse - // (It would be better if we knew the exact display type that they had) - for ( a = 0; a < stack.length; a++ ) - if ( color(stack[a]) ) { - swap[a] = stack[a].style.display; - stack[a].style.display = "block"; - } - - // Since we flip the display style, we have to handle that - // one special, otherwise get the value - ret = prop == "display" && swap[stack.length-1] != null ? - "none" : - document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop) || ""; - - // Finally, revert the display styles back - for ( a = 0; a < swap.length; a++ ) - if ( swap[a] != null ) - stack[a].style.display = swap[a]; - } - - if ( prop == "opacity" && ret == "" ) - ret = "1"; - - } else if (elem.currentStyle) { - var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();}); - ret = elem.currentStyle[prop] || elem.currentStyle[newProp]; - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !/^\d+(px)?$/i.test(ret) && /^\d/.test(ret) ) { - var style = elem.style.left; - var runtimeStyle = elem.runtimeStyle.left; - elem.runtimeStyle.left = elem.currentStyle.left; - elem.style.left = ret || 0; - ret = elem.style.pixelLeft + "px"; - elem.style.left = style; - elem.runtimeStyle.left = runtimeStyle; - } - } - - return ret; - }, - - clean: function(a, doc) { - var r = []; - doc = doc || document; - - jQuery.each( a, function(i,arg){ - if ( !arg ) return; - - if ( arg.constructor == Number ) - arg = arg.toString(); - - // Convert html string into DOM nodes - if ( typeof arg == "string" ) { - // Fix "XHTML"-style tags in all browsers - arg = arg.replace(/(<(\w+)[^>]*?)\/>/g, function(m, all, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area)$/i)? m : all+">"; - }); - - // Trim whitespace, otherwise indexOf won't work as expected - var s = jQuery.trim(arg).toLowerCase(), div = doc.createElement("div"), tb = []; - - var wrap = - // option or optgroup - !s.indexOf("", ""] || - - !s.indexOf("", ""] || - - s.match(/^<(thead|tbody|tfoot|colg|cap)/) && - [1, "", "
"] || - - !s.indexOf("", ""] || - - // matched above - (!s.indexOf("", ""] || - - !s.indexOf("", ""] || - - // IE can't serialize and - - - - - - - -
-
-
-

-

-

-

-
-
- - diff --git a/trunk/infrastructure/ace/www/domline.js b/trunk/infrastructure/ace/www/domline.js deleted file mode 100644 index 70f86cc..0000000 --- a/trunk/infrastructure/ace/www/domline.js +++ /dev/null @@ -1,210 +0,0 @@ -// 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var domline = {}; -domline.noop = function() {}; -domline.identity = function(x) { return x; }; - -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) { - // add class to line - lineClass = (lineClass ? lineClass+' ' : '')+c.substring(5); - } - }); - return lineClass; -} - -// 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 }; - - var browser = (optBrowser || {}); - var document = optDocument; - - if (document) { - result.node = document.createElement("div"); - } - else { - result.node = {innerHTML: '', className: ''}; - } - - var html = []; - var preHtml, postHtml; - var curHTML = null; - 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) { - var listType = /(?:^| )list:(\S+)/.exec(cls); - if (listType) { - listType = listType[1]; - if (listType) { - preHtml = '
  • '; - postHtml = '
'; - } - result.lineMarker += txt.length; - return; // don't append any text - } - } - 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('tag') >= 0) { - cls = cls.replace(/(^| )tag:(\S+)/g, function(x0, space, tag) { - if (! simpleTags) simpleTags = []; - simpleTags.push(tag.toLowerCase()); - return space+tag; - }); - } - if ((! txt) && cls) { - lineClass = domline.addToLineClass(lineClass, cls); - } - else if (txt) { - var extraOpenTags = ""; - var extraCloseTags = ""; - if (href) { - extraOpenTags = extraOpenTags+''; - extraCloseTags = ''+extraCloseTags; - } - if (simpleTags) { - simpleTags.sort(); - extraOpenTags = extraOpenTags+'<'+simpleTags.join('><')+'>'; - simpleTags.reverse(); - extraCloseTags = ''+extraCloseTags; - } - html.push('',extraOpenTags, - perTextNodeProcess(domline.escapeHTML(txt)), - extraCloseTags,''); - } - }; - result.clearSpans = function() { - html = []; - lineClass = ''; // non-null to cause update - result.lineMarker = 0; - }; - function writeHTML() { - var newHTML = perHtmlLineProcess(html.join('')); - if (! newHTML) { - if ((! document) || (! optBrowser)) { - newHTML += ' '; - } - else if (! browser.msie) { - newHTML += '
'; - } - } - if (nonEmpty) { - newHTML = (preHtml||'')+newHTML+(postHtml||''); - } - html = preHtml = postHtml = null; // free memory - if (newHTML !== curHTML) { - curHTML = newHTML; - result.node.innerHTML = curHTML; - } - if (lineClass !== null) result.node.className = lineClass; - } - result.prepareForAdd = writeHTML; - result.finishUpdate = writeHTML; - result.getInnerHTML = function() { return curHTML || ''; }; - - return result; -}; - -domline.escapeHTML = function(s) { - var re = /[&<>'"]/g; /']/; // stupid indentation thing - if (! re.MAP) { - // persisted across function calls! - re.MAP = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - } - return s.replace(re, function(c) { return re.MAP[c]; }); -}; - -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) { - 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--) { - var p = parts[i]; - if (p == " ") { - if (endOfLine || beforeSpace) - parts[i] = ' '; - endOfLine = false; - beforeSpace = true; - } - else if (p.charAt(0) != "<") { - endOfLine = false; - beforeSpace = false; - } - } - // beginning of line is nbsp - for(var i=0;i= 0, "bad old text length"); - assert(this[2] >= 0, "bad new text length"); - assert((this.length % 3) == 0, "bad array length"); - assert(this.length >= 6, "must be at least one strip"); - var numStrips = this.numStrips(); - var oldLen = this[1]; - var newLen = this[2]; - // iterate over the "text strips" - var actualNewLen = 0; - this.eachStrip(function(startIndex, numTaken, newText, i) { - var s = startIndex, t = numTaken, n = newText; - var isFirst = (i == 0); - var isLast = (i == numStrips-1); - assert(t >= 0, "can't take negative number of chars"); - assert(isFirst || t > 0, "all strips but first must take"); - assert((t > 0) || (s == 0), "if first strip doesn't take, must have 0 startIndex"); - assert(s >= 0 && s + t <= oldLen, "bad index: "+this.toString()); - assert(t > 0 || n.length > 0 || (isFirst && isLast), "empty strip must be first and only"); - if (! isLast) { - var s2 = this[3 + i*3 + 3]; // startIndex of following strip - var gap = s2 - (s + t); - assert(gap >= 0, "overlapping or out-of-order strips: "+this.toString()); - assert(gap > 0 || n.length > 0, "touching strips with no added text"); - } - actualNewLen += t + n.length; - }); - 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 = []; - this.eachStrip(function (s, t, n) { - buf.push(text.substr(s, t), n); - }); - return buf.join(''); - } - - function _makeBuilder(oldLen, supportAuthors) { - var C = Changeset(oldLen); - if (supportAuthors) { - _ensureAuthors(C); - } - return C.builder(); - } - - function _getNumInserted(C) { - var numChars = 0; - C.eachStrip(function(s,t,n) { - numChars += n.length; - }); - return numChars; - } - - function _ensureAuthors(C) { - if (! C.authors) { - C.setAuthor(); - } - return C; - } - - array.setAuthor = function(author) { - var C = this; - // authors array has even length >= 2; - // alternates [numChars1, author1, numChars2, author2]; - // all numChars > 0 unless there is exactly one, in which - // case it can be == 0. - C.authors = [_getNumInserted(C), author || '']; - return C; - } - - array.builder = function() { - // normal pattern is Changeset(oldLength).builder().appendOldText(...). ... - // builder methods mutate this! - var C = this; - // OOP style: state in environment - var self; - return self = { - appendNewText: function(str, author) { - C[C.length-1] += str; - C[2] += str.length; - - if (C.authors) { - var a = (author || ''); - var lastAuthorPtr = C.authors.length-1; - var lastAuthorLengthPtr = C.authors.length-2; - if ((!a) || a == C.authors[lastAuthorPtr]) { - C.authors[lastAuthorLengthPtr] += str.length; - } - else if (0 == C.authors[lastAuthorLengthPtr]) { - C.authors[lastAuthorLengthPtr] = str.length; - C.authors[lastAuthorPtr] = (a || C.authors[lastAuthorPtr]); - } - else { - C.authors.push(str.length, a); - } - } - - return self; - }, - appendOldText: function(startIndex, numTaken) { - if (numTaken == 0) return self; - // properties of last strip... - var s = C[C.length-3], t = C[C.length-2], n = C[C.length-1]; - if (t == 0 && n == "") { - // must be empty changeset, one strip that doesn't take old chars or add new ones - C[C.length-3] = startIndex; - C[C.length-2] = numTaken; - } - else if (n == "" && (s+t == startIndex)) { - C[C.length-2] += numTaken; // take more - } - else C.push(startIndex, numTaken, ""); // add a strip - C[2] += numTaken; - C.checkRep(); - return self; - }, - toChangeset: function() { return C; } - }; - } - - array.authorSlicer = function(outputBuilder) { - return _makeAuthorSlicer(this, outputBuilder); - } - - function _makeAuthorSlicer(changesetOrAuthorsIn, builderOut) { - // "builderOut" only needs to support appendNewText - var authors; // considered immutable - if (changesetOrAuthorsIn.isChangeset) { - authors = changesetOrAuthorsIn.authors; - } - 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 curAuthorWidth() { return authors[authorPtr]; } - function assertNotAtEnd() { assert(! atEnd, "_authorSlicer: can't move past end"); } - function forwardInAuthor(numChars) { - charWithinAuthor += numChars; - charIndex += numChars; - } - function nextAuthor() { - assertNotAtEnd(); - assert(charWithinAuthor == curAuthorWidth(), "_authorSlicer: not at author end"); - charWithinAuthor = 0; - authorPtr += 2; - if (authorPtr == authors.length) { - atEnd = true; - } - } - - var self; - return self = { - skipChars: function(n) { - assert(n >= 0, "_authorSlicer: can't skip negative n"); - if (n == 0) return; - assertNotAtEnd(); - - var leftToSkip = n; - while (leftToSkip > 0) { - var leftInAuthor = curAuthorWidth() - charWithinAuthor; - if (leftToSkip >= leftInAuthor) { - forwardInAuthor(leftInAuthor); - leftToSkip -= leftInAuthor; - nextAuthor(); - } - else { - forwardInAuthor(leftToSkip); - leftToSkip = 0; - } - } - }, - takeChars: function(n, text) { - assert(n >= 0, "_authorSlicer: can't take negative n"); - if (n == 0) return; - assertNotAtEnd(); - assert(n == text.length, "_authorSlicer: bad text length"); - - var textLeft = text; - var leftToTake = n; - while (leftToTake > 0) { - if (curAuthorWidth() > 0 && charWithinAuthor < curAuthorWidth()) { - // at least one char to take from current author - var leftInAuthor = (curAuthorWidth() - charWithinAuthor); - assert(leftInAuthor > 0, "_authorSlicer: should have leftInAuthor > 0"); - var toTake = min(leftInAuthor, leftToTake); - assert(toTake > 0, "_authorSlicer: should have toTake > 0"); - builderOut.appendNewText(textLeft.substring(0, toTake), curAuthor()); - forwardInAuthor(toTake); - leftToTake -= toTake; - textLeft = textLeft.substring(toTake); - } - assert(charWithinAuthor <= curAuthorWidth(), "_authorSlicer: past end of author"); - if (charWithinAuthor == curAuthorWidth()) { - nextAuthor(); - } - } - }, - setBuilder: function(builder) { - builderOut = builder; - } - }; - } - - 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 - var charWithinStrip = 0; // 0 <= charWithinStrip < curStripWidth() - var atEnd = false; - - var authorSlicer; - if (C.authors) { - authorSlicer = _makeAuthorSlicer(C.authors, output); - } - - var ptr = 3; - function curStartIndex() { return C[ptr]; } - function curNumTaken() { return C[ptr+1]; } - function curNewText() { return C[ptr+2]; } - function curStripWidth() { return curNumTaken() + curNewText().length; } - function assertNotAtEnd() { assert(! atEnd, "_slicer: can't move past changeset end"); } - function forwardInStrip(numChars) { - charWithinStrip += numChars; - charIndex += numChars; - } - function nextStrip() { - assertNotAtEnd(); - assert(charWithinStrip == curStripWidth(), "_slicer: not at strip end"); - charWithinStrip = 0; - stripIndex++; - ptr += 3; - if (stripIndex == C.numStrips()) { - atEnd = true; - } - } - function curNumNewCharsInRange(start, end) { - // takes two indices into the current strip's combined "taken" and "new" - // chars, and returns how many "new" chars are included in the range - assert(start <= end, "_slicer: curNumNewCharsInRange given out-of-order indices"); - var nt = curNumTaken(); - var nn = curNewText().length; - var s = nt; - var e = nt+nn; - if (s < start) s = start; - if (e > end) e = end; - if (e < s) return 0; - return e-s; - } - - var self; - return self = { - skipChars: function (n) { - assert(n >= 0, "_slicer: can't skip negative n"); - if (n == 0) return; - assertNotAtEnd(); - - var leftToSkip = n; - while (leftToSkip > 0) { - var leftInStrip = curStripWidth() - charWithinStrip; - if (leftToSkip >= leftInStrip) { - forwardInStrip(leftInStrip); - - if (authorSlicer) - authorSlicer.skipChars(curNumNewCharsInRange(charWithinStrip, - charWithinStrip + leftInStrip)); - - leftToSkip -= leftInStrip; - nextStrip(); - } - else { - if (authorSlicer) - authorSlicer.skipChars(curNumNewCharsInRange(charWithinStrip, - charWithinStrip + leftToSkip)); - - forwardInStrip(leftToSkip); - leftToSkip = 0; - } - } - }, - takeChars: function (n) { - assert(n >= 0, "_slicer: can't take negative n"); - if (n == 0) return; - assertNotAtEnd(); - - var leftToTake = n; - while (leftToTake > 0) { - if (curNumTaken() > 0 && charWithinStrip < curNumTaken()) { - // at least one char to take from current strip's numTaken - var leftInTaken = (curNumTaken() - charWithinStrip); - assert(leftInTaken > 0, "_slicer: should have leftInTaken > 0"); - var toTake = min(leftInTaken, leftToTake); - assert(toTake > 0, "_slicer: should have toTake > 0"); - output.appendOldText(curStartIndex() + charWithinStrip, toTake); - forwardInStrip(toTake); - leftToTake -= toTake; - } - if (leftToTake > 0 && curNewText().length > 0 && charWithinStrip >= curNumTaken() && - 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"); - var toTake = min(leftInNewText, leftToTake); - assert(toTake > 0, "_slicer: should have toTake > 0"); - var newText = curNewText().substr(charWithinStrip - curNumTaken(), toTake); - if (authorSlicer) { - authorSlicer.takeChars(newText.length, newText); - } - else { - output.appendNewText(newText); - } - forwardInStrip(toTake); - leftToTake -= toTake; - } - assert(charWithinStrip <= curStripWidth(), "_slicer: past end of strip"); - if (charWithinStrip == curStripWidth()) { - nextStrip(); - } - } - }, - skipTo: function(n) { - self.skipChars(n - charIndex); - } - }; - } - - array.slicer = function(outputBuilder) { - return _makeSlicer(this, outputBuilder); - } - - array.compose = function(next) { - assert(next.oldLen() == this.newLen(), "mismatched composition"); - - var builder = _makeBuilder(this.oldLen(), !!(this.authors || next.authors)); - var slicer = _makeSlicer(this, builder); - - var authorSlicer; - if (next.authors) { - authorSlicer = _makeAuthorSlicer(next.authors, builder); - } - - next.eachStrip(function(s, t, n) { - slicer.skipTo(s); - slicer.takeChars(t); - if (authorSlicer) { - authorSlicer.takeChars(n.length, n); - } - else { - builder.appendNewText(n); - } - }, this); - - return builder.toChangeset(); - }; - - array.traverser = function() { - return _makeTraverser(this); - } - - function _makeTraverser(C) { - var s = C[3], t = C[4], n = C[5]; - var nextIndex = 6; - var indexIntoNewText = 0; - - var authorSlicer; - if (C.authors) { - authorSlicer = _makeAuthorSlicer(C.authors, null); - } - - function advanceIfPossible() { - if (t == 0 && n == "" && nextIndex < C.length) { - s = C[nextIndex]; - t = C[nextIndex+1]; - n = C[nextIndex+2]; - nextIndex += 3; - } - } - - var self; - return self = { - numTakenChars: function() { - // if starts with taken characters, then how many, else 0 - return (t > 0) ? t : 0; - }, - numNewChars: function() { - // if starts with new characters, then how many, else 0 - return (t == 0 && n.length > 0) ? n.length : 0; - }, - takenCharsStart: function() { - return (self.numTakenChars() > 0) ? s : 0; - }, - hasMore: function() { - return self.numTakenChars() > 0 || self.numNewChars() > 0; - }, - curIndex: function() { - return indexIntoNewText; - }, - consumeTakenChars: function (x) { - assert(self.numTakenChars() > 0, "_traverser: no taken chars"); - assert(x >= 0 && x <= self.numTakenChars(), "_traverser: bad number of taken chars"); - if (x == 0) return; - if (t == x) { s = 0; t = 0; } - else { s += x; t -= x; } - indexIntoNewText += x; - advanceIfPossible(); - }, - consumeNewChars: function(x) { - return self.appendNewChars(x, null); - }, - appendNewChars: function(x, builder) { - assert(self.numNewChars() > 0, "_traverser: no new chars"); - assert(x >= 0 && x <= self.numNewChars(), "_traverser: bad number of new chars"); - if (x == 0) return ""; - var str = n.substring(0, x); - n = n.substring(x); - indexIntoNewText += x; - advanceIfPossible(); - - if (builder) { - if (authorSlicer) { - authorSlicer.setBuilder(builder); - authorSlicer.takeChars(x, str); - } - else { - builder.appendNewText(str); - } - } - else { - if (authorSlicer) authorSlicer.skipChars(x); - return str; - } - }, - consumeAvailableTakenChars: function() { - return self.consumeTakenChars(self.numTakenChars()); - }, - consumeAvailableNewChars: function() { - return self.consumeNewChars(self.numNewChars()); - }, - appendAvailableNewChars: function(builder) { - return self.appendNewChars(self.numNewChars(), builder); - } - }; - } - - array.follow = function(prev, reverseInsertOrder) { - // prev: Changeset, reverseInsertOrder: boolean - - // A.compose(B.follow(A)) is the merging of Changesets A and B, which operate on the same old text. - // It is always the same as B.compose(A.follow(B, true)). - - assert(prev.oldLen() == this.oldLen(), "mismatched follow: "+prev.oldLen()+"/"+this.oldLen()); - var builder = _makeBuilder(prev.newLen(), !! this.authors); - var a = _makeTraverser(prev); - var b = _makeTraverser(this); - while (a.hasMore() || b.hasMore()) { - if (a.numNewChars() > 0 && ! reverseInsertOrder) { - builder.appendOldText(a.curIndex(), a.numNewChars()); - a.consumeAvailableNewChars(); - } - else if (b.numNewChars() > 0) { - b.appendAvailableNewChars(builder); - } - else if (a.numNewChars() > 0 && reverseInsertOrder) { - builder.appendOldText(a.curIndex(), a.numNewChars()); - a.consumeAvailableNewChars(); - } - else if (! b.hasMore()) a.consumeAvailableTakenChars(); - else if (! a.hasMore()) b.consumeAvailableTakenChars(); - else { - var x = a.takenCharsStart(); - var y = b.takenCharsStart(); - if (x < y) a.consumeTakenChars(min(a.numTakenChars(), y-x)); - else if (y < x) b.consumeTakenChars(min(b.numTakenChars(), x-y)); - else { - var takenByBoth = min(a.numTakenChars(), b.numTakenChars()); - builder.appendOldText(a.curIndex(), takenByBoth); - a.consumeTakenChars(takenByBoth); - b.consumeTakenChars(takenByBoth); - } - } - } - return builder.toChangeset(); - } - - array.encodeToString = function(asBinary) { - var stringDataArray = []; - var numsArray = []; - if (! asBinary) numsArray.push(this[0]); - numsArray.push(this[1], this[2]); - this.eachStrip(function(s, t, n) { - numsArray.push(s, t, n.length); - stringDataArray.push(n); - }, this); - if (! asBinary) { - return numsArray.join(',')+'|'+stringDataArray.join(''); - } - else { - return "A" + Changeset.numberArrayToString(numsArray) - +escapeCrazyUnicode(stringDataArray.join('')); - } - } - - function escapeCrazyUnicode(str) { - return str.replace(/\\/g, '\\\\').replace(/[\ud800-\udfff]/g, function (c) { - return "\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4); - }); - } - - array.applyToAttributedText = Changeset.applyToAttributedText; - - function splicesFromChanges(c) { - var splices = []; - // get a list of splices, [startChar, endChar, newText] - var traverser = c.traverser(); - var oldTextLength = c.oldLen(); - var indexIntoOldText = 0; - while (traverser.hasMore() || indexIntoOldText < oldTextLength) { - var newText = ""; - var startChar = indexIntoOldText; - var endChar = indexIntoOldText; - if (traverser.numNewChars() > 0) { - newText = traverser.consumeAvailableNewChars(); - } - if (traverser.hasMore()) { - endChar = traverser.takenCharsStart(); - indexIntoOldText = endChar + traverser.numTakenChars(); - traverser.consumeAvailableTakenChars(); - } - else { - endChar = oldTextLength; - indexIntoOldText = endChar; - } - if (endChar != startChar || newText.length > 0) { - splices.push([startChar, endChar, newText]); - } - } - 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. - // Because insertions indicate intention, it doesn't matter what this string is, and even - // if the selectionChangeset is made to "follow" other changes it will still be the only - // insertion. - var selectionChangeset = - Changeset(changeset.oldLen()).builder().appendOldText(0, selStartChar).appendNewText( - "X").appendOldText(selEndChar, changeset.oldLen() - selEndChar).toChangeset(); - var newSelectionChangeset = selectionChangeset.follow(changeset, insertionsAfter); - var selectionSplices = newSelectionChangeset.toSplices(); - function includeChar(i) { - if (! includeChar.calledYet) { - selStartChar = i; - selEndChar = i; - includeChar.calledYet = true; - } - else { - if (i < selStartChar) selStartChar = i; - if (i > selEndChar) selEndChar = i; - } - } - for(var i=0; i TRUNC) a.push("..."); - return a.join(' '); - } - function unescapeCrazyUnicode(str) { - return str.replace(/\\(u....|\\)/g, function(seq) { - if (seq == "\\\\") return "\\"; - return String.fromCharCode(Number("0x"+seq.substring(2))); - }); - } - - var numData, stringData; - var binary = false; - var typ = str.charAt(0); - if (typ == "B" || typ == "A") { - var result = Changeset.numberArrayFromString(str, 1); - numData = result[0]; - stringData = result[1]; - if (typ == "A") { - stringData = unescapeCrazyUnicode(stringData); - } - binary = true; - } - else if (typ == "C") { - var barPosition = str.indexOf('|'); - numData = str.substring(0, barPosition).split(','); - stringData = str.substring(barPosition+1); - } - else { - error("Not a changeset: "+toHex(str)); - } - var stringDataOffset = 0; - var array = []; - var ptr; - if (binary) { - array.push("Changeset", numData[0], numData[1]); - var ptr = 2; - } - else { - array.push(numData[0], Number(numData[1]), Number(numData[2])); - var ptr = 3; - } - while (ptr < numData.length) { - array.push(Number(numData[ptr++]), Number(numData[ptr++])); - var newTextLength = Number(numData[ptr++]); - array.push(stringData.substr(stringDataOffset, newTextLength)); - stringDataOffset += newTextLength; - } - if (stringDataOffset != stringData.length) { - error("Extra character data beyond end of encoded string ("+toHex(str)+")"); - } - return Changeset(array); -}; - -Changeset.numberArrayToString = function(nums) { - var array = []; - function writeNum(n) { - // does not support negative numbers - var twentyEightBit = (n & 0xfffffff); - if (twentyEightBit <= 0x7fff) { - array.push(String.fromCharCode(twentyEightBit)); - } - else { - array.push(String.fromCharCode(0xa000 | (twentyEightBit >> 15), - twentyEightBit & 0x7fff)); - } - } - writeNum(nums.length); - var len = nums.length; - for(var i=0;i 0x7fff) { - if (n >= 0xa000) { - n = (((n & 0x1fff) << 15) | str.charCodeAt(strIndex++)); - } - else { - // legacy format - n = (((n & 0x1fff) << 16) | str.charCodeAt(strIndex++)); - } - } - return n; - } - var len = readNum(); - for(var i=0;i> 1); - s += s; - if (times & 1) s += str; - return s; - } - function chr(n) { return String.fromCharCode(n+48); } - function ord(c) { return c.charCodeAt(0)-48; } - function runMatcher(c) { - // Takes "A" and returns /\u0041+/g . - // Avoid creating new objects unnecessarily by caching matchers - // as properties of this function. - var re = runMatcher[c]; - if (re) return re; - re = runMatcher[c] = new RegExp("\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)+"+", 'g'); - return re; - } - function runLength(str, idx, c) { - var re = runMatcher(c); - re.lastIndex = idx; - var result = re.exec(str); - if (result && result[0]) { - return result[0].length; - } - return 0; - } - - // emptyObj may be a StorableObject - Changeset.initAttributedText = function(emptyObj, initialString, initialAuthor) { - var obj = emptyObj; - obj.authorMap = { 1: (initialAuthor || '') }; - obj.text = (initialString || ''); - obj.attribs = repeatString(chr(1), obj.text.length); - return obj; - }; - Changeset.gcAttributedText = function(atObj) { - // "garbage collect" the list of authors - var removedAuthors = []; - for(var a in atObj.authorMap) { - if (atObj.attribs.indexOf(chr(Number(a))) < 0) { - removedAuthors.push(atObj.authorMap[a]); - delete atObj.authorMap[a]; - } - } - return removedAuthors; - }; - Changeset.cloneAttributedText = function(emptyObj, atObj) { - var obj = emptyObj; - obj.text = atObj.text; // string - if (atObj.attribs) obj.attribs = atObj.attribs; // string - if (atObj.attribs_c) obj.attribs_c = atObj.attribs_c; // string - obj.authorMap = {}; - for(var a in atObj.authorMap) { - obj.authorMap[a] = atObj.authorMap[a]; - } - return obj; - }; - Changeset.applyToAttributedText = function(atObj, C) { - C = (C || this); - var oldText = atObj.text; - var oldAttribs = atObj.attribs; - Changeset._assert(C.isChangeset, "applyToAttributedText: 'this' is not a changeset"); - Changeset._assert(oldText.length == C.oldLen(), - "applyToAttributedText: mismatch "+oldText.length+" / "+C.oldLen()); - var textBuf = []; - var attribsBuf = []; - var authorMap = atObj.authorMap; - function authorId(author) { - for(var a in authorMap) { - if (authorMap[Number(a)] === author) { - return Number(a); - } - } - for(var i=1;i<=60000;i++) { - // don't use "in" because it's currently broken on StorableObjects - if (authorMap[i] === undefined) { - authorMap[i] = author; - return i; - } - } - } - var myBuilder = { appendNewText: function(txt, author) { - // object that acts as a "builder" in that it receives requests from - // authorSlicer to append text attributed to different authors - attribsBuf.push(repeatString(chr(authorId(author)), txt.length)); - } }; - var authorSlicer; - if (C.authors) { - authorSlicer = C.authorSlicer(myBuilder); - } - C.eachStrip(function (s, t, n) { - textBuf.push(oldText.substr(s, t), n); - attribsBuf.push(oldAttribs.substr(s, t)); - if (authorSlicer) { - authorSlicer.takeChars(n.length, n); - } - else { - myBuilder.appendNewText(n, ''); - } - }); - atObj.text = textBuf.join(''); - atObj.attribs = attribsBuf.join(''); - return atObj; - }; - Changeset.getAttributedTextCharAuthor = function(atObj, idx) { - return atObj.authorMap[ord(atObj.attribs.charAt(idx))]; - }; - Changeset.getAttributedTextCharRunLength = function(atObj, idx) { - var c = atObj.attribs.charAt(idx); - return runLength(atObj.attribs, idx, c); - }; - Changeset.eachAuthorInAttributedText = function(atObj, func) { - // 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]; - }; - // Compressed attributed text can be cloned, but nothing else until uncompressed!! - Changeset.compressAttributedText = function(atObj) { - // idempotent, mutates the object, returns it - if (atObj.attribs) { - atObj.attribs_c = atObj.attribs.replace(/([\s\S])\1{0,63}/g, function(run) { - return run.charAt(0)+chr(run.length);; - }); - delete atObj.attribs; - } - return atObj; - }; - Changeset.decompressAttributedText = function(atObj) { - // idempotent, mutates the object, returns it - if (atObj.attribs_c) { - atObj.attribs = atObj.attribs_c.replace(/[\s\S][\s\S]/g, function(run) { - return repeatString(run.charAt(0), ord(run.charAt(1))); - }); - delete atObj.attribs_c; - } - return atObj; - }; -})(); diff --git a/trunk/infrastructure/ace/www/easysync2.js b/trunk/infrastructure/ace/www/easysync2.js deleted file mode 100644 index efc5b99..0000000 --- a/trunk/infrastructure/ace/www/easysync2.js +++ /dev/null @@ -1,1968 +0,0 @@ -// 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//var _opt = (this.Easysync2Support || null); -var _opt = null; // disable optimization for now - -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) { - var str = String(attrib); - if (str in p.attribToNum) { - return p.attribToNum[str]; - } - if (dontAddIfAbsent) { - return -1; - } - var num = p.nextNum++; - p.attribToNum[str] = num; - p.numToAttrib[num] = [String(attrib[0]||''), - String(attrib[1]||'')]; - return 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) { - var pair = p.numToAttrib[num]; - if (! pair) return ''; - return pair[0]; - }; - - p.getAttribValue = function(num) { - var pair = p.numToAttrib[num]; - if (! pair) return ''; - return pair[1]; - }; - - 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.fromJsonable = function(obj) { - p.numToAttrib = obj.numToAttrib; - p.nextNum = obj.nextNum; - p.attribToNum = {}; - for(var n in p.numToAttrib) { - p.attribToNum[String(p.numToAttrib[n])] = Number(n); - } - return p; - }; - - return p; -} - -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) { - var msg = Array.prototype.slice.call(arguments, 1).join(''); - 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) { - 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; -}; - -Changeset.oldLen = function(cs) { - return Changeset.unpack(cs).oldLen; -}; -Changeset.newLen = function(cs) { - return Changeset.unpack(cs).newLen; -}; - -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() { - prevIndex = curIndex; - var result; - if (_opt) { - result = _opt.nextOpInString(opsStr, curIndex); - if (result) { - if (result.opcode() == '?') { - Changeset.error("Hit error opcode in op stream"); - } - curIndex = result.lastIndex(); - } - } - else { - regex.lastIndex = curIndex; - result = regex.exec(opsStr); - curIndex = regex.lastIndex; - if (result[0] == '?') { - Changeset.error("Hit error opcode in op stream"); - } - } - return result; - } - var regexResult = nextRegexMatch(); - var obj = Changeset.newOp(); - function next(optObj) { - var op = (optObj || obj); - 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]) { - op.attribs = regexResult[1]; - op.lines = Changeset.parseNum(regexResult[2] || 0); - op.opcode = regexResult[3]; - op.chars = Changeset.parseNum(regexResult[4]); - regexResult = nextRegexMatch(); - } - else { - Changeset.clearOp(op); - } - return op; - } - function hasNext() { return !! (_opt ? regexResult : regexResult[0]); } - function lastIndex() { return prevIndex; } - return {next: next, hasNext: hasNext, lastIndex: lastIndex}; -}; - -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.cloneOp = function(op) { - return {opcode: op.opcode, chars: op.chars, lines: op.lines, attribs: op.attribs}; -}; -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) { - // just for debugging - if (! op.opcode) return 'null'; - var assem = Changeset.opAssembler(); - assem.append(op); - return assem.toString(); -}; -Changeset.stringOp = function(str) { - // just for debugging - return Changeset.opIterator(str).next(); -}; - -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); - var oldLen = unpacked.oldLen; - var newLen = unpacked.newLen; - var ops = unpacked.ops; - var charBank = unpacked.charBank; - - var assem = Changeset.smartOpAssembler(); - var oldPos = 0; - var calcNewLen = 0; - var numInserted = 0; - var iter = Changeset.opIterator(ops); - 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); - break; - } - } - assem.append(o); - } - - calcNewLen += oldLen - oldPos; - charBank = charBank.substring(0, numInserted); - while (charBank.length < numInserted) { - charBank += "?"; - } - - assem.endDocument(); - var normalized = Changeset.pack(oldLen, calcNewLen, assem.toString(), charBank); - Changeset.assert(normalized == cs, normalized,' != ',cs); - - return cs; -} - -Changeset.smartOpAssembler = function() { - // Like opAssembler but able to produce conforming changesets - // from slightly looser input, at the cost of speed. - // Specifically: - // - merges consecutive operations that can be merged - // - 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(); - var assem = Changeset.stringAssembler(); - var lastOpcode = ''; - var lengthChange = 0; - - function flushKeeps() { - assem.append(keepAssem.toString()); - keepAssem.clear(); - } - - 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; - - if (op.opcode == '-') { - if (lastOpcode == '=') { - flushKeeps(); - } - minusAssem.append(op); - lengthChange -= op.chars; - } - else if (op.opcode == '+') { - if (lastOpcode == '=') { - flushKeeps(); - } - plusAssem.append(op); - lengthChange += op.chars; - } - else if (op.opcode == '=') { - if (lastOpcode != '=') { - flushPlusMinus(); - } - keepAssem.append(op); - } - lastOpcode = op.opcode; - } - - 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) { - op.chars = text.length; - op.lines = 0; - append(op); - } - else { - op.chars = lastNewlinePos+1; - op.lines = text.match(/\n/g).length; - append(op); - op.chars = text.length - (lastNewlinePos+1); - op.lines = 0; - append(op); - } - } - - function toString() { - flushPlusMinus(); - flushKeeps(); - return assem.toString(); - } - - function clear() { - minusAssem.clear(); - plusAssem.clear(); - keepAssem.clear(); - assem.clear(); - lengthChange = 0; - } - - function endDocument() { - keepAssem.endDocument(); - } - - function getLengthChange() { - return lengthChange; - } - - return {append: append, toString: toString, clear: clear, endDocument: endDocument, - appendOpWithText: appendOpWithText, getLengthChange: getLengthChange }; -}; - -if (_opt) { - Changeset.mergingOpAssembler = function() { - var assem = _opt.mergingOpAssembler(); - - function append(op) { - assem.append(op.opcode, op.chars, op.lines, op.attribs); - } - function toString() { - return assem.toString(); - } - function clear() { - assem.clear(); - } - function endDocument() { - assem.endDocument(); - } - - return {append: append, toString: toString, clear: clear, endDocument: endDocument}; - }; -} -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 - // operations. - var assem = Changeset.opAssembler(); - var bufOp = Changeset.newOp(); - - // If we get, for example, insertions [xxx\n,yyy], those don't merge, - // but if we get [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n]. - // This variable stores the length of yyy and any other newline-less - // ops immediately after it. - var bufOpAdditionalCharsAfterNewline = 0; - - function flush(isEndDocument) { - if (bufOp.opcode) { - if (isEndDocument && bufOp.opcode == '=' && ! bufOp.attribs) { - // final merged keep, leave it implicit - } - else { - assem.append(bufOp); - if (bufOpAdditionalCharsAfterNewline) { - bufOp.chars = bufOpAdditionalCharsAfterNewline; - bufOp.lines = 0; - assem.append(bufOp); - bufOpAdditionalCharsAfterNewline = 0; - } - } - bufOp.opcode = ''; - } - } - 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) { - // both bufOp and op are in-line - bufOp.chars += op.chars; - } - else { - // append in-line text to multi-line bufOp - bufOpAdditionalCharsAfterNewline += op.chars; - } - } - else { - flush(); - Changeset.copyOp(op, bufOp); - } - } - } - function endDocument() { - flush(true); - } - function toString() { - flush(); - return assem.toString(); - } - function clear() { - assem.clear(); - Changeset.clearOp(bufOp); - } - return {append: append, toString: toString, clear: clear, endDocument: endDocument}; - }; -} - -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) { - assem.append(op.opcode, op.chars, op.lines, op.attribs); - } - function toString() { - return assem.toString(); - } - function clear() { - assem.clear(); - } - return {append: append, toString: toString, clear: clear}; - }; -} -else { - 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); - if (op.lines) { - pieces.push('|', Changeset.numToString(op.lines)); - } - pieces.push(op.opcode); - pieces.push(Changeset.numToString(op.chars)); - } - function toString() { - return pieces.join(''); - } - function clear() { - pieces.length = 0; - } - return {append: append, toString: toString, clear: clear}; - }; -} - -Changeset.stringIterator = function(str) { - var curIndex = 0; - function assertRemaining(n) { - Changeset.assert(n <= remaining(), "!(",n," <= ",remaining(),")"); - } - function take(n) { - assertRemaining(n); - var s = str.substr(curIndex, n); - curIndex += n; - return s; - } - function peek(n) { - assertRemaining(n); - var s = str.substr(curIndex, n); - return s; - } - function skip(n) { - assertRemaining(n); - curIndex += n; - } - function remaining() { - return str.length - curIndex; - } - return {take:take, skip:skip, remaining:remaining, peek:peek}; -}; - -Changeset.stringAssembler = function() { - var pieces = []; - function append(x) { - pieces.push(String(x)); - } - function toString() { - return pieces.join(''); - } - 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) { - // 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 - // (i.e. ins/del ordering, forbidden no-ops, non-mergeability, final newline). - // Can be used to mutate lists of strings where the last char of each string - // 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 inSplice = false; - // position in document after curSplice is applied: - 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) { - lines.splice.apply(lines, s); - } - function lines_toSource() { - return lines.toSource(); - } - function lines_get(idx) { - if (lines.get) { - return lines.get(idx); - } - else { - return lines[idx]; - } - } - // can be unimplemented if removeLines's return value not needed - function lines_slice(start, end) { - if (lines.slice) { - return lines.slice(start, end); - } - else { - return []; - } - } - function lines_length() { - if ((typeof lines.length) == "number") { - return lines.length; - } - else { - return lines.length(); - } - } - - function enterSplice() { - curSplice[0] = curLine; - curSplice[1] = 0; - if (curCol > 0) { - putCurLineInSplice(); - } - inSplice = true; - } - function leaveSplice() { - lines_applySplice(curSplice); - curSplice.length = 2; - curSplice[0] = curSplice[1] = 0; - inSplice = false; - } - function isCurLineInSplice() { - return (curLine - curSplice[0] < (curSplice.length - 2)); - } - function debugPrint(typ) { - print(typ+": "+curSplice.toSource()+" / "+curLine+","+curCol+" / "+lines_toSource()); - } - 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 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)) { - print("BLAH"); - putCurLineInSplice(); - }*/ // 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); - } - else { - if (includeInSplice && ! inSplice) { - enterSplice(); - } - if (inSplice) { - putCurLineInSplice(); - } - curCol += N; - //debugPrint("skip"); - } - } - } - - 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; - } - //debugPrint("remove"); - } - return removed; - } - - function remove(N, L) { - var removed = ''; - 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"); - } - } - 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; - } - //debugPrint("insert"); - } - } - - function hasMore() { - //print(lines.length+" / "+inSplice+" / "+(curSplice.length - 2)+" / "+curSplice[1]); - var docLines = lines_length(); - if (inSplice) { - docLines += curSplice.length - 2 - curSplice[1]; - } - return curLine < docLines; - } - - function close() { - if (inSplice) { - leaveSplice(); - } - //debugPrint("close"); - } - - 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) { - 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); - func(op1, op2, opOut); - if (opOut.opcode) { - //print(opOut.toSource()); - assem.append(opOut); - opOut.opcode = ''; - } - } - assem.endDocument(); - return assem.toString(); -}; - -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); - } - var oldLen = Changeset.parseNum(headerMatch[1]); - var changeSign = (headerMatch[2] == '>') ? 1 : -1; - var changeMag = Changeset.parseNum(headerMatch[3]); - 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)}; -}; - -Changeset.pack = function(oldLen, newLen, opsStr, bank) { - var lenDiff = newLen - oldLen; - 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) { - var unpacked = Changeset.unpack(cs); - 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()) { - 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; - } - } - assem.append(strIter.take(strIter.remaining())); - return assem.toString(); -}; - -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()) { - 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; - } - } - mut.close(); -}; - -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 - // is a deletion or a change. - // Examples, of the form (att1Items, att2Items, resultIsMutation) -> result - // ([], [(bold, )], true) -> [(bold, )] - // ([], [(bold, )], false) -> [] - // ([], [(bold, true)], true) -> [(bold, true)] - // ([], [(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) { - // 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; - var atts = []; - att1.replace(/\*([0-9a-z]+)/g, function(_, a) { - atts.push(pool.getAttrib(Changeset.parseNum(a))); - return ''; - }); - att2.replace(/\*([0-9a-z]+)/g, function(_, a) { - var pair = pool.getAttrib(Changeset.parseNum(a)); - var found = false; - for(var i=0;i"); - - var unpacked = Changeset.unpack(cs); - var csIter = Changeset.opIterator(unpacked.ops); - var csBank = unpacked.charBank; - var csBankIndex = 0; - // treat the attribution lines as text lines, mutating a line at a time - var mut = Changeset.textLinesMutator(lines); - - var lineIter = null; - function isNextMutOp() { - return (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()) { - lineIter.next(destOp); - } - else { - destOp.opcode = ''; - } - } - var lineAssem = null; - function outputMutOp(op) { - //print("outputMutOp: "+op.toSource()); - 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"); - // ship it to the mut - mut.insert(lineAssem.toString(), 1); - lineAssem = null; - } - } - - 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()) { - 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()))) { - break; // done - } - 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 { - Changeset.copyOp(csOp, opOut); - csOp.opcode = ''; - } - outputMutOp(opOut); - csBankIndex += opOut.chars; - opOut.opcode = ''; - } - else { - if ((! attOp.opcode) && isNextMutOp()) { - nextMutOp(attOp); - } - //print("attOp: "+attOp.toSource()); - Changeset._slicerZipperFunc(attOp, csOp, opOut, pool); - if (opOut.opcode) { - outputMutOp(opOut); - opOut.opcode = ''; - } - } - } - - Changeset.assert(! lineAssem, "line assembler not finished"); - mut.close(); - - //dmesg("-> "+lines.toSource()); -}; - -Changeset.joinAttributionLines = function(theAlines) { - var assem = Changeset.mergingOpAssembler(); - for(var i=0;i 0) { - lines.push(assem.toString()); - assem.clear(); - } - pos += op.chars; - } - - 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; - Changeset.assert(newlineEnd > 0, "newlineEnd <= 0 in splitAttributionLines"); - op.chars = newlineEnd - pos; - op.lines = 1; - appendOp(op); - numChars -= op.chars; - numLines -= op.lines; - } - if (numLines == 1) { - op.chars = numChars; - op.lines = 1; - } - appendOp(op); - } - - return lines; -}; - -Changeset.splitTextLines = function(text) { - return text.match(/[^\n]*(?:\n|[^\n]$)/g); -}; - -Changeset.compose = function(cs1, cs2, pool) { - var unpacked1 = Changeset.unpack(cs1); - var unpacked2 = Changeset.unpack(cs2); - var len1 = unpacked1.oldLen; - var len2 = unpacked1.newLen; - Changeset.assert(len2 == unpacked2.oldLen, "mismatched composition"); - var len3 = unpacked2.newLen; - var bankIter1 = Changeset.stringIterator(unpacked1.charBank); - 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 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 == '-') { - 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)); - } - else { - bankAssem.append(bankIter1.take(opOut.chars)); - } - } - - //debugBuilder.append(Changeset.opString(op1)); - //debugBuilder.append(','); - //debugBuilder.append(Changeset.opString(op2)); - //debugBuilder.append(' -> '); - //debugBuilder.append(Changeset.opString(opOut)); - //print(debugBuilder.toString()); - }); - - return Changeset.pack(len1, len3, newOps, bankAssem.toString()); -}; - -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) { - return never; - } - var attribNum = pool.putAttrib(attribPair, true); - if (attribNum < 0) { - return never; - } - else { - var re = new RegExp('\\*'+Changeset.numToString(attribNum)+ - '(?!\\w)'); - return function(attribs) { - return re.test(attribs); - }; - } - function never(attribs) { return false; } -}; - -Changeset.identity = function(N) { - return Changeset.pack(N, N, "", ""); -}; - -Changeset.makeSplice = function(oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool) { - var oldLen = oldFullText.length; - - if (spliceStart >= oldLen) { - spliceStart = oldLen - 1; - } - if (numRemoved > oldFullText.length - spliceStart - 1) { - numRemoved = oldFullText.length - spliceStart - 1; - } - var oldText = oldFullText.substring(spliceStart, spliceStart+numRemoved); - var newLen = oldLen + newText.length - oldText.length; - - var assem = Changeset.smartOpAssembler(); - assem.appendOpWithText('=', oldFullText.substring(0, spliceStart)); - assem.appendOpWithText('-', oldText); - assem.appendOpWithText('+', newText, optNewTextAPairs, pool); - assem.endDocument(); - return Changeset.pack(oldLen, newLen, assem.toString(), newText); -}; - -Changeset.toSplices = function(cs) { - // get a list of splices, [startChar, endChar, newText] - - var unpacked = Changeset.unpack(cs); - var splices = []; - - var oldPos = 0; - var iter = Changeset.opIterator(unpacked.ops); - var charIter = Changeset.stringIterator(unpacked.charBank); - var inSplice = false; - while (iter.hasNext()) { - var op = iter.next(); - if (op.opcode == '=') { - oldPos += op.chars; - inSplice = false; - } - else { - if (! inSplice) { - splices.push([oldPos, oldPos, ""]); - inSplice = true; - } - 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); - } - } - } - - return splices; -}; - -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= newEndChar) { - // splice fully replaces/deletes range - // (also case that handles insertion at a collapsed selection) - if (insertionsAfter) { - newStartChar = newEndChar = spliceStart; - } - else { - newStartChar = newEndChar = spliceStart + newTextLength; - } - } - else if (spliceEnd <= newStartChar) { - // splice is before range - newStartChar += thisLengthChange; - newEndChar += thisLengthChange; - } - else if (spliceStart >= newEndChar) { - // splice is after range - } - else if (spliceStart >= newStartChar && spliceEnd <= newEndChar) { - // splice is inside range - newEndChar += thisLengthChange; - } - else if (spliceEnd < newEndChar) { - // splice overlaps beginning of range - newStartChar = spliceStart + newTextLength; - newEndChar += thisLengthChange; - } - else { - // splice overlaps end of range - newEndChar = spliceStart; - } - - lengthChangeSoFar += thisLengthChange; - } - - return [newStartChar, newEndChar]; -}; - -Changeset.moveOpsToNewPool = function(cs, oldPool, newPool) { - // works on changeset or attribution string - var dollarPos = cs.indexOf('$'); - 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) { - var oldNum = Changeset.parseNum(a); - var pair = oldPool.getAttrib(oldNum); - var newNum = newPool.putAttrib(pair); - return '*'+Changeset.numToString(newNum); - }) + fromDollar; -}; - -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) { - var dollarPos = cs.indexOf('$'); - if (dollarPos < 0) { - dollarPos = cs.length; - } - var upToDollar = cs.substring(0, dollarPos); - - upToDollar.replace(/\*([0-9a-z]+)/g, function(_, a) { - func(Changeset.parseNum(a)); - return ''; - }); -}; - -// 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) { - return Changeset.mapAttribNumbers(cs, filter); -}; - -Changeset.mapAttribNumbers = function(cs, func) { - var dollarPos = cs.indexOf('$'); - if (dollarPos < 0) { - dollarPos = cs.length; - } - var upToDollar = cs.substring(0, dollarPos); - - var newUpToDollar = upToDollar.replace(/\*([0-9a-z]+)/g, function(s, a) { - var n = func(Changeset.parseNum(a)); - if (n === true) { - return s; - } - else if ((typeof n) === "number") { - return '*'+Changeset.numToString(n); - } - else { - return ''; - } - }); - - return newUpToDollar + cs.substring(dollarPos); -}; - -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.cloneAText = function(atext) { - return { text: atext.text, attribs: atext.attribs }; -}; - -Changeset.copyAText = function(atext1, atext2) { - atext2.text = atext1.text; - atext2.attribs = atext1.attribs; -}; - -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()) { - iter.next(op); - 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 { - assem.append(op); - } - } -}; - -Changeset.prepareForWire = function(cs, pool) { - var newPool = new AttribPool(); - var newCs = Changeset.moveOpsToNewPool(cs, pool, newPool); - return {translated: newCs, pool: newPool}; -}; - -Changeset.isIdentity = function(cs) { - var unpacked = Changeset.unpack(cs); - return unpacked.ops == "" && unpacked.oldLen == unpacked.newLen; -}; - -Changeset.opAttributeValue = function(op, key, pool) { - return Changeset.attribsAttributeValue(op.attribs, key, pool); -}; - -Changeset.attribsAttributeValue = function(attribs, key, pool) { - var value = ''; - if (attribs) { - Changeset.eachAttribNumber(attribs, function(n) { - if (pool.getAttribKey(n) == key) { - value = pool.getAttribValue(n); - } - }); - } - return value; -}; - -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) { - o.opcode = '='; - o.attribs = (attribs && - Changeset.makeAttribsString('=', attribs, pool)) || ''; - o.chars = N; - o.lines = (L || 0); - assem.append(o); - return self; - }, - keepText: function(text, attribs, pool) { - assem.appendOpWithText('=', text, attribs, pool); - return self; - }, - insert: function(text, attribs, pool) { - assem.appendOpWithText('+', text, attribs, pool); - charBank.append(text); - return self; - }, - remove: function(N, L) { - o.opcode = '-'; - o.attribs = ''; - o.chars = N; - o.lines = (L || 0); - assem.append(o); - return self; - }, - toString: function() { - assem.endDocument(); - var newLen = oldLen + assem.getLengthChange(); - return Changeset.pack(oldLen, newLen, assem.toString(), - charBank.toString()); - } - }; - - return self; -}; - -Changeset.makeAttribsString = function(opcode, attribs, pool) { - // makeAttribsString(opcode, '*3') or makeAttribsString(opcode, [['foo','bar']], myPool) work - if (! attribs) { - return ''; - } - else if ((typeof attribs) == "string") { - return attribs; - } - else if (pool && attribs && attribs.length) { - if (attribs.length > 1) { - attribs = attribs.slice(); - attribs.sort(); - } - var result = []; - for(var i=0;i= attOp.chars && - attOp.lines > 0 && csOp.lines <= 0) { - csOp.lines++; - } - - Changeset._slicerZipperFunc(attOp, csOp, opOut, null); - if (opOut.opcode) { - assem.append(opOut); - opOut.opcode = ''; - } - } - } - } - - csOp.opcode = '-'; - csOp.chars = start; - - doCsOp(); - - if (optEnd === undefined) { - if (attOp.opcode) { - assem.append(attOp); - } - while (iter.hasNext()) { - iter.next(attOp); - assem.append(attOp); - } - } - else { - csOp.opcode = '='; - csOp.chars = optEnd - start; - doCsOp(); - } - - return assem.toString(); -}; - -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) { - return lines.get(idx); - } - else { - return lines[idx]; - } - } - function lines_length() { - if ((typeof lines.length) == "number") { - return lines.length; - } - else { - return lines.length(); - } - } - function alines_get(idx) { - if (alines.get) { - return alines.get(idx); - } - else { - return alines[idx]; - } - } - function alines_length() { - if ((typeof alines.length) == "number") { - return alines.length; - } - else { - return alines.length(); - } - } - - var curLine = 0; - var curChar = 0; - var curLineOpIter = null; - var curLineOpIterLine; - var curLineNextOp = Changeset.newOp('+'); - - var unpacked = Changeset.unpack(cs); - var csIter = Changeset.opIterator(unpacked.ops); - var builder = Changeset.builder(unpacked.newLen); - - function consumeAttribRuns(numChars, func/*(len, attribs, endsLine)*/) { - - 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 (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); - } - var charsToUse = Math.min(numChars, curLineNextOp.chars); - func(charsToUse, curLineNextOp.attribs, - charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0); - numChars -= charsToUse; - curLineNextOp.chars -= charsToUse; - curChar += charsToUse; - } - - if ((! curLineNextOp.chars) && (! curLineOpIter.hasNext())) { - curLine++; - curChar = 0; - } - } - - function skip(N, L) { - if (L) { - curLine += L; - curChar = 0; - } - else { - if (curLineOpIter && curLineOpIterLine == curLine) { - consumeAttribRuns(N, function() {}); - } - else { - curChar += N; - } - } - } - - 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 nextString = lines_get(lineNum); - len += nextString.length; - assem.append(nextString); - lineNum++; - } - - return assem.toString().substring(0, numChars); - } - - function cachedStrFunc(func) { - var cache = {}; - return function(s) { - if (! cache[s]) { - cache[s] = func(s); - } - return cache[s]; - }; - } - - var attribKeys = []; - var attribValues = []; - 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 throughIterator"); - var x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; - assert("throughIterator("+literal(x)+") == "+literal(x)); - })(); - - (function() { - print("> throughSmartAssembler"); - var x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; - assert("throughSmartAssembler("+literal(x)+") == "+literal(x)); - })(); - - function applyMutations(mu, arrayOfArrays) { - arrayOfArrays.forEach(function (a) { - var result = mu[a[0]].apply(mu, a.slice(1)); - if (a[0] == 'remove' && a[3]) { - assertEqualStrings(a[3], result); - } - }); - } - - function mutationsToChangeset(oldLen, arrayOfArrays) { - var assem = Changeset.smartOpAssembler(); - var op = Changeset.newOp(); - var bank = Changeset.stringAssembler(); - var oldPos = 0; - var newLen = 0; - arrayOfArrays.forEach(function (a) { - if (a[0] == 'skip') { - op.opcode = '='; - op.chars = a[1]; - op.lines = (a[2] || 0); - assem.append(op); - oldPos += op.chars; - newLen += op.chars; - } - else if (a[0] == 'remove') { - op.opcode = '-'; - op.chars = a[1]; - op.lines = (a[2] || 0); - assem.append(op); - oldPos += op.chars; - } - else if (a[0] == 'insert') { - op.opcode = '+'; - bank.append(a[1]); - op.chars = a[1].length; - op.lines = (a[2] || 0); - assem.append(op); - newLen += op.chars; - } - }); - newLen += oldLen - oldPos; - assem.endDocument(); - return Changeset.pack(oldLen, newLen, assem.toString(), - bank.toString()); - } - - function runMutationTest(testId, origLines, muts, correct) { - print("> runMutationTest#"+testId); - var lines = origLines.slice(); - var mu = Changeset.textLinesMutator(lines); - applyMutations(mu, muts); - mu.close(); - assertEqualArrays(correct, lines); - - var inText = origLines.join(''); - var cs = mutationsToChangeset(inText.length, muts); - lines = origLines.slice(); - Changeset.mutateTextLines(cs, lines); - assertEqualArrays(correct, lines); - - var correctText = correct.join(''); - //print(literal(cs)); - var outText = Changeset.applyToText(cs, inText); - assertEqualStrings(correctText, outText); - } - - runMutationTest(1, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], - [['remove',1,0,"a"],['insert',"tu"],['remove',1,0,"p"],['skip',4,1],['skip',7,1], - ['insert',"cream\npie\n",2],['skip',2],['insert',"bot"],['insert',"\n",1], - ['insert',"bu"],['skip',3],['remove',3,1,"ge\n"],['remove',6,0,"duffle"]], - ["tuple\n","banana\n","cream\n","pie\n", "cabot\n","bubba\n","eggplant\n"]); - - runMutationTest(2, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], - [['remove',1,0,"a"],['remove',1,0,"p"],['insert',"tu"],['skip',11,2], - ['insert',"cream\npie\n",2],['skip',2],['insert',"bot"],['insert',"\n",1], - ['insert',"bu"],['skip',3],['remove',3,1,"ge\n"],['remove',6,0,"duffle"]], - ["tuple\n","banana\n","cream\n","pie\n", "cabot\n","bubba\n","eggplant\n"]); - - runMutationTest(3, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], - [['remove',6,1,"apple\n"],['skip',15,2],['skip',6],['remove',1,1,"\n"], - ['remove',8,0,"eggplant"],['skip',1,1]], - ["banana\n","cabbage\n","duffle\n"]); - - runMutationTest(4, ["15\n"], - [['skip',1],['insert',"\n2\n3\n4\n",4],['skip',2,1]], - ["1\n","2\n","3\n","4\n","5\n"]); - - runMutationTest(5, ["1\n","2\n","3\n","4\n","5\n"], - [['skip',1],['remove',7,4,"\n2\n3\n4\n"],['skip',2,1]], - ["15\n"]); - - runMutationTest(6, ["123\n","abc\n","def\n","ghi\n","xyz\n"], - [['insert',"0"],['skip',4,1],['skip',4,1],['remove',8,2,"def\nghi\n"],['skip',4,1]], - ["0123\n", "abc\n", "xyz\n"]); - - runMutationTest(7, ["apple\n", "banana\n", "cabbage\n", "duffle\n", "eggplant\n"], - [['remove',6,1,"apple\n"],['skip',15,2,true],['skip',6,0,true],['remove',1,1,"\n"], - ['remove',8,0,"eggplant"],['skip',1,1,true]], - ["banana\n","cabbage\n","duffle\n"]); - - function poolOrArray(attribs) { - if (attribs.getAttrib) { - return attribs; // it's already an attrib pool - } - else { - // assume it's an array of attrib strings to be split and added - var p = new AttribPool(); - attribs.forEach(function (kv) { p.putAttrib(kv.split(',')); }); - return p; - } - } - - function runApplyToAttributionTest(testId, attribs, cs, inAttr, outCorrect) { - print("> applyToAttribution#"+testId); - var p = poolOrArray(attribs); - var result = Changeset.applyToAttribution( - Changeset.checkRep(cs), inAttr, p); - assertEqualStrings(outCorrect, result); - } - - // turn cactus\n into actusabcd\n - runApplyToAttributionTest(1, ['bold,', 'bold,true'], - "Z:7>3-1*0=1*1=1=3+4$abcd", - "+1*1+1|1+5", "+1*1+1|1+8"); - - // turn "david\ngreenspan\n" into "david\ngreen\n" - runApplyToAttributionTest(2, ['bold,', 'bold,true'], - "Z:g<4*1|1=6*1=5-4$", - "|2+g", "*1|1+6*1+5|1+1"); - - (function() { - print("> mutatorHasMore"); - var lines = ["1\n", "2\n", "3\n", "4\n"]; - var mu; - - mu = Changeset.textLinesMutator(lines); - assert(mu.hasMore()+' == true'); - mu.skip(8,4); - assert(mu.hasMore()+' == false'); - mu.close(); - assert(mu.hasMore()+' == false'); - - // still 1,2,3,4 - mu = Changeset.textLinesMutator(lines); - assert(mu.hasMore()+' == true'); - mu.remove(2,1); - assert(mu.hasMore()+' == true'); - mu.skip(2,1); - assert(mu.hasMore()+' == true'); - mu.skip(2,1); - assert(mu.hasMore()+' == true'); - mu.skip(2,1); - assert(mu.hasMore()+' == false'); - mu.insert("5\n", 1); - assert(mu.hasMore()+' == false'); - mu.close(); - assert(mu.hasMore()+' == false'); - - // 2,3,4,5 now - mu = Changeset.textLinesMutator(lines); - assert(mu.hasMore()+' == true'); - mu.remove(6,3); - assert(mu.hasMore()+' == true'); - mu.remove(2,1); - assert(mu.hasMore()+' == false'); - mu.insert("hello\n", 1); - assert(mu.hasMore()+' == false'); - mu.close(); - assert(mu.hasMore()+' == false'); - - })(); - - function runMutateAttributionTest(testId, attribs, cs, alines, outCorrect) { - print("> runMutateAttributionTest#"+testId); - var p = poolOrArray(attribs); - var alines2 = Array.prototype.slice.call(alines); - var result = Changeset.mutateAttributionLines( - Changeset.checkRep(cs), alines2, p); - assertEqualArrays(outCorrect, alines2); - - print("> runMutateAttributionTest#"+testId+".applyToAttribution"); - function removeQuestionMarks(a) { return a.replace(/\?/g, ''); } - var inMerged = Changeset.joinAttributionLines(alines.map(removeQuestionMarks)); - var correctMerged = Changeset.joinAttributionLines(outCorrect.map(removeQuestionMarks)); - var mergedResult = Changeset.applyToAttribution(cs, inMerged, p); - assertEqualStrings(correctMerged, mergedResult); - } - - // turn 123\n 456\n 789\n into 123\n 456\n 789\n - runMutateAttributionTest(1, ["bold,true"], "Z:c>0|1=4=1*0=1$", ["|1+4", "|1+4", "|1+4"], - ["|1+4", "+1*0+1|1+2", "|1+4"]); - - // make a document bold - runMutateAttributionTest(2, ["bold,true"], "Z:c>0*0|3=c$", ["|1+4", "|1+4", "|1+4"], - ["*0|1+4", "*0|1+4", "*0|1+4"]); - - // clear bold on document - runMutateAttributionTest(3, ["bold,","bold,true"], "Z:c>0*0|3=c$", - ["*1+1+1*1+1|1+1", "+1*1+1|1+2", "*1+1+1*1+1|1+1"], - ["|1+4", "|1+4", "|1+4"]); - - // add a character on line 3 of a document with 5 blank lines, and make sure - // the optimization that skips purely-kept lines is working; if any attribution string - // with a '?' is parsed it will cause an error. - runMutateAttributionTest(4, ['foo,bar','line,1','line,2','line,3','line,4','line,5'], - "Z:5>1|2=2+1$x", - ["?*1|1+1", "?*2|1+1", "*3|1+1", "?*4|1+1", "?*5|1+1"], - ["?*1|1+1", "?*2|1+1", "+1*3|1+1", "?*4|1+1", "?*5|1+1"]); - - var testPoolWithChars = (function() { - var p = new AttribPool(); - p.putAttrib(['char','newline']); - for(var i=1;i<36;i++) { - p.putAttrib(['char',Changeset.numToString(i)]); - } - p.putAttrib(['char','']); - return p; - })(); - - // based on runMutationTest#1 - runMutateAttributionTest(5, testPoolWithChars, - "Z:11>7-2*t+1*u+1|2=b|2+a=2*b+1*o+1*t+1*0|1+1*b+1*u+1=3|1-3-6$"+ - "tucream\npie\nbot\nbu", - ["*a+1*p+2*l+1*e+1*0|1+1", - "*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1", - "*c+1*a+1*b+2*a+1*g+1*e+1*0|1+1", - "*d+1*u+1*f+2*l+1*e+1*0|1+1", - "*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1"], - ["*t+1*u+1*p+1*l+1*e+1*0|1+1", - "*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1", - "|1+6", - "|1+4", - "*c+1*a+1*b+1*o+1*t+1*0|1+1", - "*b+1*u+1*b+2*a+1*0|1+1", - "*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1"]); - - // based on runMutationTest#3 - runMutateAttributionTest(6, testPoolWithChars, - "Z:117=1|4+7$\n2\n3\n4\n", - ["*1+1*5|1+2"], - ["*1+1|1+1","|1+2","|1+2","|1+2","*5|1+2"]); - - // based on runMutationTest#5 - runMutateAttributionTest(8, testPoolWithChars, - "Z:a<7=1|4-7$", - ["*1|1+2","*2|1+2","*3|1+2","*4|1+2","*5|1+2"], - ["*1+1*5|1+2"]); - - // based on runMutationTest#6 - runMutateAttributionTest(9, testPoolWithChars, - "Z:k<7*0+1*10|2=8|2-8$0", - ["*1+1*2+1*3+1|1+1","*a+1*b+1*c+1|1+1", - "*d+1*e+1*f+1|1+1","*g+1*h+1*i+1|1+1","?*x+1*y+1*z+1|1+1"], - ["*0+1|1+4", "|1+4", "?*x+1*y+1*z+1|1+1"]); - - runMutateAttributionTest(10, testPoolWithChars, - "Z:6>4=1+1=1+1|1=1+1=1*0+1$abcd", - ["|1+3", "|1+3"], - ["|1+5", "+2*0+1|1+2"]); - - - runMutateAttributionTest(11, testPoolWithChars, - "Z:s>1|1=4=6|1+1$\n", - ["*0|1+4", "*0|1+8", "*0+5|1+1", "*0|1+1", "*0|1+5", "*0|1+1", "*0|1+1", "*0|1+1", "|1+1"], - ["*0|1+4", "*0+6|1+1", "*0|1+2", "*0+5|1+1", "*0|1+1", "*0|1+5", "*0|1+1", "*0|1+1", "*0|1+1", "|1+1"]); - - function randomInlineString(len, rand) { - var assem = Changeset.stringAssembler(); - for(var i=0;i 1) doOp(); - for(var i=0;i<5;i++) doOp(); // do some more (only insertions will happen) - - var outText = outTextAssem.toString()+'\n'; - opAssem.endDocument(); - var cs = Changeset.pack(oldLen, outText.length, opAssem.toString(), charBank.toString()); - Changeset.checkRep(cs); - return [cs, outText]; - } - - function testCompose(randomSeed) { - var rand = new java.util.Random(randomSeed); - print("> testCompose#"+randomSeed); - - var p = new AttribPool(); - - var startText = randomMultiline(10, 20, rand)+'\n'; - - var x1 = randomTestChangeset(startText, rand); - var change1 = x1[0]; - var text1 = x1[1]; - - var x2 = randomTestChangeset(text1, rand); - var change2 = x2[0]; - var text2 = x2[1]; - - var x3 = randomTestChangeset(text2, rand); - var change3 = x3[0]; - var text3 = x3[1]; - - //print(literal(Changeset.toBaseTen(startText))); - //print(literal(Changeset.toBaseTen(change1))); - //print(literal(Changeset.toBaseTen(change2))); - var change12 = Changeset.checkRep(Changeset.compose(change1, change2, p)); - var change23 = Changeset.checkRep(Changeset.compose(change2, change3, p)); - var change123 = Changeset.checkRep(Changeset.compose(change12, change3, p)); - var change123a = Changeset.checkRep(Changeset.compose(change1, change23, p)); - assertEqualStrings(change123, change123a); - - assertEqualStrings(text2, Changeset.applyToText(change12, startText)); - assertEqualStrings(text3, Changeset.applyToText(change23, text1)); - assertEqualStrings(text3, Changeset.applyToText(change123, startText)); - } - - for(var i=0;i<30;i++) testCompose(i); - - (function simpleComposeAttributesTest() { - print("> simpleComposeAttributesTest"); - var p = new AttribPool(); - p.putAttrib(['bold','']); - p.putAttrib(['bold','true']); - var cs1 = Changeset.checkRep("Z:2>1*1+1*1=1$x"); - var cs2 = Changeset.checkRep("Z:3>0*0|1=3$"); - var cs12 = Changeset.checkRep(Changeset.compose(cs1, cs2, p)); - assertEqualStrings("Z:2>1+1*0|1=2$x", cs12); - })(); - - (function followAttributesTest() { - var p = new AttribPool(); - p.putAttrib(['x','']); - p.putAttrib(['x','abc']); - p.putAttrib(['x','def']); - p.putAttrib(['y','']); - p.putAttrib(['y','abc']); - p.putAttrib(['y','def']); - - function testFollow(a, b, afb, bfa, merge) { - assertEqualStrings(afb, Changeset.followAttributes(a, b, p)); - assertEqualStrings(bfa, Changeset.followAttributes(b, a, p)); - assertEqualStrings(merge, Changeset.composeAttributes(a, afb, true, p)); - assertEqualStrings(merge, Changeset.composeAttributes(b, bfa, true, p)); - } - - testFollow('', '', '', '', ''); - testFollow('*0', '', '', '*0', '*0'); - testFollow('*0', '*0', '', '', '*0'); - testFollow('*0', '*1', '', '*0', '*0'); - testFollow('*1', '*2', '', '*1', '*1'); - testFollow('*0*1', '', '', '*0*1', '*0*1'); - testFollow('*0*4', '*2*3', '*3', '*0', '*0*3'); - testFollow('*0*4', '*2', '', '*0*4', '*0*4'); - })(); - - function testFollow(randomSeed) { - var rand = new java.util.Random(randomSeed + 1000); - print("> testFollow#"+randomSeed); - - var p = new AttribPool(); - - var startText = randomMultiline(10, 20, rand)+'\n'; - - var cs1 = randomTestChangeset(startText, rand)[0]; - var cs2 = randomTestChangeset(startText, rand)[0]; - - var afb = Changeset.checkRep(Changeset.follow(cs1, cs2, false, p)); - var bfa = Changeset.checkRep(Changeset.follow(cs2, cs1, true, p)); - - var merge1 = Changeset.checkRep(Changeset.compose(cs1, afb)); - var merge2 = Changeset.checkRep(Changeset.compose(cs2, bfa)); - - assertEqualStrings(merge1, merge2); - } - - for(var i=0;i<30;i++) testFollow(i); - - function testSplitJoinAttributionLines(randomSeed) { - var rand = new java.util.Random(randomSeed + 2000); - print("> testSplitJoinAttributionLines#"+randomSeed); - - var doc = randomMultiline(10, 20, rand)+'\n'; - - function stringToOps(str) { - var assem = Changeset.mergingOpAssembler(); - var o = Changeset.newOp('+'); - o.chars = 1; - for(var i=0;i testMoveOpsToNewPool"); - - var pool1 = new AttribPool(); - var pool2 = new AttribPool(); - - pool1.putAttrib(['baz','qux']); - pool1.putAttrib(['foo','bar']); - - pool2.putAttrib(['foo','bar']); - - assertEqualStrings(Changeset.moveOpsToNewPool('Z:1>2*1+1*0+1$ab', pool1, pool2), 'Z:1>2*0+1*1+1$ab'); - assertEqualStrings(Changeset.moveOpsToNewPool('*1+1*0+1', pool1, pool2), '*0+1*1+1'); - })(); - - - (function testMakeSplice() { - print("> testMakeSplice"); - - var t = "a\nb\nc\n"; - var t2 = Changeset.applyToText(Changeset.makeSplice(t, 5, 0, "def"), t); - assertEqualStrings("a\nb\ncdef\n", t2); - - })(); - - (function testToSplices() { - print("> testToSplices"); - - var cs = Changeset.checkRep('Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk'); - var correctSplices = [[5, 8, "123456789"], [9, 17, "abcdefghijk"]]; - assertEqualArrays(correctSplices, Changeset.toSplices(cs)); - })(); - - function testCharacterRangeFollow(testId, cs, oldRange, insertionsAfter, correctNewRange) { - print("> testCharacterRangeFollow#"+testId); - - var cs = Changeset.checkRep(cs); - assertEqualArrays(correctNewRange, Changeset.characterRangeFollow(cs, oldRange[0], oldRange[1], - insertionsAfter)); - - } - - testCharacterRangeFollow(1, 'Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk', - [7, 10], false, [14, 15]); - testCharacterRangeFollow(2, "Z:bc<6|x=b4|2-6$", [400, 407], false, [400, 401]); - testCharacterRangeFollow(3, "Z:4>0-3+3$abc", [0,3], false, [3,3]); - testCharacterRangeFollow(4, "Z:4>0-3+3$abc", [0,3], true, [0,0]); - testCharacterRangeFollow(5, "Z:5>1+1=1-3+3$abcd", [1,4], false, [5,5]); - testCharacterRangeFollow(6, "Z:5>1+1=1-3+3$abcd", [1,4], true, [2,2]); - testCharacterRangeFollow(7, "Z:5>1+1=1-3+3$abcd", [0,6], false, [1,7]); - testCharacterRangeFollow(8, "Z:5>1+1=1-3+3$abcd", [0,3], false, [1,2]); - testCharacterRangeFollow(9, "Z:5>1+1=1-3+3$abcd", [2,5], false, [5,6]); - testCharacterRangeFollow(10, "Z:2>1+1$a", [0,0], false, [1,1]); - testCharacterRangeFollow(11, "Z:2>1+1$a", [0,0], true, [0,0]); - - (function testOpAttributeValue() { - print("> testOpAttributeValue"); - - var p = new AttribPool(); - p.putAttrib(['name','david']); - p.putAttrib(['color','green']); - - assertEqualStrings("david", Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'name', p)); - assertEqualStrings("david", Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'name', p)); - assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'name', p)); - assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('+1'), 'name', p)); - assertEqualStrings("green", Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'color', p)); - assertEqualStrings("green", Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'color', p)); - assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'color', p)); - assertEqualStrings("", Changeset.opAttributeValue(Changeset.stringOp('+1'), 'color', p)); - })(); - - function testAppendATextToAssembler(testId, atext, correctOps) { - print("> testAppendATextToAssembler#"+testId); - - var assem = Changeset.smartOpAssembler(); - Changeset.appendATextToAssembler(atext, assem); - assertEqualStrings(correctOps, assem.toString()); - } - - testAppendATextToAssembler(1, {text:"\n", attribs:"|1+1"}, ""); - testAppendATextToAssembler(2, {text:"\n\n", attribs:"|2+2"}, "|1+1"); - testAppendATextToAssembler(3, {text:"\n\n", attribs:"*x|2+2"}, "*x|1+1"); - testAppendATextToAssembler(4, {text:"\n\n", attribs:"*x|1+1|1+1"}, "*x|1+1"); - testAppendATextToAssembler(5, {text:"foo\n", attribs:"|1+4"}, "+3"); - testAppendATextToAssembler(6, {text:"\nfoo\n", attribs:"|2+5"}, "|1+1+3"); - testAppendATextToAssembler(7, {text:"\nfoo\n", attribs:"*x|2+5"}, "*x|1+1*x+3"); - testAppendATextToAssembler(8, {text:"\n\n\nfoo\n", attribs:"|2+2*x|2+5"}, "|2+2*x|1+1*x+3"); - - function testMakeAttribsString(testId, pool, opcode, attribs, correctString) { - print("> testMakeAttribsString#"+testId); - - var p = poolOrArray(pool); - var str = Changeset.makeAttribsString(opcode, attribs, p); - assertEqualStrings(correctString, str); - } - - testMakeAttribsString(1, ['bold,'], '+', [['bold','']], ''); - testMakeAttribsString(2, ['abc,def','bold,'], '=', [['bold','']], '*1'); - testMakeAttribsString(3, ['abc,def','bold,true'], '+', [['abc','def'],['bold','true']], '*0*1'); - testMakeAttribsString(4, ['abc,def','bold,true'], '+', [['bold','true'],['abc','def']], '*0*1'); - - function testSubattribution(testId, astr, start, end, correctOutput) { - print("> testSubattribution#"+testId); - - var str = Changeset.subattribution(astr, start, end); - assertEqualStrings(correctOutput, str); - } - - testSubattribution(1, "+1", 0, 0, ""); - testSubattribution(2, "+1", 0, 1, "+1"); - testSubattribution(3, "+1", 0, undefined, "+1"); - testSubattribution(4, "|1+1", 0, 0, ""); - testSubattribution(5, "|1+1", 0, 1, "|1+1"); - testSubattribution(6, "|1+1", 0, undefined, "|1+1"); - testSubattribution(7, "*0+1", 0, 0, ""); - testSubattribution(8, "*0+1", 0, 1, "*0+1"); - testSubattribution(9, "*0+1", 0, undefined, "*0+1"); - testSubattribution(10, "*0|1+1", 0, 0, ""); - testSubattribution(11, "*0|1+1", 0, 1, "*0|1+1"); - testSubattribution(12, "*0|1+1", 0, undefined, "*0|1+1"); - testSubattribution(13, "*0+2+1*1+3", 0, 1, "*0+1"); - testSubattribution(14, "*0+2+1*1+3", 0, 2, "*0+2"); - testSubattribution(15, "*0+2+1*1+3", 0, 3, "*0+2+1"); - testSubattribution(16, "*0+2+1*1+3", 0, 4, "*0+2+1*1+1"); - testSubattribution(17, "*0+2+1*1+3", 0, 5, "*0+2+1*1+2"); - testSubattribution(18, "*0+2+1*1+3", 0, 6, "*0+2+1*1+3"); - testSubattribution(19, "*0+2+1*1+3", 0, 7, "*0+2+1*1+3"); - testSubattribution(20, "*0+2+1*1+3", 0, undefined, "*0+2+1*1+3"); - testSubattribution(21, "*0+2+1*1+3", 1, undefined, "*0+1+1*1+3"); - testSubattribution(22, "*0+2+1*1+3", 2, undefined, "+1*1+3"); - testSubattribution(23, "*0+2+1*1+3", 3, undefined, "*1+3"); - testSubattribution(24, "*0+2+1*1+3", 4, undefined, "*1+2"); - testSubattribution(25, "*0+2+1*1+3", 5, undefined, "*1+1"); - testSubattribution(26, "*0+2+1*1+3", 6, undefined, ""); - testSubattribution(27, "*0+2+1*1|1+3", 0, 1, "*0+1"); - testSubattribution(28, "*0+2+1*1|1+3", 0, 2, "*0+2"); - testSubattribution(29, "*0+2+1*1|1+3", 0, 3, "*0+2+1"); - testSubattribution(30, "*0+2+1*1|1+3", 0, 4, "*0+2+1*1+1"); - testSubattribution(31, "*0+2+1*1|1+3", 0, 5, "*0+2+1*1+2"); - testSubattribution(32, "*0+2+1*1|1+3", 0, 6, "*0+2+1*1|1+3"); - testSubattribution(33, "*0+2+1*1|1+3", 0, 7, "*0+2+1*1|1+3"); - testSubattribution(34, "*0+2+1*1|1+3", 0, undefined, "*0+2+1*1|1+3"); - testSubattribution(35, "*0+2+1*1|1+3", 1, undefined, "*0+1+1*1|1+3"); - testSubattribution(36, "*0+2+1*1|1+3", 2, undefined, "+1*1|1+3"); - testSubattribution(37, "*0+2+1*1|1+3", 3, undefined, "*1|1+3"); - testSubattribution(38, "*0+2+1*1|1+3", 4, undefined, "*1|1+2"); - testSubattribution(39, "*0+2+1*1|1+3", 5, undefined, "*1|1+1"); - testSubattribution(40, "*0+2+1*1|1+3", 1, 5, "*0+1+1*1+2"); - testSubattribution(41, "*0+2+1*1|1+3", 2, 6, "+1*1|1+3"); - testSubattribution(42, "*0+2+1*1+3", 2, 6, "+1*1+3"); - - function testFilterAttribNumbers(testId, cs, filter, correctOutput) { - print("> testFilterAttribNumbers#"+testId); - - var str = Changeset.filterAttribNumbers(cs, filter); - assertEqualStrings(correctOutput, str); - } - - testFilterAttribNumbers(1, "*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6", - function(n) { return (n%2) == 0; }, - "*0+1+2+3+4*2+5*0*2*c+6"); - testFilterAttribNumbers(2, "*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6", - function(n) { return (n%2) == 1; }, - "*1+1+2+3*1+4+5*1*b+6"); - - function testInverse(testId, cs, lines, alines, pool, correctOutput) { - print("> testInverse#"+testId); - - pool = poolOrArray(pool); - var str = Changeset.inverse(Changeset.checkRep(cs), lines, alines, pool); - assertEqualStrings(correctOutput, str); - } - - // take "FFFFTTTTT" and apply "-FT--FFTT", the inverse of which is "--F--TT--" - testInverse(1, "Z:9>0=1*0=1*1=1=2*0=2*1|1=2$", null, ["+4*1+5"], ['bold,','bold,true'], - "Z:9>0=2*0=1=2*1=2$"); - - function testMutateTextLines(testId, cs, lines, correctLines) { - print("> testMutateTextLines#"+testId); - - var a = lines.slice(); - Changeset.mutateTextLines(cs, a); - assertEqualArrays(correctLines, a); - } - - testMutateTextLines(1, "Z:4<1|1-2-1|1+1+1$\nc", ["a\n", "b\n"], ["\n", "c\n"]); - testMutateTextLines(2, "Z:4>0|1-2-1|2+3$\nc\n", ["a\n", "b\n"], ["\n", "c\n", "\n"]); - - function testInverseRandom(randomSeed) { - var rand = new java.util.Random(randomSeed + 3000); - print("> testInverseRandom#"+randomSeed); - - var p = poolOrArray(['apple,','apple,true','banana,','banana,true']); - - var startText = randomMultiline(10, 20, rand)+'\n'; - var alines = Changeset.splitAttributionLines(Changeset.makeAttribution(startText), startText); - var lines = startText.slice(0,-1).split('\n').map(function(s) { return s+'\n'; }); - - var stylifier = randomTestChangeset(startText, rand, true)[0]; - - //print(alines.join('\n')); - Changeset.mutateAttributionLines(stylifier, alines, p); - //print(stylifier); - //print(alines.join('\n')); - Changeset.mutateTextLines(stylifier, lines); - - var changeset = randomTestChangeset(lines.join(''), rand, true)[0]; - var inverseChangeset = Changeset.inverse(changeset, lines, alines, p); - - var origLines = lines.slice(); - var origALines = alines.slice(); - - Changeset.mutateTextLines(changeset, lines); - Changeset.mutateAttributionLines(changeset, alines, p); - //print(origALines.join('\n')); - //print(changeset); - //print(inverseChangeset); - //print(origLines.map(function(s) { return '1: '+s.slice(0,-1); }).join('\n')); - //print(lines.map(function(s) { return '2: '+s.slice(0,-1); }).join('\n')); - //print(alines.join('\n')); - Changeset.mutateTextLines(inverseChangeset, lines); - Changeset.mutateAttributionLines(inverseChangeset, alines, p); - //print(lines.map(function(s) { return '3: '+s.slice(0,-1); }).join('\n')); - - assertEqualArrays(origLines, lines); - assertEqualArrays(origALines, alines); - } - - for(var i=0;i<30;i++) testInverseRandom(i); -} \ No newline at end of file diff --git a/trunk/infrastructure/ace/www/editor.css b/trunk/infrastructure/ace/www/editor.css deleted file mode 100644 index 0a43478..0000000 --- a/trunk/infrastructure/ace/www/editor.css +++ /dev/null @@ -1,109 +0,0 @@ - -/* These CSS rules are included in both the outer and inner ACE iframe. - Also see inner.css, included only in the inner one. -*/ - -body { - margin: 0; - white-space: nowrap; -} - -#outerdocbody { - background-color: #fff; -} -body.grayedout { background-color: #eee !important } - -#innerdocbody { - font-size: 12px; /* overridden by body.style */ - font-family: monospace; /* overridden by body.style */ - line-height: 16px; /* overridden by body.style */ -} - -body.doesWrap { - white-space: normal; -} - -#innerdocbody { - padding-top: 1px; /* important for some reason? */ - padding-right: 10px; - padding-bottom: 8px; - padding-left: 1px /* prevents characters from looking chopped off in FF3 */; - overflow: hidden; - /* blank 1x1 gif, so that IE8 doesn't consider the body transparent */ - background-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==); -} - -#sidediv { - font-size: 11px; - font-family: monospace; - line-height: 16px; /* overridden by sideDiv.style */ - padding-top: 8px; /* EDIT_BODY_PADDING_TOP */ - padding-right: 3px; /* LINE_NUMBER_PADDING_RIGHT - 1 */ - position: absolute; - width: 20px; /* MIN_LINEDIV_WIDTH */ - top: 0; - left: 0; - cursor: default; - color: white; -} - -#sidedivinner { - text-align: right; -} - -.sidedivdelayed { /* class set after sizes are set */ - background-color: #eee; - color: #888 !important; - border-right: 1px solid #999; -} -.sidedivhidden { - display: none; -} - -#outerdocbody iframe { - display: block; /* codemirror says it suppresses bugs */ - position: relative; - left: 32px; /* MIN_LINEDIV_WIDTH + LINE_NUMBER_PADDING_RIGHT + EDIT_BODY_PADDING_LEFT */ - top: 7px; /* EDIT_BODY_PADDING_TOP - 1*/ - border: 0; - width: 1px; /* changed programmatically */ - height: 1px; /* changed programmatically */ -} - -#outerdocbody .hotrect { - border: 1px solid #999; - position: absolute; -} - -/* cause "body" area (e.g. where clicks are heard) to grow horizontally with text */ -body.mozilla, body.safari { - display: table-cell; -} - -body.doesWrap { - display: block !important; -} - -.safari div { - /* prevents the caret from disappearing on the longest line of the doc */ - padding-right: 1px; -} - -p { - margin: 0; -} - -/*b, strong, .Apple-style-span { font-weight: normal !important; font-style: normal !important; - color: red !important; }*/ - -#linemetricsdiv { - position: absolute; - left: -1000px; - top: -1000px; - color: white; - z-index: -1; - font-size: 12px; /* overridden by lineMetricsDiv.style */ - font-family: monospace; /* overridden by lineMetricsDiv.style */ -} - -#overlaysdiv { position: absolute; left: -1000px; top: -1000px; } \ No newline at end of file diff --git a/trunk/infrastructure/ace/www/firebug/errorIcon.png b/trunk/infrastructure/ace/www/firebug/errorIcon.png deleted file mode 100644 index 2d75261..0000000 Binary files a/trunk/infrastructure/ace/www/firebug/errorIcon.png and /dev/null differ diff --git a/trunk/infrastructure/ace/www/firebug/firebug.css b/trunk/infrastructure/ace/www/firebug/firebug.css deleted file mode 100644 index 1f041c4..0000000 --- a/trunk/infrastructure/ace/www/firebug/firebug.css +++ /dev/null @@ -1,209 +0,0 @@ - -html, body { - margin: 0; - background: #FFFFFF; - font-family: Lucida Grande, Tahoma, sans-serif; - font-size: 11px; - overflow: hidden; -} - -a { - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -.toolbar { - height: 14px; - border-top: 1px solid ThreeDHighlight; - border-bottom: 1px solid ThreeDShadow; - padding: 2px 6px; - background: ThreeDFace; -} - -.toolbarRight { - position: absolute; - top: 4px; - right: 6px; -} - -#log { - overflow: auto; - position: absolute; - left: 0; - width: 100%; -} - -#commandLine { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 18px; - border: none; - border-top: 1px solid ThreeDShadow; -} - -/************************************************************************************************/ - -.logRow { - position: relative; - border-bottom: 1px solid #D7D7D7; - padding: 2px 4px 1px 6px; - background-color: #FFFFFF; -} - -.logRow-command { - font-family: Monaco, monospace; - color: blue; -} - -.objectBox-null { - padding: 0 2px; - border: 1px solid #666666; - background-color: #888888; - color: #FFFFFF; -} - -.objectBox-string { - font-family: Monaco, monospace; - color: red; - white-space: pre; -} - -.objectBox-number { - color: #000088; -} - -.objectBox-function { - font-family: Monaco, monospace; - color: DarkGreen; -} - -.objectBox-object { - color: DarkGreen; - font-weight: bold; -} - -/************************************************************************************************/ - -.logRow-info, -.logRow-error, -.logRow-warning { - background: #FFFFFF no-repeat 2px 2px; - padding-left: 20px; - padding-bottom: 3px; -} - -.logRow-info { - background-image: url(infoIcon.png); -} - -.logRow-warning { - background-color: cyan; - background-image: url(warningIcon.png); -} - -.logRow-error { - background-color: LightYellow; - background-image: url(errorIcon.png); -} - -.errorMessage { - vertical-align: top; - color: #FF0000; -} - -.objectBox-sourceLink { - position: absolute; - right: 4px; - top: 2px; - padding-left: 8px; - font-family: Lucida Grande, sans-serif; - font-weight: bold; - color: #0000FF; -} - -/************************************************************************************************/ - -.logRow-group { - background: #EEEEEE; - border-bottom: none; -} - -.logGroup { - background: #EEEEEE; -} - -.logGroupBox { - margin-left: 24px; - border-top: 1px solid #D7D7D7; - border-left: 1px solid #D7D7D7; -} - -/************************************************************************************************/ - -.selectorTag, -.selectorId, -.selectorClass { - font-family: Monaco, monospace; - font-weight: normal; -} - -.selectorTag { - color: #0000FF; -} - -.selectorId { - color: DarkBlue; -} - -.selectorClass { - color: red; -} - -/************************************************************************************************/ - -.objectBox-element { - font-family: Monaco, monospace; - color: #000088; -} - -.nodeChildren { - margin-left: 16px; -} - -.nodeTag { - color: blue; -} - -.nodeValue { - color: #FF0000; - font-weight: normal; -} - -.nodeText, -.nodeComment { - margin: 0 2px; - vertical-align: top; -} - -.nodeText { - color: #333333; -} - -.nodeComment { - color: DarkGreen; -} - -/************************************************************************************************/ - -.propertyNameCell { - vertical-align: top; -} - -.propertyName { - font-weight: bold; -} diff --git a/trunk/infrastructure/ace/www/firebug/firebug.html b/trunk/infrastructure/ace/www/firebug/firebug.html deleted file mode 100644 index 861e639..0000000 --- a/trunk/infrastructure/ace/www/firebug/firebug.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - Firebug - - - - -
- Clear - - Close - -
-
- - - - - diff --git a/trunk/infrastructure/ace/www/firebug/firebug.js b/trunk/infrastructure/ace/www/firebug/firebug.js deleted file mode 100644 index d3c1978..0000000 --- a/trunk/infrastructure/ace/www/firebug/firebug.js +++ /dev/null @@ -1,688 +0,0 @@ -/** - * 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. - */ - - -if (!("console" in window) || !("firebug" in console)) { -(function() -{ - window.console = - { - log: function() - { - logFormatted(arguments, ""); - }, - - debug: function() - { - logFormatted(arguments, "debug"); - }, - - info: function() - { - logFormatted(arguments, "info"); - }, - - warn: function() - { - logFormatted(arguments, "warning"); - }, - - error: function() - { - logFormatted(arguments, "error"); - }, - - assert: function(truth, message) - { - if (!truth) - { - var args = []; - for (var i = 1; i < arguments.length; ++i) - args.push(arguments[i]); - - logFormatted(args.length ? args : ["Assertion Failure"], "error"); - throw message ? message : "Assertion Failure"; - } - }, - - dir: function(object) - { - var html = []; - - var pairs = []; - for (var name in object) - { - try - { - pairs.push([name, object[name]]); - } - catch (exc) - { - } - } - - pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; }); - - html.push(''); - for (var i = 0; i < pairs.length; ++i) - { - var name = pairs[i][0], value = pairs[i][1]; - - html.push('', - '', ''); - } - html.push('
', - escapeHTML(name), ''); - appendObject(value, html); - html.push('
'); - - logRow(html, "dir"); - }, - - dirxml: function(node) - { - var html = []; - - appendNode(node, html); - logRow(html, "dirxml"); - }, - - group: function() - { - logRow(arguments, "group", pushGroup); - }, - - groupEnd: function() - { - logRow(arguments, "", popGroup); - }, - - time: function(name) - { - timeMap[name] = (new Date()).getTime(); - }, - - timeEnd: function(name) - { - if (name in timeMap) - { - var delta = (new Date()).getTime() - timeMap[name]; - logFormatted([name+ ":", delta+"ms"]); - delete timeMap[name]; - } - }, - - count: function() - { - this.warn(["count() not supported."]); - }, - - trace: function() - { - this.warn(["trace() not supported."]); - }, - - profile: function() - { - this.warn(["profile() not supported."]); - }, - - profileEnd: function() - { - }, - - clear: function() - { - consoleBody.innerHTML = ""; - }, - - open: function() - { - toggleConsole(true); - }, - - close: function() - { - if (frameVisible) - toggleConsole(); - } - }; - - // ******************************************************************************************** - - var consoleFrame = null; - var consoleBody = null; - var commandLine = null; - - var frameVisible = false; - var messageQueue = []; - var groupStack = []; - var timeMap = {}; - - var clPrefix = ">>> "; - - var isFirefox = navigator.userAgent.indexOf("Firefox") != -1; - var isIE = navigator.userAgent.indexOf("MSIE") != -1; - var isOpera = navigator.userAgent.indexOf("Opera") != -1; - var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1; - - // ******************************************************************************************** - - function toggleConsole(forceOpen) - { - frameVisible = forceOpen || !frameVisible; - if (consoleFrame) - consoleFrame.style.visibility = frameVisible ? "visible" : "hidden"; - else - waitForBody(); - } - - function focusCommandLine() - { - toggleConsole(true); - if (commandLine) - commandLine.focus(); - } - - function waitForBody() - { - if (document.body) - createFrame(); - else - setTimeout(waitForBody, 200); - } - - function createFrame() - { - if (consoleFrame) - return; - - window.onFirebugReady = function(doc) - { - window.onFirebugReady = null; - - var toolbar = doc.getElementById("toolbar"); - toolbar.onmousedown = onSplitterMouseDown; - - commandLine = doc.getElementById("commandLine"); - addEvent(commandLine, "keydown", onCommandLineKeyDown); - - addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown); - - consoleBody = doc.getElementById("log"); - layout(); - flush(); - } - - var baseURL = getFirebugURL(); - - consoleFrame = document.createElement("iframe"); - consoleFrame.setAttribute("src", baseURL+"/firebug.html"); - consoleFrame.setAttribute("frameBorder", "0"); - consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden"); - consoleFrame.style.zIndex = "2147483647"; - consoleFrame.style.position = "fixed"; - consoleFrame.style.width = "100%"; - consoleFrame.style.left = "0"; - consoleFrame.style.bottom = "0"; - consoleFrame.style.height = "200px"; - document.body.appendChild(consoleFrame); - } - - function getFirebugURL() - { - var scripts = document.getElementsByTagName("script"); - for (var i = 0; i < scripts.length; ++i) - { - if (scripts[i].src.indexOf("firebug.js") != -1) - { - var lastSlash = scripts[i].src.lastIndexOf("/"); - return scripts[i].src.substr(0, lastSlash); - } - } - } - - function evalCommandLine() - { - var text = commandLine.value; - commandLine.value = ""; - - logRow([clPrefix, text], "command"); - - var value; - try - { - value = eval(text); - } - catch (exc) - { - } - - console.log(value); - } - - function layout() - { - var toolbar = consoleBody.ownerDocument.getElementById("toolbar"); - var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight); - consoleBody.style.top = toolbar.offsetHeight + "px"; - consoleBody.style.height = height + "px"; - - commandLine.style.top = (consoleFrame.offsetHeight - commandLine.offsetHeight) + "px"; - } - - function logRow(message, className, handler) - { - if (consoleBody) - writeMessage(message, className, handler); - else - { - messageQueue.push([message, className, handler]); - waitForBody(); - } - } - - function flush() - { - var queue = messageQueue; - messageQueue = []; - - for (var i = 0; i < queue.length; ++i) - writeMessage(queue[i][0], queue[i][1], queue[i][2]); - } - - function writeMessage(message, className, handler) - { - var isScrolledToBottom = - consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight; - - if (!handler) - handler = writeRow; - - handler(message, className); - - if (isScrolledToBottom) - consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight; - } - - function appendRow(row) - { - var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody; - container.appendChild(row); - } - - function writeRow(message, className) - { - var row = consoleBody.ownerDocument.createElement("div"); - row.className = "logRow" + (className ? " logRow-"+className : ""); - row.innerHTML = message.join(""); - appendRow(row); - } - - function pushGroup(message, className) - { - logFormatted(message, className); - - var groupRow = consoleBody.ownerDocument.createElement("div"); - groupRow.className = "logGroup"; - var groupRowBox = consoleBody.ownerDocument.createElement("div"); - groupRowBox.className = "logGroupBox"; - groupRow.appendChild(groupRowBox); - appendRow(groupRowBox); - groupStack.push(groupRowBox); - } - - function popGroup() - { - groupStack.pop(); - } - - // ******************************************************************************************** - - function logFormatted(objects, className) - { - var html = []; - - var format = objects[0]; - var objIndex = 0; - - if (typeof(format) != "string") - { - format = ""; - objIndex = -1; - } - - var parts = parseFormat(format); - for (var i = 0; i < parts.length; ++i) - { - var part = parts[i]; - if (part && typeof(part) == "object") - { - var object = objects[++objIndex]; - part.appender(object, html); - } - else - appendText(part, html); - } - - for (var i = objIndex+1; i < objects.length; ++i) - { - appendText(" ", html); - - var object = objects[i]; - if (typeof(object) == "string") - appendText(object, html); - else - appendObject(object, html); - } - - logRow(html, className); - } - - function parseFormat(format) - { - var parts = []; - - var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/; - var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat}; - - for (var m = reg.exec(format); m; m = reg.exec(format)) - { - var type = m[8] ? m[8] : m[5]; - var appender = type in appenderMap ? appenderMap[type] : appendObject; - var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0); - - parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1)); - parts.push({appender: appender, precision: precision}); - - format = format.substr(m.index+m[0].length); - } - - parts.push(format); - - return parts; - } - - function escapeHTML(value) - { - function replaceChars(ch) - { - switch (ch) - { - case "<": - return "<"; - case ">": - return ">"; - case "&": - return "&"; - case "'": - return "'"; - case '"': - return """; - } - return "?"; - }; - return String(value).replace(/[<>&"']/g, replaceChars); - } - - function objectToString(object) - { - try - { - return object+""; - } - catch (exc) - { - return null; - } - } - - // ******************************************************************************************** - - function appendText(object, html) - { - html.push(escapeHTML(objectToString(object))); - } - - function appendNull(object, html) - { - html.push('', escapeHTML(objectToString(object)), ''); - } - - function appendString(object, html) - { - html.push('"', escapeHTML(objectToString(object)), - '"'); - } - - function appendInteger(object, html) - { - html.push('', escapeHTML(objectToString(object)), ''); - } - - function appendFloat(object, html) - { - html.push('', escapeHTML(objectToString(object)), ''); - } - - function appendFunction(object, html) - { - var reName = /function ?(.*?)\(/; - var m = reName.exec(objectToString(object)); - var name = m ? m[1] : "function"; - html.push('', escapeHTML(name), '()'); - } - - function appendObject(object, html) - { - try - { - if (object == undefined) - appendNull("undefined", html); - else if (object == null) - appendNull("null", html); - else if (typeof object == "string") - appendString(object, html); - else if (typeof object == "number") - appendInteger(object, html); - else if (typeof object == "function") - appendFunction(object, html); - else if (object.nodeType == 1) - appendSelector(object, html); - else if (typeof object == "object") - appendObjectFormatted(object, html); - else - appendText(object, html); - } - catch (exc) - { - } - } - - function appendObjectFormatted(object, html) - { - var text = objectToString(object); - var reObject = /\[object (.*?)\]/; - - var m = reObject.exec(text); - html.push('', m ? m[1] : text, '') - } - - function appendSelector(object, html) - { - html.push(''); - - html.push('', escapeHTML(object.nodeName.toLowerCase()), ''); - if (object.id) - html.push('#', escapeHTML(object.id), ''); - if (object.className) - html.push('.', escapeHTML(object.className), ''); - - html.push(''); - } - - function appendNode(node, html) - { - if (node.nodeType == 1) - { - html.push( - '
', - '<', node.nodeName.toLowerCase(), ''); - - for (var i = 0; i < node.attributes.length; ++i) - { - var attr = node.attributes[i]; - if (!attr.specified) - continue; - - html.push(' ', attr.nodeName.toLowerCase(), - '="', escapeHTML(attr.nodeValue), - '"') - } - - if (node.firstChild) - { - html.push('>
'); - - for (var child = node.firstChild; child; child = child.nextSibling) - appendNode(child, html); - - html.push('
</', - node.nodeName.toLowerCase(), '>
'); - } - else - html.push('/>'); - } - else if (node.nodeType == 3) - { - html.push('
', escapeHTML(node.nodeValue), - '
'); - } - } - - // ******************************************************************************************** - - function addEvent(object, name, handler) - { - if (document.all) - object.attachEvent("on"+name, handler); - else - object.addEventListener(name, handler, false); - } - - function removeEvent(object, name, handler) - { - if (document.all) - object.detachEvent("on"+name, handler); - else - object.removeEventListener(name, handler, false); - } - - function cancelEvent(event) - { - if (document.all) - event.cancelBubble = true; - else - event.stopPropagation(); - } - - function onError(msg, href, lineNo) - { - var html = []; - - var lastSlash = href.lastIndexOf("/"); - var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1); - - html.push( - '', msg, '', - '' - ); - - logRow(html, "error"); - }; - - function onKeyDown(event) - { - if (event.keyCode == 123) - toggleConsole(); - else if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey - && (event.metaKey || event.ctrlKey)) - focusCommandLine(); - else - return; - - cancelEvent(event); - } - - function onSplitterMouseDown(event) - { - if (isSafari || isOpera) - return; - - addEvent(document, "mousemove", onSplitterMouseMove); - addEvent(document, "mouseup", onSplitterMouseUp); - - for (var i = 0; i < frames.length; ++i) - { - addEvent(frames[i].document, "mousemove", onSplitterMouseMove); - addEvent(frames[i].document, "mouseup", onSplitterMouseUp); - } - } - - function onSplitterMouseMove(event) - { - var win = document.all - ? event.srcElement.ownerDocument.parentWindow - : event.target.ownerDocument.defaultView; - - var clientY = event.clientY; - if (win != win.parent) - clientY += win.frameElement ? win.frameElement.offsetTop : 0; - - var height = consoleFrame.offsetTop + consoleFrame.clientHeight; - var y = height - clientY; - - consoleFrame.style.height = y + "px"; - layout(); - } - - function onSplitterMouseUp(event) - { - removeEvent(document, "mousemove", onSplitterMouseMove); - removeEvent(document, "mouseup", onSplitterMouseUp); - - for (var i = 0; i < frames.length; ++i) - { - removeEvent(frames[i].document, "mousemove", onSplitterMouseMove); - removeEvent(frames[i].document, "mouseup", onSplitterMouseUp); - } - } - - function onCommandLineKeyDown(event) - { - if (event.keyCode == 13) - evalCommandLine(); - else if (event.keyCode == 27) - commandLine.value = ""; - } - - window.onerror = onError; - addEvent(document, isIE || isSafari ? "keydown" : "keypress", onKeyDown); - - if (document.documentElement.getAttribute("debug") == "true") - toggleConsole(true); -})(); -} diff --git a/trunk/infrastructure/ace/www/firebug/firebugx.js b/trunk/infrastructure/ace/www/firebug/firebugx.js deleted file mode 100644 index b2cc49c..0000000 --- a/trunk/infrastructure/ace/www/firebug/firebugx.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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. - */ - - -if (!("console" in window) || !("firebug" in console)) -{ - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", - "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; - - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {} -} \ No newline at end of file diff --git a/trunk/infrastructure/ace/www/firebug/infoIcon.png b/trunk/infrastructure/ace/www/firebug/infoIcon.png deleted file mode 100644 index da1e533..0000000 Binary files a/trunk/infrastructure/ace/www/firebug/infoIcon.png and /dev/null differ diff --git a/trunk/infrastructure/ace/www/firebug/warningIcon.png b/trunk/infrastructure/ace/www/firebug/warningIcon.png deleted file mode 100644 index de51084..0000000 Binary files a/trunk/infrastructure/ace/www/firebug/warningIcon.png and /dev/null differ diff --git a/trunk/infrastructure/ace/www/index.html b/trunk/infrastructure/ace/www/index.html deleted file mode 100644 index a1e6e96..0000000 --- a/trunk/infrastructure/ace/www/index.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - A Code Editor - - - - - - - - - - - -
- - diff --git a/trunk/infrastructure/ace/www/inner.css b/trunk/infrastructure/ace/www/inner.css deleted file mode 100644 index 7479cfe..0000000 --- a/trunk/infrastructure/ace/www/inner.css +++ /dev/null @@ -1,48 +0,0 @@ - -/* Firefox (3) is bad about keeping the text cursor in design mode; - various actions (clicking, dragging, scroll-wheel) lose it and it - doesn't come back easily, presumably because of optimizations. - These rules try to maximize the chance Firefox will think the cursor - needs changing again. -*/ -html { cursor: text; } /* in Safari, produces text cursor for whole doc (inc. below body) */ -span { cursor: auto; } - -a { cursor: pointer !important; } - -/*span { padding-bottom: 1px; }/* padding-top: 1px; }*/ - -/*.inspoint_atstart_generic { background: transparent url(/genimg/solid/2x10/000000.gif) repeat-y left top } -.inspoint_atend_generic { background: transparent url(/genimg/solid/2x10/000000.gif) repeat-y right top }*/ - -/*div { background: transparent url(/static/img/acecarets/default.gif) repeat-y left top }*/ - -/*tt { padding-left: 3px; padding-right: 3px; margin-right: -3px; margin-left: -3px; }*/ - -/*div { display: list-item; list-style: disc outside; margin-left: 20px; }*/ -/*div:before { content:"foo" }*/ - -ul, ol, li { - padding: 0; - margin: 0; -} -ul { margin-left: 1.5em; } -ul ul { margin-left: 0 !important; } -ul.list-bullet1 { margin-left: 1.5em; } -ul.list-bullet2 { margin-left: 3em; } -ul.list-bullet3 { margin-left: 4.5em; } -ul.list-bullet4 { margin-left: 6em; } -ul.list-bullet5 { margin-left: 7.5em; } -ul.list-bullet6 { margin-left: 9em; } -ul.list-bullet7 { margin-left: 10.5em; } -ul.list-bullet8 { margin-left: 12em; } - -ul { list-style-type: disc; } -ul.list-bullet1 { list-style-type: disc; } -ul.list-bullet2 { list-style-type: circle; } -ul.list-bullet3 { list-style-type: square; } -ul.list-bullet4 { list-style-type: disc; } -ul.list-bullet5 { list-style-type: circle; } -ul.list-bullet6 { list-style-type: square; } -ul.list-bullet7 { list-style-type: disc; } -ul.list-bullet8 { list-style-type: circle; } diff --git a/trunk/infrastructure/ace/www/jquery-1.2.1.js b/trunk/infrastructure/ace/www/jquery-1.2.1.js deleted file mode 100644 index b4eb132..0000000 --- a/trunk/infrastructure/ace/www/jquery-1.2.1.js +++ /dev/null @@ -1,2992 +0,0 @@ -(function(){ -/* - * jQuery 1.2.1 - New Wave Javascript - * - * Copyright (c) 2007 John Resig (jquery.com) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * $Date: 2007-09-16 23:42:06 -0400 (Sun, 16 Sep 2007) $ - * $Rev: 3353 $ - */ - -// Map over jQuery in case of overwrite -if ( typeof jQuery != "undefined" ) - var _jQuery = jQuery; - -var jQuery = window.jQuery = function(selector, context) { - // If the context is a namespace object, return a new object - return this instanceof jQuery ? - this.init(selector, context) : - new jQuery(selector, context); -}; - -// Map over the $ in case of overwrite -if ( typeof $ != "undefined" ) - var _$ = $; - -// Map the jQuery namespace to the '$' one -window.$ = jQuery; - -var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; - -jQuery.fn = jQuery.prototype = { - init: function(selector, context) { - // Make sure that a selection was provided - selector = selector || document; - - // Handle HTML strings - if ( typeof selector == "string" ) { - var m = quickExpr.exec(selector); - if ( m && (m[1] || !context) ) { - // HANDLE: $(html) -> $(array) - if ( m[1] ) - selector = jQuery.clean( [ m[1] ], context ); - - // HANDLE: $("#id") - else { - var tmp = document.getElementById( m[3] ); - if ( tmp ) - // Handle the case where IE and Opera return items - // by name instead of ID - if ( tmp.id != m[3] ) - return jQuery().find( selector ); - else { - this[0] = tmp; - this.length = 1; - return this; - } - else - selector = []; - } - - // HANDLE: $(expr) - } else - return new jQuery( context ).find( selector ); - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction(selector) ) - return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( selector ); - - return this.setArray( - // HANDLE: $(array) - selector.constructor == Array && selector || - - // HANDLE: $(arraylike) - // Watch for when an array-like object is passed as the selector - (selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) || - - // HANDLE: $(*) - [ selector ] ); - }, - - jquery: "1.2.1", - - size: function() { - return this.length; - }, - - length: 0, - - get: function( num ) { - return num == undefined ? - - // Return a 'clean' array - jQuery.makeArray( this ) : - - // Return just the object - this[num]; - }, - - pushStack: function( a ) { - var ret = jQuery(a); - ret.prevObject = this; - return ret; - }, - - setArray: function( a ) { - this.length = 0; - Array.prototype.push.apply( this, a ); - return this; - }, - - each: function( fn, args ) { - return jQuery.each( this, fn, args ); - }, - - index: function( obj ) { - var pos = -1; - this.each(function(i){ - if ( this == obj ) pos = i; - }); - return pos; - }, - - attr: function( key, value, type ) { - var obj = key; - - // Look for the case where we're accessing a style value - if ( key.constructor == String ) - if ( value == undefined ) - return this.length && jQuery[ type || "attr" ]( this[0], key ) || undefined; - else { - obj = {}; - obj[ key ] = value; - } - - // Check to see if we're setting style values - return this.each(function(index){ - // Set all the styles - for ( var prop in obj ) - jQuery.attr( - type ? this.style : this, - prop, jQuery.prop(this, obj[prop], type, index, prop) - ); - }); - }, - - css: function( key, value ) { - return this.attr( key, value, "curCSS" ); - }, - - text: function(e) { - if ( typeof e != "object" && e != null ) - return this.empty().append( document.createTextNode( e ) ); - - var t = ""; - jQuery.each( e || this, function(){ - jQuery.each( this.childNodes, function(){ - if ( this.nodeType != 8 ) - t += this.nodeType != 1 ? - this.nodeValue : jQuery.fn.text([ this ]); - }); - }); - return t; - }, - - wrapAll: function(html) { - if ( this[0] ) - // The elements to wrap the target around - jQuery(html, this[0].ownerDocument) - .clone() - .insertBefore(this[0]) - .map(function(){ - var elem = this; - while ( elem.firstChild ) - elem = elem.firstChild; - return elem; - }) - .append(this); - - return this; - }, - - wrapInner: function(html) { - return this.each(function(){ - jQuery(this).contents().wrapAll(html); - }); - }, - - wrap: function(html) { - return this.each(function(){ - jQuery(this).wrapAll(html); - }); - }, - - append: function() { - return this.domManip(arguments, true, 1, function(a){ - this.appendChild( a ); - }); - }, - - prepend: function() { - return this.domManip(arguments, true, -1, function(a){ - this.insertBefore( a, this.firstChild ); - }); - }, - - before: function() { - return this.domManip(arguments, false, 1, function(a){ - this.parentNode.insertBefore( a, this ); - }); - }, - - after: function() { - return this.domManip(arguments, false, -1, function(a){ - this.parentNode.insertBefore( a, this.nextSibling ); - }); - }, - - end: function() { - return this.prevObject || jQuery([]); - }, - - find: function(t) { - var data = jQuery.map(this, function(a){ return jQuery.find(t,a); }); - return this.pushStack( /[^+>] [^+>]/.test( t ) || t.indexOf("..") > -1 ? - jQuery.unique( data ) : data ); - }, - - clone: function(events) { - // Do the clone - var ret = this.map(function(){ - return this.outerHTML ? jQuery(this.outerHTML)[0] : this.cloneNode(true); - }); - - // Need to set the expando to null on the cloned set if it exists - // removeData doesn't work here, IE removes it from the original as well - // this is primarily for IE but the data expando shouldn't be copied over in any browser - var clone = ret.find("*").andSelf().each(function(){ - if ( this[ expando ] != undefined ) - this[ expando ] = null; - }); - - // Copy the events from the original to the clone - if (events === true) - this.find("*").andSelf().each(function(i) { - var events = jQuery.data(this, "events"); - for ( var type in events ) - for ( var handler in events[type] ) - jQuery.event.add(clone[i], type, events[type][handler], events[type][handler].data); - }); - - // Return the cloned set - return ret; - }, - - filter: function(t) { - return this.pushStack( - jQuery.isFunction( t ) && - jQuery.grep(this, function(el, index){ - return t.apply(el, [index]); - }) || - - jQuery.multiFilter(t,this) ); - }, - - not: function(t) { - return this.pushStack( - t.constructor == String && - jQuery.multiFilter(t, this, true) || - - jQuery.grep(this, function(a) { - return ( t.constructor == Array || t.jquery ) - ? jQuery.inArray( a, t ) < 0 - : a != t; - }) - ); - }, - - add: function(t) { - return this.pushStack( jQuery.merge( - this.get(), - t.constructor == String ? - jQuery(t).get() : - t.length != undefined && (!t.nodeName || jQuery.nodeName(t, "form")) ? - t : [t] ) - ); - }, - - is: function(expr) { - return expr ? jQuery.multiFilter(expr,this).length > 0 : false; - }, - - hasClass: function(expr) { - return this.is("." + expr); - }, - - val: function( val ) { - if ( val == undefined ) { - if ( this.length ) { - var elem = this[0]; - - // We need to handle select boxes special - if ( jQuery.nodeName(elem, "select") ) { - var index = elem.selectedIndex, - a = [], - options = elem.options, - one = elem.type == "select-one"; - - // Nothing was selected - if ( index < 0 ) - return null; - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[i]; - if ( option.selected ) { - // Get the specifc value for the option - var val = jQuery.browser.msie && !option.attributes["value"].specified ? option.text : option.value; - - // We don't need an array for one selects - if ( one ) - return val; - - // Multi-Selects return an array - a.push(val); - } - } - - return a; - - // Everything else, we just grab the value - } else - return this[0].value.replace(/\r/g, ""); - } - } else - return this.each(function(){ - if ( val.constructor == Array && /radio|checkbox/.test(this.type) ) - this.checked = (jQuery.inArray(this.value, val) >= 0 || - jQuery.inArray(this.name, val) >= 0); - else if ( jQuery.nodeName(this, "select") ) { - var tmp = val.constructor == Array ? val : [val]; - - jQuery("option", this).each(function(){ - this.selected = (jQuery.inArray(this.value, tmp) >= 0 || - jQuery.inArray(this.text, tmp) >= 0); - }); - - if ( !tmp.length ) - this.selectedIndex = -1; - } else - this.value = val; - }); - }, - - html: function( val ) { - return val == undefined ? - ( this.length ? this[0].innerHTML : null ) : - this.empty().append( val ); - }, - - replaceWith: function( val ) { - return this.after( val ).remove(); - }, - - eq: function(i){ - return this.slice(i, i+1); - }, - - slice: function() { - return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); - }, - - map: function(fn) { - return this.pushStack(jQuery.map( this, function(elem,i){ - return fn.call( elem, i, elem ); - })); - }, - - andSelf: function() { - return this.add( this.prevObject ); - }, - - domManip: function(args, table, dir, fn) { - var clone = this.length > 1, a; - - return this.each(function(){ - if ( !a ) { - a = jQuery.clean(args, this.ownerDocument); - if ( dir < 0 ) - a.reverse(); - } - - var obj = this; - - if ( table && jQuery.nodeName(this, "table") && jQuery.nodeName(a[0], "tr") ) - obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody")); - - jQuery.each( a, function(){ - var elem = clone ? this.cloneNode(true) : this; - if ( !evalScript(0, elem) ) - fn.call( obj, elem ); - }); - }); - } -}; - -function evalScript(i, elem){ - var script = jQuery.nodeName(elem, "script"); - - if ( script ) { - if ( elem.src ) - jQuery.ajax({ url: elem.src, async: false, dataType: "script" }); - else - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - - if ( elem.parentNode ) - elem.parentNode.removeChild(elem); - - } else if ( elem.nodeType == 1 ) - jQuery("script", elem).each(evalScript); - - return script; -} - -jQuery.extend = jQuery.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, a = 1, al = arguments.length, deep = false; - - // Handle a deep copy situation - if ( target.constructor == Boolean ) { - deep = target; - target = arguments[1] || {}; - } - - // extend jQuery itself if only one argument is passed - if ( al == 1 ) { - target = this; - a = 0; - } - - var prop; - - for ( ; a < al; a++ ) - // Only deal with non-null/undefined values - if ( (prop = arguments[a]) != null ) - // Extend the base object - for ( var i in prop ) { - // Prevent never-ending loop - if ( target == prop[i] ) - continue; - - // Recurse if we're merging object values - if ( deep && typeof prop[i] == 'object' && target[i] ) - jQuery.extend( target[i], prop[i] ); - - // Don't bring in undefined values - else if ( prop[i] != undefined ) - target[i] = prop[i]; - } - - // Return the modified object - return target; -}; - -var expando = "jQuery" + (new Date()).getTime(), uuid = 0, win = {}; - -jQuery.extend({ - noConflict: function(deep) { - window.$ = _$; - if ( deep ) - window.jQuery = _jQuery; - return jQuery; - }, - - // This may seem like some crazy code, but trust me when I say that this - // is the only cross-browser way to do this. --John - isFunction: function( fn ) { - return !!fn && typeof fn != "string" && !fn.nodeName && - fn.constructor != Array && /function/i.test( fn + "" ); - }, - - // check if an element is in a XML document - isXMLDoc: function(elem) { - return elem.documentElement && !elem.body || - elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; - }, - - // Evalulates a script in a global context - // Evaluates Async. in Safari 2 :-( - globalEval: function( data ) { - data = jQuery.trim( data ); - if ( data ) { - if ( window.execScript ) - window.execScript( data ); - else if ( jQuery.browser.safari ) - // safari doesn't provide a synchronous global eval - window.setTimeout( data, 0 ); - else - eval.call( window, data ); - } - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); - }, - - cache: {}, - - data: function( elem, name, data ) { - elem = elem == window ? win : elem; - - var id = elem[ expando ]; - - // Compute a unique ID for the element - if ( !id ) - id = elem[ expando ] = ++uuid; - - // Only generate the data cache if we're - // trying to access or manipulate it - if ( name && !jQuery.cache[ id ] ) - jQuery.cache[ id ] = {}; - - // Prevent overriding the named cache with undefined values - if ( data != undefined ) - jQuery.cache[ id ][ name ] = data; - - // Return the named cache data, or the ID for the element - return name ? jQuery.cache[ id ][ name ] : id; - }, - - removeData: function( elem, name ) { - elem = elem == window ? win : elem; - - var id = elem[ expando ]; - - // If we want to remove a specific section of the element's data - if ( name ) { - if ( jQuery.cache[ id ] ) { - // Remove the section of cache data - delete jQuery.cache[ id ][ name ]; - - // If we've removed all the data, remove the element's cache - name = ""; - for ( name in jQuery.cache[ id ] ) break; - if ( !name ) - jQuery.removeData( elem ); - } - - // Otherwise, we want to remove all of the element's data - } else { - // Clean up the element expando - try { - delete elem[ expando ]; - } catch(e){ - // IE has trouble directly removing the expando - // but it's ok with using removeAttribute - if ( elem.removeAttribute ) - elem.removeAttribute( expando ); - } - - // Completely remove the data cache - delete jQuery.cache[ id ]; - } - }, - - // args is for internal usage only - each: function( obj, fn, args ) { - if ( args ) { - if ( obj.length == undefined ) - for ( var i in obj ) - fn.apply( obj[i], args ); - else - for ( var i = 0, ol = obj.length; i < ol; i++ ) - if ( fn.apply( obj[i], args ) === false ) break; - - // A special, fast, case for the most common use of each - } else { - if ( obj.length == undefined ) - for ( var i in obj ) - fn.call( obj[i], i, obj[i] ); - else - for ( var i = 0, ol = obj.length, val = obj[0]; - i < ol && fn.call(val,i,val) !== false; val = obj[++i] ){} - } - - return obj; - }, - - prop: function(elem, value, type, index, prop){ - // Handle executable functions - if ( jQuery.isFunction( value ) ) - value = value.call( elem, [index] ); - - // exclude the following css properties to add px - var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i; - - // Handle passing in a number to a CSS property - return value && value.constructor == Number && type == "curCSS" && !exclude.test(prop) ? - value + "px" : - value; - }, - - className: { - // internal only, use addClass("class") - add: function( elem, c ){ - jQuery.each( (c || "").split(/\s+/), function(i, cur){ - if ( !jQuery.className.has( elem.className, cur ) ) - elem.className += ( elem.className ? " " : "" ) + cur; - }); - }, - - // internal only, use removeClass("class") - remove: function( elem, c ){ - elem.className = c != undefined ? - jQuery.grep( elem.className.split(/\s+/), function(cur){ - return !jQuery.className.has( c, cur ); - }).join(" ") : ""; - }, - - // internal only, use is(".class") - has: function( t, c ) { - return jQuery.inArray( c, (t.className || t).toString().split(/\s+/) ) > -1; - } - }, - - swap: function(e,o,f) { - for ( var i in o ) { - e.style["old"+i] = e.style[i]; - e.style[i] = o[i]; - } - f.apply( e, [] ); - for ( var i in o ) - e.style[i] = e.style["old"+i]; - }, - - css: function(e,p) { - if ( p == "height" || p == "width" ) { - var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"]; - - jQuery.each( d, function(){ - old["padding" + this] = 0; - old["border" + this + "Width"] = 0; - }); - - jQuery.swap( e, old, function() { - if ( jQuery(e).is(':visible') ) { - oHeight = e.offsetHeight; - oWidth = e.offsetWidth; - } else { - e = jQuery(e.cloneNode(true)) - .find(":radio").removeAttr("checked").end() - .css({ - visibility: "hidden", position: "absolute", display: "block", right: "0", left: "0" - }).appendTo(e.parentNode)[0]; - - var parPos = jQuery.css(e.parentNode,"position") || "static"; - if ( parPos == "static" ) - e.parentNode.style.position = "relative"; - - oHeight = e.clientHeight; - oWidth = e.clientWidth; - - if ( parPos == "static" ) - e.parentNode.style.position = "static"; - - e.parentNode.removeChild(e); - } - }); - - return p == "height" ? oHeight : oWidth; - } - - return jQuery.curCSS( e, p ); - }, - - curCSS: function(elem, prop, force) { - var ret, stack = [], swap = []; - - // A helper method for determining if an element's values are broken - function color(a){ - if ( !jQuery.browser.safari ) - return false; - - var ret = document.defaultView.getComputedStyle(a,null); - return !ret || ret.getPropertyValue("color") == ""; - } - - if (prop == "opacity" && jQuery.browser.msie) { - ret = jQuery.attr(elem.style, "opacity"); - return ret == "" ? "1" : ret; - } - - if (prop.match(/float/i)) - prop = styleFloat; - - if (!force && elem.style[prop]) - ret = elem.style[prop]; - - else if (document.defaultView && document.defaultView.getComputedStyle) { - - if (prop.match(/float/i)) - prop = "float"; - - prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase(); - var cur = document.defaultView.getComputedStyle(elem, null); - - if ( cur && !color(elem) ) - ret = cur.getPropertyValue(prop); - - // If the element isn't reporting its values properly in Safari - // then some display: none elements are involved - else { - // Locate all of the parent display: none elements - for ( var a = elem; a && color(a); a = a.parentNode ) - stack.unshift(a); - - // Go through and make them visible, but in reverse - // (It would be better if we knew the exact display type that they had) - for ( a = 0; a < stack.length; a++ ) - if ( color(stack[a]) ) { - swap[a] = stack[a].style.display; - stack[a].style.display = "block"; - } - - // Since we flip the display style, we have to handle that - // one special, otherwise get the value - ret = prop == "display" && swap[stack.length-1] != null ? - "none" : - document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop) || ""; - - // Finally, revert the display styles back - for ( a = 0; a < swap.length; a++ ) - if ( swap[a] != null ) - stack[a].style.display = swap[a]; - } - - if ( prop == "opacity" && ret == "" ) - ret = "1"; - - } else if (elem.currentStyle) { - var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();}); - ret = elem.currentStyle[prop] || elem.currentStyle[newProp]; - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !/^\d+(px)?$/i.test(ret) && /^\d/.test(ret) ) { - var style = elem.style.left; - var runtimeStyle = elem.runtimeStyle.left; - elem.runtimeStyle.left = elem.currentStyle.left; - elem.style.left = ret || 0; - ret = elem.style.pixelLeft + "px"; - elem.style.left = style; - elem.runtimeStyle.left = runtimeStyle; - } - } - - return ret; - }, - - clean: function(a, doc) { - var r = []; - doc = doc || document; - - jQuery.each( a, function(i,arg){ - if ( !arg ) return; - - if ( arg.constructor == Number ) - arg = arg.toString(); - - // Convert html string into DOM nodes - if ( typeof arg == "string" ) { - // Fix "XHTML"-style tags in all browsers - arg = arg.replace(/(<(\w+)[^>]*?)\/>/g, function(m, all, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area)$/i)? m : all+">"; - }); - - // Trim whitespace, otherwise indexOf won't work as expected - var s = jQuery.trim(arg).toLowerCase(), div = doc.createElement("div"), tb = []; - - var wrap = - // option or optgroup - !s.indexOf("", ""] || - - !s.indexOf("", ""] || - - s.match(/^<(thead|tbody|tfoot|colg|cap)/) && - [1, "", "
"] || - - !s.indexOf("", ""] || - - // matched above - (!s.indexOf("", ""] || - - !s.indexOf("", ""] || - - // IE can't serialize and