michael@0: (function() { michael@0: // A minilanguage for instantiating linked CodeMirror instances and Docs michael@0: function instantiateSpec(spec, place, opts) { michael@0: var names = {}, pos = 0, l = spec.length, editors = []; michael@0: while (spec) { michael@0: var m = spec.match(/^(\w+)(\*?)(?:='([^\']*)'|<(~?)(\w+)(?:\/(\d+)-(\d+))?)\s*/); michael@0: var name = m[1], isDoc = m[2], cur; michael@0: if (m[3]) { michael@0: cur = isDoc ? CodeMirror.Doc(m[3]) : CodeMirror(place, clone(opts, {value: m[3]})); michael@0: } else { michael@0: var other = m[5]; michael@0: if (!names.hasOwnProperty(other)) { michael@0: names[other] = editors.length; michael@0: editors.push(CodeMirror(place, opts)); michael@0: } michael@0: var doc = editors[names[other]].linkedDoc({ michael@0: sharedHist: !m[4], michael@0: from: m[6] ? Number(m[6]) : null, michael@0: to: m[7] ? Number(m[7]) : null michael@0: }); michael@0: cur = isDoc ? doc : CodeMirror(place, clone(opts, {value: doc})); michael@0: } michael@0: names[name] = editors.length; michael@0: editors.push(cur); michael@0: spec = spec.slice(m[0].length); michael@0: } michael@0: return editors; michael@0: } michael@0: michael@0: function clone(obj, props) { michael@0: if (!obj) return; michael@0: clone.prototype = obj; michael@0: var inst = new clone(); michael@0: if (props) for (var n in props) if (props.hasOwnProperty(n)) michael@0: inst[n] = props[n]; michael@0: return inst; michael@0: } michael@0: michael@0: function eqAll(val) { michael@0: var end = arguments.length, msg = null; michael@0: if (typeof arguments[end-1] == "string") michael@0: msg = arguments[--end]; michael@0: if (i == end) throw new Error("No editors provided to eqAll"); michael@0: for (var i = 1; i < end; ++i) michael@0: eq(arguments[i].getValue(), val, msg) michael@0: } michael@0: michael@0: function testDoc(name, spec, run, opts, expectFail) { michael@0: if (!opts) opts = {}; michael@0: michael@0: return test("doc_" + name, function() { michael@0: var place = document.getElementById("testground"); michael@0: var editors = instantiateSpec(spec, place, opts); michael@0: var successful = false; michael@0: michael@0: try { michael@0: run.apply(null, editors); michael@0: successful = true; michael@0: } finally { michael@0: if (!successful || verbose) { michael@0: place.style.visibility = "visible"; michael@0: } else { michael@0: for (var i = 0; i < editors.length; ++i) michael@0: if (editors[i] instanceof CodeMirror) michael@0: place.removeChild(editors[i].getWrapperElement()); michael@0: } michael@0: } michael@0: }, expectFail); michael@0: } michael@0: michael@0: var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); michael@0: michael@0: function testBasic(a, b) { michael@0: eqAll("x", a, b); michael@0: a.setValue("hey"); michael@0: eqAll("hey", a, b); michael@0: b.setValue("wow"); michael@0: eqAll("wow", a, b); michael@0: a.replaceRange("u\nv\nw", Pos(0, 3)); michael@0: b.replaceRange("i", Pos(0, 4)); michael@0: b.replaceRange("j", Pos(2, 1)); michael@0: eqAll("wowui\nv\nwj", a, b); michael@0: } michael@0: michael@0: testDoc("basic", "A='x' B 0, "not at left"); michael@0: is(pos.top > 0, "not at top"); michael@0: }); michael@0: michael@0: testDoc("copyDoc", "A='u'", function(a) { michael@0: var copy = a.getDoc().copy(true); michael@0: a.setValue("foo"); michael@0: copy.setValue("bar"); michael@0: var old = a.swapDoc(copy); michael@0: eq(a.getValue(), "bar"); michael@0: a.undo(); michael@0: eq(a.getValue(), "u"); michael@0: a.swapDoc(old); michael@0: eq(a.getValue(), "foo"); michael@0: eq(old.historySize().undo, 1); michael@0: eq(old.copy(false).historySize().undo, 0); michael@0: }); michael@0: michael@0: testDoc("docKeepsMode", "A='1+1'", function(a) { michael@0: var other = CodeMirror.Doc("hi", "text/x-markdown"); michael@0: a.setOption("mode", "text/javascript"); michael@0: var old = a.swapDoc(other); michael@0: eq(a.getOption("mode"), "text/x-markdown"); michael@0: eq(a.getMode().name, "markdown"); michael@0: a.swapDoc(old); michael@0: eq(a.getOption("mode"), "text/javascript"); michael@0: eq(a.getMode().name, "javascript"); michael@0: }); michael@0: michael@0: testDoc("subview", "A='1\n2\n3\n4\n5' B<~A/1-3", function(a, b) { michael@0: eq(b.getValue(), "2\n3"); michael@0: eq(b.firstLine(), 1); michael@0: b.setCursor(Pos(4)); michael@0: eqPos(b.getCursor(), Pos(2, 1)); michael@0: a.replaceRange("-1\n0\n", Pos(0, 0)); michael@0: eq(b.firstLine(), 3); michael@0: eqPos(b.getCursor(), Pos(4, 1)); michael@0: a.undo(); michael@0: eqPos(b.getCursor(), Pos(2, 1)); michael@0: b.replaceRange("oyoy\n", Pos(2, 0)); michael@0: eq(a.getValue(), "1\n2\noyoy\n3\n4\n5"); michael@0: b.undo(); michael@0: eq(a.getValue(), "1\n2\n3\n4\n5"); michael@0: }); michael@0: michael@0: testDoc("subviewEditOnBoundary", "A='11\n22\n33\n44\n55' B<~A/1-4", function(a, b) { michael@0: a.replaceRange("x\nyy\nz", Pos(0, 1), Pos(2, 1)); michael@0: eq(b.firstLine(), 2); michael@0: eq(b.lineCount(), 2); michael@0: eq(b.getValue(), "z3\n44"); michael@0: a.replaceRange("q\nrr\ns", Pos(3, 1), Pos(4, 1)); michael@0: eq(b.firstLine(), 2); michael@0: eq(b.getValue(), "z3\n4q"); michael@0: eq(a.getValue(), "1x\nyy\nz3\n4q\nrr\ns5"); michael@0: a.execCommand("selectAll"); michael@0: a.replaceSelection("!"); michael@0: eqAll("!", a, b); michael@0: }); michael@0: michael@0: michael@0: testDoc("sharedMarker", "A='ab\ncd\nef\ngh' B