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