dvcsdemo.js
author Tony Garnock-Jones <tonyg@kcbbs.gen.nz>
Mon Dec 21 23:52:38 2009 +0000 (2 years ago)
changeset 123 bc4c90cbd778
parent 56ed61272b607d
permissions -rw-r--r--
For OS X
     1 // Copyright (c) 2008 Tony Garnock-Jones <tonyg@lshift.net>
     2 // Copyright (c) 2008 LShift Ltd. <query@lshift.net>
     3 //
     4 // Permission is hereby granted, free of charge, to any person
     5 // obtaining a copy of this software and associated documentation files
     6 // (the "Software"), to deal in the Software without restriction,
     7 // including without limitation the rights to use, copy, modify, merge,
     8 // publish, distribute, sublicense, and/or sell copies of the Software,
     9 // and to permit persons to whom the Software is furnished to do so,
    10 // subject to the following conditions:
    11 //
    12 // The above copyright notice and this permission notice shall be
    13 // included in all copies or substantial portions of the Software.
    14 //
    15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
    19 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
    20 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    21 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    22 // SOFTWARE.
    23 
    24 var repo = new Dvcs.Repository();
    25 var fs = repo.update(null);
    26 var dirty = false;
    27 var mergeAncestor = null;
    28 
    29 Dvcs._debugMode = true;
    30 
    31 function redisplay_repository_history() {
    32     var ordering = DrawDvcs.renderRepository(repo);
    33     var html = "";
    34     for (var i = 0; i < ordering.length; i++) {
    35         var item = ordering[i];
    36         var pictures = item.pictures;
    37         for (var j = 0; j < pictures.length; j++) {
    38             html = html + "<img src='img/" + pictures[j] + "' />";
    39         }
    40         html = html + "<span class='historyline'><span id='" + item.revId +
    41             "' class='nonselected'>" +
    42             "<span onclick='maybeSelectRev(\"" + item.revId + "\")'>" + item.revId + "</span>" +
    43             " <span class='mergecommand" +
    44             "' onclick='maybeMergeRev(\"" + item.revId + "\")'>(m)</span></span></span><br />\n";
    45     }
    46     document.getElementById("historyContainer").innerHTML =
    47         "<p style='line-height: 0px; white-space: nowrap;'>" + html + "</p>";
    48 }
    49 
    50 function set_rev_span(className, revId) {
    51     var revSpan = document.getElementById(revId || fs.directParent);
    52     if (revSpan) {
    53         revSpan.className = className;
    54     }
    55 }
    56 
    57 function mark_dirty(newValue, comment) {
    58     if (newValue && !dirty) { set_rev_span("dirty"); }
    59     dirty = newValue;
    60     document.getElementById("revertbutton").disabled = newValue ? "" : "disabled";
    61     document.getElementById("commitbutton").disabled = newValue ? "" : "disabled";
    62     var stateSpan = document.getElementById("statespan");
    63     if (dirty) {
    64         if (comment != undefined || stateSpan.innerHTML === "")  {
    65             stateSpan.innerHTML = comment ? comment : "Files changed";
    66         }
    67     } else {
    68         stateSpan.innerHTML = "";
    69     }
    70 }
    71 
    72 function sync_view_of_fs() {
    73     var workspace = document.getElementById("workspace");
    74     workspace.innerHTML = "";
    75     for (var inode in fs.inodes) {
    76         var inodeDiv = document.createElement("div");
    77         workspace.appendChild(inodeDiv);
    78         var filename = document.createElement("input");
    79         filename.value = fs.getProp(inode, "name");
    80         filename.size = 70;
    81         filename.onkeyup = function () {
    82             fs.setProp(inode, "name", filename.value);
    83             mark_dirty(true);
    84         };
    85         inodeDiv.appendChild(filename);
    86         var deleteButton = document.createElement("button");
    87         deleteButton.innerHTML = "Delete";
    88         deleteButton.onclick = function () {
    89             fs.deleteFile(inode);
    90             mark_dirty(true);
    91             inodeDiv.className = "deletedFile";
    92         };
    93         inodeDiv.appendChild(deleteButton);
    94         inodeDiv.appendChild(document.createElement("br"));
    95         var contents = document.createElement("textarea");
    96         contents.cols = 80;
    97         contents.rows = 10;
    98         contents.value = fs.getProp(inode, "text").join("\n");
    99         contents.onkeyup = function () {
   100             fs.setProp(inode, "text", contents.value.split(/\n/));
   101             mark_dirty(true);
   102         };
   103         inodeDiv.appendChild(contents);
   104     }
   105     set_rev_span(dirty ? "dirty" : "clean");
   106 
   107     //document.getElementById("log").innerHTML = "";
   108     dumpRepo();
   109 }
   110 
   111 function maybeSelectRev(revId) {
   112     if (!dirty) {
   113         selectRev(revId);
   114     }
   115 }
   116 
   117 function maybeMergeRev(revId) {
   118     if (!dirty) {
   119 	if (!repo.canMerge(fs.directParent, revId)) {
   120 	    alert("Refusing to attempt merge with ancestor");
   121 	} else {
   122             var m = repo.merge(fs.directParent, revId);
   123 	    if (m == null) {
   124 		alert("Attempted to merge with ancestor");
   125 	    } else {
   126 		fs = m.files;
   127 		for (var i = 0; i < m.conflicts.length; i++) {
   128 		    var conflictRecord = m.conflicts[i];
   129 		    var inode = conflictRecord.inode;
   130 		    for (var okProp in conflictRecord.partialResult) {
   131 			fs.setProp(inode, okProp, conflictRecord.partialResult[okProp]);
   132 		    }
   133 		    for (var badProp in conflictRecord.conflictDetails) {
   134 			var merger = conflictRecord.conflictDetails[badProp];
   135 			if (badProp == "name") {
   136 			    fs.setProp(inode, "name", merger[0].conflict.a + " / " + merger[0].conflict.b);
   137 			} else if (badProp == "text") {
   138 			    fs.setProp(inode, "text", build_conflict_markers(merger));
   139 			}
   140 		    }
   141 		}
   142 		set_rev_span("ancestor", m.ancestor);
   143 		mergeAncestor = m.ancestor;
   144 		mark_dirty(true, "Merge in progress (merging "+revId+" into "+fs.directParent+")");
   145 		sync_view_of_fs();
   146 	    }
   147 	}
   148     }
   149 }
   150 
   151 function build_conflict_markers(merger) {
   152     var lines = [];
   153     for (var i = 0; i < merger.length; i++) {
   154         var item = merger[i];
   155         if (item.ok) {
   156             lines = lines.concat(item.ok);
   157         } else {
   158             lines = lines.concat(["<<<<<<<<<"], item.conflict.a,
   159                                  ["========="], item.conflict.b,
   160                                  [">>>>>>>>>"]);
   161         }
   162     }
   163     return lines;
   164 }
   165 
   166 function selectRev(revId) {
   167     set_rev_span("nonselected");
   168     if (mergeAncestor) {
   169         set_rev_span("nonselected", mergeAncestor);
   170         mergeAncestor = null;
   171     }
   172     fs = repo.update(revId);
   173     mark_dirty(false);
   174     sync_view_of_fs();
   175 }
   176 
   177 function revert() {
   178     selectRev(fs.directParent);
   179 }
   180 
   181 function commit() {
   182     if (dirty) {
   183         var newRevId = repo.commit(fs);
   184         redisplay_repository_history();
   185         selectRev(newRevId);
   186     }
   187 }
   188 
   189 function newfile() {
   190     var inode = fs.createFile();
   191     fs.setProp(inode, "name", inode);
   192     fs.setProp(inode, "text", []);
   193     mark_dirty(true);
   194     sync_view_of_fs();
   195 }
   196 
   197 function dvcsdemo_main() {
   198     selectPresetNamed("clear");
   199 }
   200 
   201 function dumpRepo() {
   202     log(JSON.stringify(repo, null, 2));
   203 }
   204 
   205 function toggleLogVisible() {
   206     var e = document.getElementById("log");
   207     e.className = (e.className == "invisible") ? "" : "invisible";
   208 }
   209 
   210 var presets = {};
   211 presets.clear = function () {
   212     repo = new Dvcs.Repository();
   213     fs = repo.update(null);
   214 };
   215 
   216 presets.preset1 = function () {
   217     presets.clear();
   218     var fileA = fs.createFile();
   219     fs.setProp(fileA, "name", "File A");
   220     fs.setProp(fileA, "text", "A B C D E".split(/ /));
   221     var rA = repo.commit(fs);
   222     fs.setBranch("BBB");
   223     fs.setProp(fileA, "text", "G G G A B C D E".split(/ /));
   224     var rB1 = repo.commit(fs);
   225     fs.setProp(fileA, "text", "A B C D E G G G A B C D E".split(/ /));
   226     var rB2 = repo.commit(fs);
   227     fs = repo.update(rA);
   228     fs.setProp(fileA, "name", "File A, renamed");
   229     fs.setProp(fileA, "text", "A B X D E".split(/ /));
   230     var rC = repo.commit(fs);
   231 };
   232 
   233 presets.preset2 = function () {
   234     presets.clear();
   235     var fileA = fs.createFile();
   236     fs.setProp(fileA, "name", "File A");
   237     fs.setProp(fileA, "text", "A B C D E".split(/ /));
   238     var rA = repo.commit(fs);
   239     fs.setBranch("BBB");
   240     fs.setProp(fileA, "text", "G G G A B C D E".split(/ /));
   241     var rB1 = repo.commit(fs);
   242     fs.setProp(fileA, "text", "A B C D E G G G A B C D E".split(/ /));
   243     var rB2 = repo.commit(fs);
   244     fs = repo.update(rA);
   245     fs.setProp(fileA, "name", "File A, renamed");
   246     fs.setProp(fileA, "text", "A B X D E".split(/ /));
   247     var rC = repo.commit(fs);
   248 
   249     fs = repo.update(rB2);
   250     fs.setProp(fileA, "text", "A B Z D E G G G A B C D E".split(/ /));
   251     var rB3 = repo.commit(fs);
   252 };
   253 
   254 presets.preset3 = function () {
   255     presets.clear();
   256     var fileA = fs.createFile();
   257     fs.setProp(fileA, "name", "File A");
   258     fs.setProp(fileA, "text", "A B C D E".split(/ /));
   259     var rA = repo.commit(fs);
   260     fs.setBranch("BBB");
   261     fs.setProp(fileA, "text", "G G G A B C D E".split(/ /));
   262     var rB1 = repo.commit(fs);
   263     fs.setProp(fileA, "text", "A B C D E G G G A B C D E".split(/ /));
   264     var rB2 = repo.commit(fs);
   265     fs = repo.update(rA);
   266     fs.setProp(fileA, "name", "File A, renamed");
   267     fs.setProp(fileA, "text", "A B X D E".split(/ /));
   268     var rC = repo.commit(fs);
   269 
   270     var mergeResult = repo.merge(rC, rB2);
   271     var rMerger = repo.commit(mergeResult.files);
   272     fs = repo.update(rMerger);
   273     fs = repo.update("BBB");
   274     fs.deleteFile(fileA);
   275     var rB3 = repo.commit(fs);
   276     var rMerger2 = repo.commit(repo.merge(rB3, rMerger).files);
   277     fs = repo.update(rMerger2);
   278 };
   279 
   280 presets.ambiguousLCA = function () {
   281     var repoExt = {
   282         "bodies": {
   283             "46c316ca-5c4f-4474-a7ea-cfcea3b2ca0f": {
   284                 "name": "The File",
   285                 "text": ["a"]
   286             },
   287             "8e184218-c4d8-426b-b190-916e47249ec6": {
   288                 "name": "The File",
   289                 "text": ["b"]
   290             },
   291             "2f1dd5b6-271b-4adc-a3ea-cbd11588e95d": {
   292                 "name": "The File",
   293                 "text": ["c"]
   294             },
   295             "6a28a74c-727b-4ea0-bda0-aa88c53c730c": {
   296                 "name": "The File",
   297                 "text": ["g"]
   298             },
   299             "50a9df9e-e1c9-4138-a7d4-042d6b0e1e1d": {
   300                 "name": "The File",
   301                 "text": ["d"]
   302             },
   303             "98a26661-3cc4-42bc-9cac-1d01dd64b7f3": {
   304                 "name": "The File",
   305                 "text": ["h"]
   306             },
   307             "c8a089a0-abf3-4940-935f-6a039fdb32ed": {
   308                 "name": "The File",
   309                 "text": ["i"]
   310             },
   311             "3e805399-d30b-4cd8-8e29-52d98650de77": {
   312                 "name": "The File",
   313                 "text": ["e"]
   314             }
   315         },
   316         "revisions": {
   317             "814f45c3-916d-4a51-8f91-f8c8ed24c598": {
   318                 "alive": {
   319                     "d077723c-5169-45dc-a465-0668870979d6": "46c316ca-5c4f-4474-a7ea-cfcea3b2ca0f"
   320                 },
   321                 "dead": {},
   322                 "changed": ["d077723c-5169-45dc-a465-0668870979d6"],
   323                 "branch": null,
   324                 "timestamp": 1212662063125,
   325                 "directParent": null,
   326                 "additionalParent": null
   327             },
   328             "ff9b6da5-b759-4c8c-8c5d-8497504f4529": {
   329                 "alive": {
   330                     "d077723c-5169-45dc-a465-0668870979d6": "8e184218-c4d8-426b-b190-916e47249ec6"
   331                 },
   332                 "dead": {},
   333                 "changed": ["d077723c-5169-45dc-a465-0668870979d6"],
   334                 "branch": null,
   335                 "timestamp": 1212662070767,
   336                 "directParent": "814f45c3-916d-4a51-8f91-f8c8ed24c598",
   337                 "additionalParent": null
   338             },
   339             "d092f83a-5e91-42aa-bd6e-d86e8aaeeffd": {
   340                 "alive": {
   341                     "d077723c-5169-45dc-a465-0668870979d6": "2f1dd5b6-271b-4adc-a3ea-cbd11588e95d"
   342                 },
   343                 "dead": {},
   344                 "changed": ["d077723c-5169-45dc-a465-0668870979d6"],
   345                 "branch": null,
   346                 "timestamp": 1212662073640,
   347                 "directParent": "ff9b6da5-b759-4c8c-8c5d-8497504f4529",
   348                 "additionalParent": null
   349             },
   350             "737f2636-9db0-4369-8521-a9b9d64d8fc6": {
   351                 "alive": {
   352                     "d077723c-5169-45dc-a465-0668870979d6": "6a28a74c-727b-4ea0-bda0-aa88c53c730c"
   353                 },
   354                 "dead": {},
   355                 "changed": ["d077723c-5169-45dc-a465-0668870979d6"],
   356                 "branch": null,
   357                 "timestamp": 1212662085012,
   358                 "directParent": "814f45c3-916d-4a51-8f91-f8c8ed24c598",
   359                 "additionalParent": null
   360             },
   361             "f5a7d71d-41d0-4d69-8434-c0fb68ba1bde": {
   362                 "alive": {
   363                     "d077723c-5169-45dc-a465-0668870979d6": "50a9df9e-e1c9-4138-a7d4-042d6b0e1e1d"
   364                 },
   365                 "dead": {},
   366                 "changed": ["d077723c-5169-45dc-a465-0668870979d6"],
   367                 "branch": null,
   368                 "timestamp": 1212662096545,
   369                 "directParent": "737f2636-9db0-4369-8521-a9b9d64d8fc6",
   370                 "additionalParent": "d092f83a-5e91-42aa-bd6e-d86e8aaeeffd"
   371             },
   372             "f06bc572-c234-4a71-980f-4a9d58bea46c": {
   373                 "alive": {
   374                     "d077723c-5169-45dc-a465-0668870979d6": "98a26661-3cc4-42bc-9cac-1d01dd64b7f3"
   375                 },
   376                 "dead": {},
   377                 "changed": ["d077723c-5169-45dc-a465-0668870979d6"],
   378                 "branch": null,
   379                 "timestamp": 1212662104627,
   380                 "directParent": "737f2636-9db0-4369-8521-a9b9d64d8fc6",
   381                 "additionalParent": null
   382             },
   383             "33b9c253-9378-4115-9cad-dfa0c81efc57": {
   384                 "alive": {
   385                     "d077723c-5169-45dc-a465-0668870979d6": "c8a089a0-abf3-4940-935f-6a039fdb32ed"
   386                 },
   387                 "dead": {},
   388                 "changed": ["d077723c-5169-45dc-a465-0668870979d6"],
   389                 "branch": null,
   390                 "timestamp": 1212662119727,
   391                 "directParent": "f06bc572-c234-4a71-980f-4a9d58bea46c",
   392                 "additionalParent": "d092f83a-5e91-42aa-bd6e-d86e8aaeeffd"
   393             },
   394             "554a6354-f6d5-4033-9fc5-b5b7a4cf5991": {
   395                 "alive": {
   396                     "d077723c-5169-45dc-a465-0668870979d6": "3e805399-d30b-4cd8-8e29-52d98650de77"
   397                 },
   398                 "dead": {},
   399                 "changed": ["d077723c-5169-45dc-a465-0668870979d6"],
   400                 "branch": null,
   401                 "timestamp": 1212662141759,
   402                 "directParent": "f5a7d71d-41d0-4d69-8434-c0fb68ba1bde",
   403                 "additionalParent": "f06bc572-c234-4a71-980f-4a9d58bea46c"
   404             }
   405         },
   406         "children": {
   407             "814f45c3-916d-4a51-8f91-f8c8ed24c598": [
   408                 "ff9b6da5-b759-4c8c-8c5d-8497504f4529",
   409                 "737f2636-9db0-4369-8521-a9b9d64d8fc6"
   410             ],
   411             "ff9b6da5-b759-4c8c-8c5d-8497504f4529": [
   412                 "d092f83a-5e91-42aa-bd6e-d86e8aaeeffd"
   413             ],
   414             "737f2636-9db0-4369-8521-a9b9d64d8fc6": [
   415                 "f5a7d71d-41d0-4d69-8434-c0fb68ba1bde",
   416                 "f06bc572-c234-4a71-980f-4a9d58bea46c"
   417             ],
   418             "d092f83a-5e91-42aa-bd6e-d86e8aaeeffd": [
   419                 "f5a7d71d-41d0-4d69-8434-c0fb68ba1bde",
   420                 "33b9c253-9378-4115-9cad-dfa0c81efc57"
   421             ],
   422             "f06bc572-c234-4a71-980f-4a9d58bea46c": [
   423                 "33b9c253-9378-4115-9cad-dfa0c81efc57",
   424                 "554a6354-f6d5-4033-9fc5-b5b7a4cf5991"
   425             ],
   426             "f5a7d71d-41d0-4d69-8434-c0fb68ba1bde": [
   427                 "554a6354-f6d5-4033-9fc5-b5b7a4cf5991"
   428             ]
   429         }
   430     };
   431 
   432     presets.clear();
   433     repo.importRevisions(repoExt);
   434     fs = repo.update(repo.branchTip(null)); // null -> default branch
   435 };
   436 
   437 function selectPreset() {
   438     selectPresetNamed(document.getElementById("presetSelect").value);
   439 }
   440 
   441 function selectPresetNamed(name) {
   442     presets[name]();
   443     redisplay_repository_history();
   444     sync_view_of_fs();
   445 }
   446 
   447 function log() {
   448     for (var i = 0; i < arguments.length; i++) {
   449         var arg = arguments[i];
   450         if (typeof(arg) == 'string') {
   451             document.getElementById("log").appendChild(document.createTextNode(arg + "\n"));
   452         } else {
   453             document.getElementById("log").appendChild(document .createTextNode(uneval(arg) + "\n"));
   454         }
   455     }
   456 }