Browse Source

updated libs

Gerald 12 years ago
parent
commit
4f2de64d1e

+ 2 - 2
common.js

@@ -13,8 +13,8 @@ function initCSS(){
 }
 }
 function initI18n(callback){
 function initI18n(callback){
 	window.addEventListener('DOMContentLoaded',function(){
 	window.addEventListener('DOMContentLoaded',function(){
-		var nodes=document.querySelectorAll('.i18n'),i,t;
-		for(i=0;i<nodes.length;i++) nodes[i].innerHTML=_(nodes[i].innerHTML);
+		var nodes=document.querySelectorAll('*[data-i18n]'),i,t;
+		for(i=0;i<nodes.length;i++) nodes[i].innerHTML=_(nodes[i].getAttribute('data-i18n'));
 		if(callback) callback();
 		if(callback) callback();
 	},true);
 	},true);
 }
 }

+ 7 - 7
confirm.html

@@ -5,21 +5,21 @@
 		<link rel="shortcut icon" type="image/png" href="images/icon16.png" />
 		<link rel="shortcut icon" type="image/png" href="images/icon16.png" />
 		<link rel="stylesheet" type="text/css" href="style.css" />
 		<link rel="stylesheet" type="text/css" href="style.css" />
 		<script type="text/javascript" src="load.js"></script>
 		<script type="text/javascript" src="load.js"></script>
-		<title class=i18n>extName</title>
+		<title data-i18n=extName></title>
 	</head>
 	</head>
-	<body class="fill hide">
+	<body class=fill>
 		<table class=frame>
 		<table class=frame>
 			<tr>
 			<tr>
-				<td><h2><span class=i18n>labelInstall</span> - <span class=i18n>extName</span></h2></td>
+				<td><h2><span data-i18n=labelInstall></span> - <span data-i18n=extName></span></h2></td>
 				<td class=buttons>
 				<td class=buttons>
-					<input type=checkbox id=cClose><label for=cClose class=i18n>optionClose</label>
-					<button class=i18n id=bInstall disabled=disabled>buttonConfirmInstallation</button>
-					<button class=i18n id=bClose>buttonClose</button>
+					<input type=checkbox id=cClose><label for=cClose data-i18n=optionClose></label>
+					<button id=bInstall disabled=disabled data-i18n=buttonConfirmInstallation></button>
+					<button id=bClose data-i18n=buttonClose></button>
 				</td>
 				</td>
 			</tr>
 			</tr>
 			<tr><td colspan=2 id=msg>&nbsp;</td></tr>
 			<tr><td colspan=2 id=msg>&nbsp;</td></tr>
 			<tr class=expand><td id=eCode colspan=2 class=expandr></td></tr>
 			<tr class=expand><td id=eCode colspan=2 class=expandr></td></tr>
-			<tr><td colspan=2 class=center><span class=i18n>anchorSupportPage</span> - <span class=i18n>anchorAuthor</span> - 2013</td></tr>
+			<tr><td colspan=2 class=center><span data-i18n=anchorSupportPage></span> - <span data-i18n=anchorAuthor></span> - 2013</td></tr>
 		</table>
 		</table>
 	</body>
 	</body>
 </html>
 </html>

+ 1 - 1
confirm.js

@@ -47,4 +47,4 @@ initEditor(function(o){
 		x.send();
 		x.send();
 	}
 	}
 },{exit:B.onclick,readonly:true});
 },{exit:B.onclick,readonly:true});
-initCSS();initI18n(function(){document.body.classList.remove('hide');});
+initCSS();initI18n();

+ 2 - 1
lib/CodeMirror/addon/edit/matchbrackets.js

@@ -8,6 +8,7 @@
   function findMatchingBracket(cm, where, strict) {
   function findMatchingBracket(cm, where, strict) {
     var state = cm.state.matchBrackets;
     var state = cm.state.matchBrackets;
     var maxScanLen = (state && state.maxScanLineLength) || 10000;
     var maxScanLen = (state && state.maxScanLineLength) || 10000;
+    var maxScanLines = (state && state.maxScanLines) || 100;
 
 
     var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
     var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
     var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
     var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
@@ -32,7 +33,7 @@
         }
         }
       }
       }
     }
     }
-    for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
+    for (var i = cur.line, found, e = forward ? Math.min(i + maxScanLines, cm.lineCount()) : Math.max(-1, i - maxScanLines); i != e; i+=d) {
       if (i == cur.line) found = scan(line, i, pos);
       if (i == cur.line) found = scan(line, i, pos);
       else found = scan(cm.getLineHandle(i), i);
       else found = scan(cm.getLineHandle(i), i);
       if (found) break;
       if (found) break;

+ 6 - 4
lib/CodeMirror/addon/fold/foldcode.js

@@ -1,7 +1,7 @@
 (function() {
 (function() {
   "use strict";
   "use strict";
 
 
-  function doFold(cm, pos, options) {
+  function doFold(cm, pos, options, force) {
     var finder = options && (options.call ? options : options.rangeFinder);
     var finder = options && (options.call ? options : options.rangeFinder);
     if (!finder) finder = cm.getHelper(pos, "fold");
     if (!finder) finder = cm.getHelper(pos, "fold");
     if (!finder) return;
     if (!finder) return;
@@ -13,7 +13,7 @@
       if (!range || range.to.line - range.from.line < minSize) return null;
       if (!range || range.to.line - range.from.line < minSize) return null;
       var marks = cm.findMarksAt(range.from);
       var marks = cm.findMarksAt(range.from);
       for (var i = 0; i < marks.length; ++i) {
       for (var i = 0; i < marks.length; ++i) {
-        if (marks[i].__isFold) {
+        if (marks[i].__isFold && force !== "fold") {
           if (!allowFolded) return null;
           if (!allowFolded) return null;
           range.cleared = true;
           range.cleared = true;
           marks[i].clear();
           marks[i].clear();
@@ -27,7 +27,7 @@
       pos = CodeMirror.Pos(pos.line - 1, 0);
       pos = CodeMirror.Pos(pos.line - 1, 0);
       range = getRange(false);
       range = getRange(false);
     }
     }
-    if (!range || range.cleared) return;
+    if (!range || range.cleared || force === "unfold") return;
 
 
     var myWidget = makeWidget(options);
     var myWidget = makeWidget(options);
     CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); });
     CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); });
@@ -59,7 +59,9 @@
   };
   };
 
 
   // New-style interface
   // New-style interface
-  CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); });
+  CodeMirror.defineExtension("foldCode", function(pos, options, force) {
+    doFold(this, pos, options, force);
+  });
 
 
   CodeMirror.registerHelper("fold", "combine", function() {
   CodeMirror.registerHelper("fold", "combine", function() {
     var funcs = Array.prototype.slice.call(arguments, 0);
     var funcs = Array.prototype.slice.call(arguments, 0);

+ 6 - 4
lib/CodeMirror/addon/fold/foldgutter.js

@@ -10,6 +10,7 @@
       cm.off("viewportChange", onViewportChange);
       cm.off("viewportChange", onViewportChange);
       cm.off("fold", onFold);
       cm.off("fold", onFold);
       cm.off("unfold", onFold);
       cm.off("unfold", onFold);
+      cm.off("swapDoc", updateInViewport);
     }
     }
     if (val) {
     if (val) {
       cm.state.foldGutter = new State(parseOptions(val));
       cm.state.foldGutter = new State(parseOptions(val));
@@ -19,6 +20,7 @@
       cm.on("viewportChange", onViewportChange);
       cm.on("viewportChange", onViewportChange);
       cm.on("fold", onFold);
       cm.on("fold", onFold);
       cm.on("unfold", onFold);
       cm.on("unfold", onFold);
+      cm.on("swapDoc", updateInViewport);
     }
     }
   });
   });
 
 
@@ -86,14 +88,14 @@
   }
   }
 
 
   function onChange(cm) {
   function onChange(cm) {
-    var state = cm.state.foldGutter;
+    var state = cm.state.foldGutter, opts = cm.state.foldGutter.options;
     state.from = state.to = 0;
     state.from = state.to = 0;
     clearTimeout(state.changeUpdate);
     clearTimeout(state.changeUpdate);
-    state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, 600);
+    state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
   }
   }
 
 
   function onViewportChange(cm) {
   function onViewportChange(cm) {
-    var state = cm.state.foldGutter;
+    var state = cm.state.foldGutter, opts = cm.state.foldGutter.options;
     clearTimeout(state.changeUpdate);
     clearTimeout(state.changeUpdate);
     state.changeUpdate = setTimeout(function() {
     state.changeUpdate = setTimeout(function() {
       var vp = cm.getViewport();
       var vp = cm.getViewport();
@@ -111,7 +113,7 @@
           }
           }
         });
         });
       }
       }
-    }, 400);
+    }, opts.updateViewportTimeSpan || 400);
   }
   }
 
 
   function onFold(cm, from) {
   function onFold(cm, from) {

+ 48 - 24
lib/CodeMirror/addon/search/searchcursor.js

@@ -47,6 +47,7 @@
                   match: match};
                   match: match};
       };
       };
     } else { // String query
     } else { // String query
+      var origQuery = query;
       if (caseFold) query = query.toLowerCase();
       if (caseFold) query = query.toLowerCase();
       var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
       var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
       var target = query.split("\n");
       var target = query.split("\n");
@@ -58,33 +59,45 @@
           this.matches = function() {};
           this.matches = function() {};
         } else {
         } else {
           this.matches = function(reverse, pos) {
           this.matches = function(reverse, pos) {
-            var line = fold(doc.getLine(pos.line)), len = query.length, match;
-            if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
-                        : (match = line.indexOf(query, pos.ch)) != -1)
-              return {from: Pos(pos.line, match),
-                      to: Pos(pos.line, match + len)};
+            if (reverse) {
+              var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig);
+              var match = line.lastIndexOf(query);
+              if (match > -1) {
+                match = adjustPos(orig, line, match);
+                return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
+              }
+             } else {
+               var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig);
+               var match = line.indexOf(query);
+               if (match > -1) {
+                 match = adjustPos(orig, line, match) + pos.ch;
+                 return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
+               }
+            }
           };
           };
         }
         }
       } else {
       } else {
+        var origTarget = origQuery.split("\n");
         this.matches = function(reverse, pos) {
         this.matches = function(reverse, pos) {
-          var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln));
-          var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
-          if (reverse ? offsetA > pos.ch || offsetA != match.length
-              : offsetA < pos.ch || offsetA != line.length - match.length)
-            return;
-          for (;;) {
-            if (reverse ? !ln : ln == doc.lineCount() - 1) return;
-            line = fold(doc.getLine(ln += reverse ? -1 : 1));
-            match = target[reverse ? --idx : ++idx];
-            if (idx > 0 && idx < target.length - 1) {
-              if (line != match) return;
-              else continue;
-            }
-            var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
-            if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
-              return;
-            var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
-            return {from: reverse ? end : start, to: reverse ? start : end};
+          var last = target.length - 1;
+          if (reverse) {
+            if (pos.line - (target.length - 1) < doc.firstLine()) return;
+            if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return;
+            var to = Pos(pos.line, origTarget[last].length);
+            for (var ln = pos.line - 1, i = last - 1; i >= 1; --i, --ln)
+              if (target[i] != fold(doc.getLine(ln))) return;
+            var line = doc.getLine(ln), cut = line.length - origTarget[0].length;
+            if (fold(line.slice(cut)) != target[0]) return;
+            return {from: Pos(ln, cut), to: to};
+          } else {
+            if (pos.line + (target.length - 1) > doc.lastLine()) return;
+            var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length;
+            if (fold(line.slice(cut)) != target[0]) return;
+            var from = Pos(pos.line, cut);
+            for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln)
+              if (target[i] != fold(doc.getLine(ln))) return;
+            if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return;
+            return {from: from, to: Pos(ln, origTarget[last].length)};
           }
           }
         };
         };
       }
       }
@@ -106,7 +119,6 @@
 
 
       for (;;) {
       for (;;) {
         if (this.pos = this.matches(reverse, pos)) {
         if (this.pos = this.matches(reverse, pos)) {
-          if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
           this.atOccurrence = true;
           this.atOccurrence = true;
           return this.pos.match || true;
           return this.pos.match || true;
         }
         }
@@ -134,6 +146,18 @@
     }
     }
   };
   };
 
 
+  // Maps a position in a case-folded line back to a position in the original line
+  // (compensating for codepoints increasing in number during folding)
+  function adjustPos(orig, folded, pos) {
+    if (orig.length == folded.length) return pos;
+    for (var pos1 = Math.min(pos, orig.length);;) {
+      var len1 = orig.slice(0, pos1).toLowerCase().length;
+      if (len1 < pos) ++pos1;
+      else if (len1 > pos) --pos1;
+      else return pos1;
+    }
+  }
+
   CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
   CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
     return new SearchCursor(this.doc, query, pos, caseFold);
     return new SearchCursor(this.doc, query, pos, caseFold);
   });
   });

+ 15 - 9
lib/CodeMirror/addon/selection/active-line.js

@@ -12,10 +12,10 @@
   CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
   CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
     var prev = old && old != CodeMirror.Init;
     var prev = old && old != CodeMirror.Init;
     if (val && !prev) {
     if (val && !prev) {
-      updateActiveLine(cm);
-      cm.on("cursorActivity", updateActiveLine);
+      updateActiveLine(cm, cm.getCursor().line);
+      cm.on("beforeSelectionChange", selectionChange);
     } else if (!val && prev) {
     } else if (!val && prev) {
-      cm.off("cursorActivity", updateActiveLine);
+      cm.off("beforeSelecionChange", selectionChange);
       clearActiveLine(cm);
       clearActiveLine(cm);
       delete cm.state.activeLine;
       delete cm.state.activeLine;
     }
     }
@@ -28,12 +28,18 @@
     }
     }
   }
   }
 
 
-  function updateActiveLine(cm) {
-    var line = cm.getLineHandleVisualStart(cm.getCursor().line);
+  function updateActiveLine(cm, selectedLine) {
+    var line = cm.getLineHandleVisualStart(selectedLine);
     if (cm.state.activeLine == line) return;
     if (cm.state.activeLine == line) return;
-    clearActiveLine(cm);
-    cm.addLineClass(line, "wrap", WRAP_CLASS);
-    cm.addLineClass(line, "background", BACK_CLASS);
-    cm.state.activeLine = line;
+    cm.operation(function() {
+      clearActiveLine(cm);
+      cm.addLineClass(line, "wrap", WRAP_CLASS);
+      cm.addLineClass(line, "background", BACK_CLASS);
+      cm.state.activeLine = line;
+    });
+  }
+
+  function selectionChange(cm, sel) {
+    updateActiveLine(cm, sel.head.line);
   }
   }
 })();
 })();

+ 141 - 70
lib/CodeMirror/lib/codemirror.js

@@ -7,9 +7,13 @@ window.CodeMirror = (function() {
   // Crude, but necessary to handle a number of hard-to-feature-detect
   // Crude, but necessary to handle a number of hard-to-feature-detect
   // bugs and behavior differences.
   // bugs and behavior differences.
   var gecko = /gecko\/\d/i.test(navigator.userAgent);
   var gecko = /gecko\/\d/i.test(navigator.userAgent);
+  // IE11 currently doesn't count as 'ie', since it has almost none of
+  // the same bugs as earlier versions. Use ie_gt10 to handle
+  // incompatibilities in that version.
   var ie = /MSIE \d/.test(navigator.userAgent);
   var ie = /MSIE \d/.test(navigator.userAgent);
   var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
   var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
   var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
   var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
+  var ie_gt10 = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent);
   var webkit = /WebKit\//.test(navigator.userAgent);
   var webkit = /WebKit\//.test(navigator.userAgent);
   var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
   var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
   var chrome = /Chrome\//.test(navigator.userAgent);
   var chrome = /Chrome\//.test(navigator.userAgent);
@@ -353,8 +357,10 @@ window.CodeMirror = (function() {
       d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
       d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
     } else d.gutterFiller.style.display = "";
     } else d.gutterFiller.style.display = "";
 
 
-    if (mac_geLion && scrollbarWidth(d.measure) === 0)
+    if (mac_geLion && scrollbarWidth(d.measure) === 0) {
       d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
       d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
+      d.scrollbarV.style.pointerEvents = d.scrollbarH.style.pointerEvents = "none";
+    }
   }
   }
 
 
   function visibleLines(display, doc, viewPort) {
   function visibleLines(display, doc, viewPort) {
@@ -529,6 +535,7 @@ window.CodeMirror = (function() {
     }
     }
     display.showingFrom = from; display.showingTo = to;
     display.showingFrom = from; display.showingTo = to;
 
 
+    display.gutters.style.height = "";
     updateHeightsInViewport(cm);
     updateHeightsInViewport(cm);
     updateViewOffset(cm);
     updateViewOffset(cm);
 
 
@@ -711,9 +718,9 @@ window.CodeMirror = (function() {
     if (bgClass)
     if (bgClass)
       wrap.insertBefore(elt("div", null, bgClass + " CodeMirror-linebackground"), wrap.firstChild);
       wrap.insertBefore(elt("div", null, bgClass + " CodeMirror-linebackground"), wrap.firstChild);
     if (cm.options.lineNumbers || markers) {
     if (cm.options.lineNumbers || markers) {
-      var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
+      var gutterWrap = wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "position: absolute; left: " +
                                              (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
                                              (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
-                                         wrap.firstChild);
+                                         lineElement);
       if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
       if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
       if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
       if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
         wrap.lineNumber = gutterWrap.appendChild(
         wrap.lineNumber = gutterWrap.appendChild(
@@ -904,7 +911,7 @@ window.CodeMirror = (function() {
     doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
     doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
       if (doc.frontier >= cm.display.showingFrom) { // Visible
       if (doc.frontier >= cm.display.showingFrom) { // Visible
         var oldStyles = line.styles;
         var oldStyles = line.styles;
-        line.styles = highlightLine(cm, line, state);
+        line.styles = highlightLine(cm, line, state, true);
         var ischange = !oldStyles || oldStyles.length != line.styles.length;
         var ischange = !oldStyles || oldStyles.length != line.styles.length;
         for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
         for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
         if (ischange) {
         if (ischange) {
@@ -913,7 +920,7 @@ window.CodeMirror = (function() {
         }
         }
         line.stateAfter = copyState(doc.mode, state);
         line.stateAfter = copyState(doc.mode, state);
       } else {
       } else {
-        processLine(cm, line, state);
+        processLine(cm, line.text, state);
         line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
         line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
       }
       }
       ++doc.frontier;
       ++doc.frontier;
@@ -935,8 +942,9 @@ window.CodeMirror = (function() {
   // smallest indentation, which tends to need the least context to
   // smallest indentation, which tends to need the least context to
   // parse correctly.
   // parse correctly.
   function findStartLine(cm, n, precise) {
   function findStartLine(cm, n, precise) {
-    var minindent, minline, doc = cm.doc, maxScan = cm.doc.mode.innerMode ? 1000 : 100;
-    for (var search = n, lim = n - maxScan; search > lim; --search) {
+    var minindent, minline, doc = cm.doc;
+    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
+    for (var search = n; search > lim; --search) {
       if (search <= doc.first) return doc.first;
       if (search <= doc.first) return doc.first;
       var line = getLine(doc, search - 1);
       var line = getLine(doc, search - 1);
       if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
       if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
@@ -956,11 +964,12 @@ window.CodeMirror = (function() {
     if (!state) state = startState(doc.mode);
     if (!state) state = startState(doc.mode);
     else state = copyState(doc.mode, state);
     else state = copyState(doc.mode, state);
     doc.iter(pos, n, function(line) {
     doc.iter(pos, n, function(line) {
-      processLine(cm, line, state);
+      processLine(cm, line.text, state);
       var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
       var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
       line.stateAfter = save ? copyState(doc.mode, state) : null;
       line.stateAfter = save ? copyState(doc.mode, state) : null;
       ++pos;
       ++pos;
     });
     });
+    if (precise) doc.frontier = pos;
     return state;
     return state;
   }
   }
 
 
@@ -1105,7 +1114,7 @@ window.CodeMirror = (function() {
         }
         }
       }
       }
       if (!rect) rect = data[i] = measureRect(getRect(node));
       if (!rect) rect = data[i] = measureRect(getRect(node));
-      if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
+      if (cur.measureRight) rect.right = getRect(cur.measureRight).left - outer.left;
       if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
       if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
     }
     }
     removeChildren(cm.display.measure);
     removeChildren(cm.display.measure);
@@ -1379,11 +1388,14 @@ window.CodeMirror = (function() {
     }
     }
     if (!updated && op.selectionChanged) updateSelection(cm);
     if (!updated && op.selectionChanged) updateSelection(cm);
     if (op.updateScrollPos) {
     if (op.updateScrollPos) {
-      display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
-      display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
+      var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, newScrollPos.scrollTop));
+      var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, newScrollPos.scrollLeft));
+      display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
+      display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left;
       alignHorizontally(cm);
       alignHorizontally(cm);
       if (op.scrollToPos)
       if (op.scrollToPos)
-        scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
+        scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
+                          clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
     } else if (newScrollPos) {
     } else if (newScrollPos) {
       scrollCursorIntoView(cm);
       scrollCursorIntoView(cm);
     }
     }
@@ -1898,7 +1910,6 @@ window.CodeMirror = (function() {
           if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
           if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
           cm.replaceSelection(text, null, "paste");
           cm.replaceSelection(text, null, "paste");
           focusInput(cm);
           focusInput(cm);
-          onFocus(cm);
         }
         }
       }
       }
       catch(e){}
       catch(e){}
@@ -2179,13 +2190,17 @@ window.CodeMirror = (function() {
 
 
     var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
     var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
     if (!pos || opera) return; // Opera is difficult.
     if (!pos || opera) return; // Opera is difficult.
-    if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
+
+    // Reset the current text selection only if the click is done outside of the selection
+    // and 'resetSelectionOnContextMenu' option is true.
+    var reset = cm.options.resetSelectionOnContextMenu;
+    if (reset && (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)))
       operation(cm, setSelection)(cm.doc, pos, pos);
       operation(cm, setSelection)(cm.doc, pos, pos);
 
 
     var oldCSS = display.input.style.cssText;
     var oldCSS = display.input.style.cssText;
     display.inputDiv.style.position = "absolute";
     display.inputDiv.style.position = "absolute";
     display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
     display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
-      "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
+      "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: transparent; outline: none;" +
       "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
       "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
     focusInput(cm);
     focusInput(cm);
     resetInput(cm, true);
     resetInput(cm, true);
@@ -2210,7 +2225,7 @@ window.CodeMirror = (function() {
         if (!ie || ie_lt9) prepareSelectAllHack();
         if (!ie || ie_lt9) prepareSelectAllHack();
         clearTimeout(detectingSelectAll);
         clearTimeout(detectingSelectAll);
         var i = 0, poll = function(){
         var i = 0, poll = function(){
-          if (display.prevInput == " " && display.input.selectionStart == 0)
+          if (display.prevInput == "\u200b" && display.input.selectionStart == 0)
             operation(cm, commands.selectAll)(cm);
             operation(cm, commands.selectAll)(cm);
           else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
           else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
           else resetInput(cm);
           else resetInput(cm);
@@ -2630,7 +2645,7 @@ window.CodeMirror = (function() {
   // SCROLLING
   // SCROLLING
 
 
   function scrollCursorIntoView(cm) {
   function scrollCursorIntoView(cm) {
-    var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
+    var coords = scrollPosIntoView(cm, cm.doc.sel.head, null, cm.options.cursorScrollMargin);
     if (!cm.state.focused) return;
     if (!cm.state.focused) return;
     var display = cm.display, box = getRect(display.sizer), doScroll = null;
     var display = cm.display, box = getRect(display.sizer), doScroll = null;
     if (coords.top + box.top < 0) doScroll = true;
     if (coords.top + box.top < 0) doScroll = true;
@@ -2647,11 +2662,15 @@ window.CodeMirror = (function() {
     }
     }
   }
   }
 
 
-  function scrollPosIntoView(cm, pos, margin) {
+  function scrollPosIntoView(cm, pos, end, margin) {
     if (margin == null) margin = 0;
     if (margin == null) margin = 0;
     for (;;) {
     for (;;) {
       var changed = false, coords = cursorCoords(cm, pos);
       var changed = false, coords = cursorCoords(cm, pos);
-      var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
+      var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
+      var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
+                                         Math.min(coords.top, endCoords.top) - margin,
+                                         Math.max(coords.left, endCoords.left),
+                                         Math.max(coords.bottom, endCoords.bottom) + margin);
       var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
       var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
       if (scrollPos.scrollTop != null) {
       if (scrollPos.scrollTop != null) {
         setScrollTop(cm, scrollPos.scrollTop);
         setScrollTop(cm, scrollPos.scrollTop);
@@ -2748,6 +2767,8 @@ window.CodeMirror = (function() {
 
 
     if (indentString != curSpaceString)
     if (indentString != curSpaceString)
       replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
       replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
+    else if (doc.sel.head.line == n && doc.sel.head.ch < curSpaceString.length)
+      setSelection(doc, Pos(n, curSpaceString.length), Pos(n, curSpaceString.length), 1);
     line.stateAfter = null;
     line.stateAfter = null;
   }
   }
 
 
@@ -2848,7 +2869,7 @@ window.CodeMirror = (function() {
 
 
   CodeMirror.prototype = {
   CodeMirror.prototype = {
     constructor: CodeMirror,
     constructor: CodeMirror,
-    focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
+    focus: function(){window.focus(); focusInput(this); fastPoll(this);},
 
 
     setOption: function(option, value) {
     setOption: function(option, value) {
       var options = this.options, old = options[option];
       var options = this.options, old = options[option];
@@ -3169,17 +3190,23 @@ window.CodeMirror = (function() {
               clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
               clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
     },
     },
 
 
-    scrollIntoView: operation(null, function(pos, margin) {
-      if (typeof pos == "number") pos = Pos(pos, 0);
+    scrollIntoView: operation(null, function(range, margin) {
+      if (range == null) range = {from: this.doc.sel.head, to: null};
+      else if (typeof range == "number") range = {from: Pos(range, 0), to: null};
+      else if (range.from == null) range = {from: range, to: null};
+      if (!range.to) range.to = range.from;
       if (!margin) margin = 0;
       if (!margin) margin = 0;
-      var coords = pos;
 
 
-      if (!pos || pos.line != null) {
-        this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
-        this.curOp.scrollToPosMargin = margin;
-        coords = cursorCoords(this, this.curOp.scrollToPos);
+      var coords = range;
+      if (range.from.line != null) {
+        this.curOp.scrollToPos = {from: range.from, to: range.to, margin: margin};
+        coords = {from: cursorCoords(this, range.from),
+                  to: cursorCoords(this, range.to)};
       }
       }
-      var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
+      var sPos = calculateScrollPos(this, Math.min(coords.from.left, coords.to.left),
+                                    Math.min(coords.from.top, coords.to.top) - margin,
+                                    Math.max(coords.from.right, coords.to.right),
+                                    Math.max(coords.from.bottom, coords.to.bottom) + margin);
       updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
       updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
     }),
     }),
 
 
@@ -3211,6 +3238,7 @@ window.CodeMirror = (function() {
       clearCaches(this);
       clearCaches(this);
       resetInput(this, true);
       resetInput(this, true);
       updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
       updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
+      signalLater(this, "swapDoc", this, old);
       return old;
       return old;
     }),
     }),
 
 
@@ -3254,8 +3282,14 @@ window.CodeMirror = (function() {
     clearCaches(cm);
     clearCaches(cm);
     regChange(cm);
     regChange(cm);
   }, true);
   }, true);
+  option("specialChars", /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/g, function(cm, val) {
+    cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
+    cm.refresh();
+  }, true);
+  option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
   option("electricChars", true);
   option("electricChars", true);
   option("rtlMoveVisually", !windows);
   option("rtlMoveVisually", !windows);
+  option("wholeLineUpdateBefore", true);
 
 
   option("theme", "default", function(cm) {
   option("theme", "default", function(cm) {
     themeChanged(cm);
     themeChanged(cm);
@@ -3285,9 +3319,17 @@ window.CodeMirror = (function() {
   option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
   option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
   option("showCursorWhenSelecting", false, updateSelection, true);
   option("showCursorWhenSelecting", false, updateSelection, true);
 
 
+  option("resetSelectionOnContextMenu", true);
+
   option("readOnly", false, function(cm, val) {
   option("readOnly", false, function(cm, val) {
-    if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
-    else if (!val) resetInput(cm, true);
+    if (val == "nocursor") {
+      onBlur(cm);
+      cm.display.input.blur();
+      cm.display.disabled = true;
+    } else {
+      cm.display.disabled = false;
+      if (!val) resetInput(cm, true);
+    }
   });
   });
   option("dragDrop", true);
   option("dragDrop", true);
 
 
@@ -3668,11 +3710,12 @@ window.CodeMirror = (function() {
     this.string = string;
     this.string = string;
     this.tabSize = tabSize || 8;
     this.tabSize = tabSize || 8;
     this.lastColumnPos = this.lastColumnValue = 0;
     this.lastColumnPos = this.lastColumnValue = 0;
+    this.lineStart = 0;
   }
   }
 
 
   StringStream.prototype = {
   StringStream.prototype = {
     eol: function() {return this.pos >= this.string.length;},
     eol: function() {return this.pos >= this.string.length;},
-    sol: function() {return this.pos == 0;},
+    sol: function() {return this.pos == this.lineStart;},
     peek: function() {return this.string.charAt(this.pos) || undefined;},
     peek: function() {return this.string.charAt(this.pos) || undefined;},
     next: function() {
     next: function() {
       if (this.pos < this.string.length)
       if (this.pos < this.string.length)
@@ -3705,9 +3748,12 @@ window.CodeMirror = (function() {
         this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
         this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
         this.lastColumnPos = this.start;
         this.lastColumnPos = this.start;
       }
       }
-      return this.lastColumnValue;
+      return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
+    },
+    indentation: function() {
+      return countColumn(this.string, null, this.tabSize) -
+        (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
     },
     },
-    indentation: function() {return countColumn(this.string, null, this.tabSize);},
     match: function(pattern, consume, caseInsensitive) {
     match: function(pattern, consume, caseInsensitive) {
       if (typeof pattern == "string") {
       if (typeof pattern == "string") {
         var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
         var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
@@ -3723,7 +3769,12 @@ window.CodeMirror = (function() {
         return match;
         return match;
       }
       }
     },
     },
-    current: function(){return this.string.slice(this.start, this.pos);}
+    current: function(){return this.string.slice(this.start, this.pos);},
+    hideFirstChars: function(n, inner) {
+      this.lineStart += n;
+      try { return inner(); }
+      finally { this.lineStart -= n; }
+    }
   };
   };
   CodeMirror.StringStream = StringStream;
   CodeMirror.StringStream = StringStream;
 
 
@@ -3828,8 +3879,9 @@ window.CodeMirror = (function() {
     if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
     if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
 
 
     var marker = new TextMarker(doc, type);
     var marker = new TextMarker(doc, type);
-    if (type == "range" && !posLess(from, to)) return marker;
     if (options) copyObj(options, marker);
     if (options) copyObj(options, marker);
+    if (posLess(to, from) || posEq(from, to) && marker.clearWhenEmpty !== false)
+      return marker;
     if (marker.replacedWith) {
     if (marker.replacedWith) {
       marker.collapsed = true;
       marker.collapsed = true;
       marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
       marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
@@ -3944,7 +3996,7 @@ window.CodeMirror = (function() {
     if (old) for (var i = 0, nw; i < old.length; ++i) {
     if (old) for (var i = 0, nw; i < old.length; ++i) {
       var span = old[i], marker = span.marker;
       var span = old[i], marker = span.marker;
       var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
       var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
-      if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
+      if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
         var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
         var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
         (nw || (nw = [])).push({from: span.from,
         (nw || (nw = [])).push({from: span.from,
                                 to: endsAfter ? null : span.to,
                                 to: endsAfter ? null : span.to,
@@ -3958,7 +4010,7 @@ window.CodeMirror = (function() {
     if (old) for (var i = 0, nw; i < old.length; ++i) {
     if (old) for (var i = 0, nw; i < old.length; ++i) {
       var span = old[i], marker = span.marker;
       var span = old[i], marker = span.marker;
       var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
       var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
-      if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
+      if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
         var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
         var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
         (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
         (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
                                 to: span.to == null ? null : span.to - endCh,
                                 to: span.to == null ? null : span.to - endCh,
@@ -4008,13 +4060,9 @@ window.CodeMirror = (function() {
         }
         }
       }
       }
     }
     }
-    if (sameLine && first) {
-      // Make sure we didn't create any zero-length spans
-      for (var i = 0; i < first.length; ++i)
-        if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
-          first.splice(i--, 1);
-      if (!first.length) first = null;
-    }
+    // Make sure we didn't create any zero-length spans
+    if (first) first = clearEmptySpans(first);
+    if (last && last != first) last = clearEmptySpans(last);
 
 
     var newMarkers = [first];
     var newMarkers = [first];
     if (!sameLine) {
     if (!sameLine) {
@@ -4031,6 +4079,16 @@ window.CodeMirror = (function() {
     return newMarkers;
     return newMarkers;
   }
   }
 
 
+  function clearEmptySpans(spans) {
+    for (var i = 0; i < spans.length; ++i) {
+      var span = spans[i];
+      if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
+        spans.splice(i--, 1);
+    }
+    if (!spans.length) return null;
+    return spans;
+  }
+
   function mergeOldSpans(doc, change) {
   function mergeOldSpans(doc, change) {
     var old = getOldSpans(doc, change);
     var old = getOldSpans(doc, change);
     var stretched = stretchSpansOverChange(doc, change);
     var stretched = stretchSpansOverChange(doc, change);
@@ -4124,6 +4182,7 @@ window.CodeMirror = (function() {
     for (var sp, i = 0; i < line.markedSpans.length; ++i) {
     for (var sp, i = 0; i < line.markedSpans.length; ++i) {
       sp = line.markedSpans[i];
       sp = line.markedSpans[i];
       if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
       if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
+          (sp.to == null || sp.to != span.from) &&
           (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
           (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
           lineIsHiddenInner(doc, line, sp)) return true;
           lineIsHiddenInner(doc, line, sp)) return true;
     }
     }
@@ -4217,6 +4276,7 @@ window.CodeMirror = (function() {
     this.height = estimateHeight ? estimateHeight(this) : 1;
     this.height = estimateHeight ? estimateHeight(this) : 1;
   };
   };
   eventMixin(Line);
   eventMixin(Line);
+  Line.prototype.lineNo = function() { return lineNo(this); };
 
 
   function updateLine(line, text, markedSpans, estimateHeight) {
   function updateLine(line, text, markedSpans, estimateHeight) {
     line.text = text;
     line.text = text;
@@ -4237,7 +4297,7 @@ window.CodeMirror = (function() {
   // Run the given mode's parser over a line, update the styles
   // Run the given mode's parser over a line, update the styles
   // array, which contains alternating fragments of text and CSS
   // array, which contains alternating fragments of text and CSS
   // classes.
   // classes.
-  function runMode(cm, text, mode, state, f) {
+  function runMode(cm, text, mode, state, f, forceToEnd) {
     var flattenSpans = mode.flattenSpans;
     var flattenSpans = mode.flattenSpans;
     if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
     if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
     var curStart = 0, curStyle = null;
     var curStart = 0, curStyle = null;
@@ -4246,6 +4306,7 @@ window.CodeMirror = (function() {
     while (!stream.eol()) {
     while (!stream.eol()) {
       if (stream.pos > cm.options.maxHighlightLength) {
       if (stream.pos > cm.options.maxHighlightLength) {
         flattenSpans = false;
         flattenSpans = false;
+        if (forceToEnd) processLine(cm, text, state, stream.pos);
         stream.pos = text.length;
         stream.pos = text.length;
         style = null;
         style = null;
       } else {
       } else {
@@ -4265,12 +4326,14 @@ window.CodeMirror = (function() {
     }
     }
   }
   }
 
 
-  function highlightLine(cm, line, state) {
+  function highlightLine(cm, line, state, forceToEnd) {
     // A styles array always starts with a number identifying the
     // A styles array always starts with a number identifying the
     // mode/overlays that it is based on (for easy invalidation).
     // mode/overlays that it is based on (for easy invalidation).
     var st = [cm.state.modeGen];
     var st = [cm.state.modeGen];
     // Compute the base array of styles
     // Compute the base array of styles
-    runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
+    runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
+      st.push(end, style);
+    }, forceToEnd);
 
 
     // Run overlays, adjust style array.
     // Run overlays, adjust style array.
     for (var o = 0; o < cm.state.overlays.length; ++o) {
     for (var o = 0; o < cm.state.overlays.length; ++o) {
@@ -4309,10 +4372,11 @@ window.CodeMirror = (function() {
 
 
   // Lightweight form of highlight -- proceed over this line and
   // Lightweight form of highlight -- proceed over this line and
   // update state, but don't save a style array.
   // update state, but don't save a style array.
-  function processLine(cm, line, state) {
+  function processLine(cm, text, state, startAt) {
     var mode = cm.doc.mode;
     var mode = cm.doc.mode;
-    var stream = new StringStream(line.text, cm.options.tabSize);
-    if (line.text == "" && mode.blankLine) mode.blankLine(state);
+    var stream = new StringStream(text, cm.options.tabSize);
+    stream.start = stream.pos = startAt || 0;
+    if (text == "" && mode.blankLine) mode.blankLine(state);
     while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
     while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
       mode.token(stream, state);
       mode.token(stream, state);
       stream.start = stream.pos;
       stream.start = stream.pos;
@@ -4369,7 +4433,7 @@ window.CodeMirror = (function() {
     // Work around problem with the reported dimensions of single-char
     // Work around problem with the reported dimensions of single-char
     // direction spans on IE (issue #1129). See also the comment in
     // direction spans on IE (issue #1129). See also the comment in
     // cursorCoords.
     // cursorCoords.
-    if (measure && ie && (order = getOrder(line))) {
+    if (measure && (ie || ie_gt10) && (order = getOrder(line))) {
       var l = order.length - 1;
       var l = order.length - 1;
       if (order[l].from == order[l].to) --l;
       if (order[l].from == order[l].to) --l;
       var last = order[l], prev = order[l - 1];
       var last = order[l], prev = order[l - 1];
@@ -4387,17 +4451,23 @@ window.CodeMirror = (function() {
     return builder;
     return builder;
   }
   }
 
 
-  var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
+  function defaultSpecialCharPlaceholder(ch) {
+    var token = elt("span", "\u2022", "cm-invalidchar");
+    token.title = "\\u" + ch.charCodeAt(0).toString(16);
+    return token;
+  }
+
   function buildToken(builder, text, style, startStyle, endStyle, title) {
   function buildToken(builder, text, style, startStyle, endStyle, title) {
     if (!text) return;
     if (!text) return;
-    if (!tokenSpecialChars.test(text)) {
+    var special = builder.cm.options.specialChars;
+    if (!special.test(text)) {
       builder.col += text.length;
       builder.col += text.length;
       var content = document.createTextNode(text);
       var content = document.createTextNode(text);
     } else {
     } else {
       var content = document.createDocumentFragment(), pos = 0;
       var content = document.createDocumentFragment(), pos = 0;
       while (true) {
       while (true) {
-        tokenSpecialChars.lastIndex = pos;
-        var m = tokenSpecialChars.exec(text);
+        special.lastIndex = pos;
+        var m = special.exec(text);
         var skipped = m ? m.index - pos : text.length - pos;
         var skipped = m ? m.index - pos : text.length - pos;
         if (skipped) {
         if (skipped) {
           content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
           content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
@@ -4410,8 +4480,7 @@ window.CodeMirror = (function() {
           content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
           content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
           builder.col += tabWidth;
           builder.col += tabWidth;
         } else {
         } else {
-          var token = elt("span", "\u2022", "cm-invalidchar");
-          token.title = "\\u" + m[0].charCodeAt(0).toString(16);
+          var token = builder.cm.options.specialCharPlaceholder(m[0]);
           content.appendChild(token);
           content.appendChild(token);
           builder.col += 1;
           builder.col += 1;
         }
         }
@@ -4462,7 +4531,7 @@ window.CodeMirror = (function() {
       return out;
       return out;
     }
     }
     return function(builder, text, style, startStyle, endStyle, title) {
     return function(builder, text, style, startStyle, endStyle, title) {
-      return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title);
+      return inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
     };
     };
   }
   }
 
 
@@ -4564,7 +4633,8 @@ window.CodeMirror = (function() {
     var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
     var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
 
 
     // First adjust the line structure
     // First adjust the line structure
-    if (from.ch == 0 && to.ch == 0 && lastText == "") {
+    if (from.ch == 0 && to.ch == 0 && lastText == "" &&
+        (!doc.cm || doc.cm.options.wholeLineUpdateBefore)) {
       // This is a whole-line replace. Treated specially to make
       // This is a whole-line replace. Treated specially to make
       // sure line objects move the way they are supposed to.
       // sure line objects move the way they are supposed to.
       for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
       for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
@@ -4871,7 +4941,8 @@ window.CodeMirror = (function() {
     },
     },
     setBookmark: function(pos, options) {
     setBookmark: function(pos, options) {
       var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
       var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
-                      insertLeft: options && options.insertLeft};
+                      insertLeft: options && options.insertLeft,
+                      clearWhenEmpty: false};
       pos = clipPos(this, pos);
       pos = clipPos(this, pos);
       return markText(this, pos, pos, realOpts, "bookmark");
       return markText(this, pos, pos, realOpts, "bookmark");
     },
     },
@@ -5451,7 +5522,7 @@ window.CodeMirror = (function() {
     return true;
     return true;
   }
   }
 
 
-  var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
+  var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\u1DC0-\u1DFF\u20D0-\u20FF\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff\uFE20-\uFE2F]/;
 
 
   // DOM UTILITIES
   // DOM UTILITIES
 
 
@@ -5524,7 +5595,7 @@ window.CodeMirror = (function() {
         if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true;
         if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true;
         if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false;
         if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false;
       }
       }
-      return /[~!#%&*)=+}\]\\|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
+      return /[~!#%&*)=+}\]\\|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|\u2026[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
     };
     };
 
 
   var knownScrollbarWidth;
   var knownScrollbarWidth;
@@ -5656,22 +5727,22 @@ window.CodeMirror = (function() {
   }
   }
   var bidiOther;
   var bidiOther;
   function getBidiPartAt(order, pos) {
   function getBidiPartAt(order, pos) {
+    bidiOther = null;
     for (var i = 0, found; i < order.length; ++i) {
     for (var i = 0, found; i < order.length; ++i) {
       var cur = order[i];
       var cur = order[i];
-      if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
-      if (cur.from == pos || cur.to == pos) {
+      if (cur.from < pos && cur.to > pos) return i;
+      if ((cur.from == pos || cur.to == pos)) {
         if (found == null) {
         if (found == null) {
           found = i;
           found = i;
         } else if (compareBidiLevel(order, cur.level, order[found].level)) {
         } else if (compareBidiLevel(order, cur.level, order[found].level)) {
-          bidiOther = found;
+          if (cur.from != cur.to) bidiOther = found;
           return i;
           return i;
         } else {
         } else {
-          bidiOther = i;
+          if (cur.from != cur.to) bidiOther = i;
           return found;
           return found;
         }
         }
       }
       }
     }
     }
-    bidiOther = null;
     return found;
     return found;
   }
   }
 
 
@@ -5805,7 +5876,7 @@ window.CodeMirror = (function() {
         if (type == ",") types[i] = "N";
         if (type == ",") types[i] = "N";
         else if (type == "%") {
         else if (type == "%") {
           for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
           for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
-          var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
+          var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
           for (var j = i; j < end; ++j) types[j] = replace;
           for (var j = i; j < end; ++j) types[j] = replace;
           i = end - 1;
           i = end - 1;
         }
         }
@@ -5830,7 +5901,7 @@ window.CodeMirror = (function() {
         if (isNeutral.test(types[i])) {
         if (isNeutral.test(types[i])) {
           for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
           for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
           var before = (i ? types[i-1] : outerType) == "L";
           var before = (i ? types[i-1] : outerType) == "L";
-          var after = (end < len - 1 ? types[end] : outerType) == "L";
+          var after = (end < len ? types[end] : outerType) == "L";
           var replace = before || after ? "L" : "R";
           var replace = before || after ? "L" : "R";
           for (var j = i; j < end; ++j) types[j] = replace;
           for (var j = i; j < end; ++j) types[j] = replace;
           i = end - 1;
           i = end - 1;
@@ -5880,7 +5951,7 @@ window.CodeMirror = (function() {
 
 
   // THE END
   // THE END
 
 
-  CodeMirror.version = "3.18.1";
+  CodeMirror.version = "3.20.1";
 
 
   return CodeMirror;
   return CodeMirror;
 })();
 })();

+ 223 - 78
lib/CodeMirror/mode/javascript/javascript.js

@@ -21,7 +21,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
       "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
       "in": operator, "typeof": operator, "instanceof": operator,
       "in": operator, "typeof": operator, "instanceof": operator,
       "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
       "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
-      "this": kw("this")
+      "this": kw("this"), "module": kw("module"), "class": kw("class"), "super": kw("atom"),
+      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
     };
     };
 
 
     // Extend the 'normal' keywords with the TypeScript language extensions
     // Extend the 'normal' keywords with the TypeScript language extensions
@@ -30,7 +31,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       var tsKeywords = {
       var tsKeywords = {
         // object-like things
         // object-like things
         "interface": kw("interface"),
         "interface": kw("interface"),
-        "class": kw("class"),
         "extends": kw("extends"),
         "extends": kw("extends"),
         "constructor": kw("constructor"),
         "constructor": kw("constructor"),
 
 
@@ -40,8 +40,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
         "protected": kw("protected"),
         "protected": kw("protected"),
         "static": kw("static"),
         "static": kw("static"),
 
 
-        "super": kw("super"),
-
         // types
         // types
         "string": type, "number": type, "bool": type, "any": type
         "string": type, "number": type, "bool": type, "any": type
       };
       };
@@ -56,11 +54,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
 
 
   var isOperatorChar = /[+\-*&%=<>!?|~^]/;
   var isOperatorChar = /[+\-*&%=<>!?|~^]/;
 
 
-  function chain(stream, state, f) {
-    state.tokenize = f;
-    return f(stream, state);
-  }
-
   function nextUntilUnescaped(stream, end) {
   function nextUntilUnescaped(stream, end) {
     var escaped = false, next;
     var escaped = false, next;
     while ((next = stream.next()) != null) {
     while ((next = stream.next()) != null) {
@@ -78,50 +71,51 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     type = tp; content = cont;
     type = tp; content = cont;
     return style;
     return style;
   }
   }
-  function jsTokenBase(stream, state) {
+  function tokenBase(stream, state) {
     var ch = stream.next();
     var ch = stream.next();
-    if (ch == '"' || ch == "'")
-      return chain(stream, state, jsTokenString(ch));
-    else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/))
+    if (ch == '"' || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
       return ret("number", "number");
       return ret("number", "number");
-    else if (/[\[\]{}\(\),;\:\.]/.test(ch))
+    } else if (ch == "." && stream.match("..")) {
+      return ret("spread", "meta");
+    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
       return ret(ch);
       return ret(ch);
-    else if (ch == "0" && stream.eat(/x/i)) {
+    } else if (ch == "=" && stream.eat(">")) {
+      return ret("=>");
+    } else if (ch == "0" && stream.eat(/x/i)) {
       stream.eatWhile(/[\da-f]/i);
       stream.eatWhile(/[\da-f]/i);
       return ret("number", "number");
       return ret("number", "number");
-    }
-    else if (/\d/.test(ch)) {
+    } else if (/\d/.test(ch)) {
       stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
       stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
       return ret("number", "number");
       return ret("number", "number");
-    }
-    else if (ch == "/") {
+    } else if (ch == "/") {
       if (stream.eat("*")) {
       if (stream.eat("*")) {
-        return chain(stream, state, jsTokenComment);
-      }
-      else if (stream.eat("/")) {
+        state.tokenize = tokenComment;
+        return tokenComment(stream, state);
+      } else if (stream.eat("/")) {
         stream.skipToEnd();
         stream.skipToEnd();
         return ret("comment", "comment");
         return ret("comment", "comment");
-      }
-      else if (state.lastType == "operator" || state.lastType == "keyword c" ||
-               /^[\[{}\(,;:]$/.test(state.lastType)) {
+      } else if (state.lastType == "operator" || state.lastType == "keyword c" ||
+               state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
         nextUntilUnescaped(stream, "/");
         nextUntilUnescaped(stream, "/");
         stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
         stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
         return ret("regexp", "string-2");
         return ret("regexp", "string-2");
-      }
-      else {
+      } else {
         stream.eatWhile(isOperatorChar);
         stream.eatWhile(isOperatorChar);
         return ret("operator", null, stream.current());
         return ret("operator", null, stream.current());
       }
       }
-    }
-    else if (ch == "#") {
+    } else if (ch == "`") {
+      state.tokenize = tokenQuasi;
+      return tokenQuasi(stream, state);
+    } else if (ch == "#") {
       stream.skipToEnd();
       stream.skipToEnd();
       return ret("error", "error");
       return ret("error", "error");
-    }
-    else if (isOperatorChar.test(ch)) {
+    } else if (isOperatorChar.test(ch)) {
       stream.eatWhile(isOperatorChar);
       stream.eatWhile(isOperatorChar);
       return ret("operator", null, stream.current());
       return ret("operator", null, stream.current());
-    }
-    else {
+    } else {
       stream.eatWhile(/[\w\$_]/);
       stream.eatWhile(/[\w\$_]/);
       var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
       var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
       return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
       return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
@@ -129,19 +123,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     }
     }
   }
   }
 
 
-  function jsTokenString(quote) {
+  function tokenString(quote) {
     return function(stream, state) {
     return function(stream, state) {
       if (!nextUntilUnescaped(stream, quote))
       if (!nextUntilUnescaped(stream, quote))
-        state.tokenize = jsTokenBase;
+        state.tokenize = tokenBase;
       return ret("string", "string");
       return ret("string", "string");
     };
     };
   }
   }
 
 
-  function jsTokenComment(stream, state) {
+  function tokenComment(stream, state) {
     var maybeEnd = false, ch;
     var maybeEnd = false, ch;
     while (ch = stream.next()) {
     while (ch = stream.next()) {
       if (ch == "/" && maybeEnd) {
       if (ch == "/" && maybeEnd) {
-        state.tokenize = jsTokenBase;
+        state.tokenize = tokenBase;
         break;
         break;
       }
       }
       maybeEnd = (ch == "*");
       maybeEnd = (ch == "*");
@@ -149,6 +143,50 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     return ret("comment", "comment");
     return ret("comment", "comment");
   }
   }
 
 
+  function tokenQuasi(stream, state) {
+    var escaped = false, next;
+    while ((next = stream.next()) != null) {
+      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
+        state.tokenize = tokenBase;
+        break;
+      }
+      escaped = !escaped && next == "\\";
+    }
+    return ret("quasi", "string-2", stream.current());
+  }
+
+  var brackets = "([{}])";
+  // This is a crude lookahead trick to try and notice that we're
+  // parsing the argument patterns for a fat-arrow function before we
+  // actually hit the arrow token. It only works if the arrow is on
+  // the same line as the arguments and there's no strange noise
+  // (comments) in between. Fallback is to only notice when we hit the
+  // arrow, and not declare the arguments as locals for the arrow
+  // body.
+  function findFatArrow(stream, state) {
+    if (state.fatArrowAt) state.fatArrowAt = null;
+    var arrow = stream.string.indexOf("=>", stream.start);
+    if (arrow < 0) return;
+
+    var depth = 0, sawSomething = false;
+    for (var pos = arrow - 1; pos >= 0; --pos) {
+      var ch = stream.string.charAt(pos);
+      var bracket = brackets.indexOf(ch);
+      if (bracket >= 0 && bracket < 3) {
+        if (!depth) { ++pos; break; }
+        if (--depth == 0) break;
+      } else if (bracket >= 3 && bracket < 6) {
+        ++depth;
+      } else if (/[$\w]/.test(ch)) {
+        sawSomething = true;
+      } else if (sawSomething && !depth) {
+        ++pos;
+        break;
+      }
+    }
+    if (sawSomething && !depth) state.fatArrowAt = pos;
+  }
+
   // Parser
   // Parser
 
 
   var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true};
   var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true};
@@ -165,6 +203,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
   function inScope(state, varname) {
   function inScope(state, varname) {
     for (var v = state.localVars; v; v = v.next)
     for (var v = state.localVars; v; v = v.next)
       if (v.name == varname) return true;
       if (v.name == varname) return true;
+    for (var cx = state.context; cx; cx = cx.prev) {
+      for (var v = cx.vars; v; v = v.next)
+        if (v.name == varname) return true;
+    }
   }
   }
 
 
   function parseJS(state, style, type, content, stream) {
   function parseJS(state, style, type, content, stream) {
@@ -211,7 +253,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       state.localVars = {name: varname, next: state.localVars};
       state.localVars = {name: varname, next: state.localVars};
     } else {
     } else {
       if (inList(state.globalVars)) return;
       if (inList(state.globalVars)) return;
-      state.globalVars = {name: varname, next: state.globalVars};
+      if (parserConfig.globalVars)
+        state.globalVars = {name: varname, next: state.globalVars};
     }
     }
   }
   }
 
 
@@ -253,16 +296,15 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     };
     };
   }
   }
 
 
-  function statement(type) {
-    if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
+  function statement(type, value) {
+    if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
     if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
     if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
     if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
     if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
     if (type == "{") return cont(pushlex("}"), block, poplex);
     if (type == "{") return cont(pushlex("}"), block, poplex);
     if (type == ";") return cont();
     if (type == ";") return cont();
     if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse);
     if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse);
     if (type == "function") return cont(functiondef);
     if (type == "function") return cont(functiondef);
-    if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
-                                   poplex, statement, poplex);
+    if (type == "for") return cont(pushlex("form"), forspec, poplex, statement, poplex);
     if (type == "variable") return cont(pushlex("stat"), maybelabel);
     if (type == "variable") return cont(pushlex("stat"), maybelabel);
     if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
     if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
                                       block, poplex, poplex);
                                       block, poplex, poplex);
@@ -270,6 +312,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == "default") return cont(expect(":"));
     if (type == "default") return cont(expect(":"));
     if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
     if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
                                      statement, poplex, popcontext);
                                      statement, poplex, popcontext);
+    if (type == "module") return cont(pushlex("form"), pushcontext, afterModule, popcontext, poplex);
+    if (type == "class") return cont(pushlex("form"), className, objlit, poplex);
+    if (type == "export") return cont(pushlex("form"), afterExport, poplex);
+    if (type == "import") return cont(pushlex("form"), afterImport, poplex);
     return pass(pushlex("stat"), expression, expect(";"), poplex);
     return pass(pushlex("stat"), expression, expect(";"), poplex);
   }
   }
   function expression(type) {
   function expression(type) {
@@ -279,14 +325,20 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     return expressionInner(type, true);
     return expressionInner(type, true);
   }
   }
   function expressionInner(type, noComma) {
   function expressionInner(type, noComma) {
+    if (cx.state.fatArrowAt == cx.stream.start) {
+      var body = noComma ? arrowBodyNoComma : arrowBody;
+      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
+      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
+    }
+
     var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
     var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
     if (type == "function") return cont(functiondef);
     if (type == "function") return cont(functiondef);
     if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
     if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
-    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
-    if (type == "operator") return cont(noComma ? expressionNoComma : expression);
-    if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop);
-    if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop);
+    if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
+    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
+    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
+    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
     return cont();
     return cont();
   }
   }
   function maybeexpression(type) {
   function maybeexpression(type) {
@@ -305,16 +357,40 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
   function maybeoperatorNoComma(type, value, noComma) {
   function maybeoperatorNoComma(type, value, noComma) {
     var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
     var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
     var expr = noComma == false ? expression : expressionNoComma;
     var expr = noComma == false ? expression : expressionNoComma;
+    if (value == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
     if (type == "operator") {
     if (type == "operator") {
       if (/\+\+|--/.test(value)) return cont(me);
       if (/\+\+|--/.test(value)) return cont(me);
       if (value == "?") return cont(expression, expect(":"), expr);
       if (value == "?") return cont(expression, expect(":"), expr);
       return cont(expr);
       return cont(expr);
     }
     }
+    if (type == "quasi") { cx.cc.push(me); return quasi(value); }
     if (type == ";") return;
     if (type == ";") return;
-    if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me);
+    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
     if (type == ".") return cont(property, me);
     if (type == ".") return cont(property, me);
     if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
     if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
   }
   }
+  function quasi(value) {
+    if (!value) debugger;
+    if (value.slice(value.length - 2) != "${") return cont();
+    return cont(expression, continueQuasi);
+  }
+  function continueQuasi(type) {
+    if (type == "}") {
+      cx.marked = "string-2";
+      cx.state.tokenize = tokenQuasi;
+      return cont();
+    }
+  }
+  function arrowBody(type) {
+    findFatArrow(cx.stream, cx.state);
+    if (type == "{") return pass(statement);
+    return pass(expression);
+  }
+  function arrowBodyNoComma(type) {
+    findFatArrow(cx.stream, cx.state);
+    if (type == "{") return pass(statement);
+    return pass(expressionNoComma);
+  }
   function maybelabel(type) {
   function maybelabel(type) {
     if (type == ":") return cont(poplex, statement);
     if (type == ":") return cont(poplex, statement);
     return pass(maybeoperatorComma, expect(";"), poplex);
     return pass(maybeoperatorComma, expect(";"), poplex);
@@ -328,15 +404,20 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       if (value == "get" || value == "set") return cont(getterSetter);
       if (value == "get" || value == "set") return cont(getterSetter);
     } else if (type == "number" || type == "string") {
     } else if (type == "number" || type == "string") {
       cx.marked = type + " property";
       cx.marked = type + " property";
+    } else if (type == "[") {
+      return cont(expression, expect("]"), afterprop);
     }
     }
-    if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expressionNoComma);
+    if (atomicTypes.hasOwnProperty(type)) return cont(afterprop);
   }
   }
   function getterSetter(type) {
   function getterSetter(type) {
-    if (type == ":") return cont(expression);
-    if (type != "variable") return cont(expect(":"), expression);
+    if (type != "variable") return pass(afterprop);
     cx.marked = "property";
     cx.marked = "property";
     return cont(functiondef);
     return cont(functiondef);
   }
   }
+  function afterprop(type) {
+    if (type == ":") return cont(expressionNoComma);
+    if (type == "(") return pass(functiondef);
+  }
   function commasep(what, end) {
   function commasep(what, end) {
     function proceed(type) {
     function proceed(type) {
       if (type == ",") {
       if (type == ",") {
@@ -349,75 +430,138 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     }
     }
     return function(type) {
     return function(type) {
       if (type == end) return cont();
       if (type == end) return cont();
-      else return pass(what, proceed);
+      return pass(what, proceed);
     };
     };
   }
   }
+  function contCommasep(what, end, info) {
+    for (var i = 3; i < arguments.length; i++)
+      cx.cc.push(arguments[i]);
+    return cont(pushlex(end, info), commasep(what, end), poplex);
+  }
   function block(type) {
   function block(type) {
     if (type == "}") return cont();
     if (type == "}") return cont();
     return pass(statement, block);
     return pass(statement, block);
   }
   }
   function maybetype(type) {
   function maybetype(type) {
-    if (type == ":") return cont(typedef);
-    return pass();
+    if (isTS && type == ":") return cont(typedef);
   }
   }
   function typedef(type) {
   function typedef(type) {
     if (type == "variable"){cx.marked = "variable-3"; return cont();}
     if (type == "variable"){cx.marked = "variable-3"; return cont();}
-    return pass();
   }
   }
-  function vardef1(type, value) {
-    if (type == "variable") {
+  function vardef() {
+    return pass(pattern, maybetype, maybeAssign, vardefCont);
+  }
+  function pattern(type, value) {
+    if (type == "variable") { register(value); return cont(); }
+    if (type == "[") return contCommasep(pattern, "]");
+    if (type == "{") return contCommasep(proppattern, "}");
+  }
+  function proppattern(type, value) {
+    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
       register(value);
       register(value);
-      return isTS ? cont(maybetype, vardef2) : cont(vardef2);
+      return cont(maybeAssign);
     }
     }
-    return pass();
+    if (type == "variable") cx.marked = "property";
+    return cont(expect(":"), pattern, maybeAssign);
+  }
+  function maybeAssign(_type, value) {
+    if (value == "=") return cont(expressionNoComma);
   }
   }
-  function vardef2(type, value) {
-    if (value == "=") return cont(expressionNoComma, vardef2);
-    if (type == ",") return cont(vardef1);
+  function vardefCont(type) {
+    if (type == ",") return cont(vardef);
   }
   }
   function maybeelse(type, value) {
   function maybeelse(type, value) {
     if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
     if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
   }
   }
+  function forspec(type) {
+    if (type == "(") return cont(pushlex(")"), forspec1, expect(")"));
+  }
   function forspec1(type) {
   function forspec1(type) {
-    if (type == "var") return cont(vardef1, expect(";"), forspec2);
+    if (type == "var") return cont(vardef, expect(";"), forspec2);
     if (type == ";") return cont(forspec2);
     if (type == ";") return cont(forspec2);
-    if (type == "variable") return cont(formaybein);
+    if (type == "variable") return cont(formaybeinof);
     return pass(expression, expect(";"), forspec2);
     return pass(expression, expect(";"), forspec2);
   }
   }
-  function formaybein(_type, value) {
-    if (value == "in") return cont(expression);
+  function formaybeinof(_type, value) {
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
     return cont(maybeoperatorComma, forspec2);
     return cont(maybeoperatorComma, forspec2);
   }
   }
   function forspec2(type, value) {
   function forspec2(type, value) {
     if (type == ";") return cont(forspec3);
     if (type == ";") return cont(forspec3);
-    if (value == "in") return cont(expression);
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
     return pass(expression, expect(";"), forspec3);
     return pass(expression, expect(";"), forspec3);
   }
   }
   function forspec3(type) {
   function forspec3(type) {
     if (type != ")") cont(expression);
     if (type != ")") cont(expression);
   }
   }
   function functiondef(type, value) {
   function functiondef(type, value) {
+    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
     if (type == "variable") {register(value); return cont(functiondef);}
     if (type == "variable") {register(value); return cont(functiondef);}
-    if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
+    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
+  }
+  function funarg(type) {
+    if (type == "spread") return cont(funarg);
+    return pass(pattern, maybetype);
+  }
+  function className(type, value) {
+    if (type == "variable") {register(value); return cont(classNameAfter);}
+  }
+  function classNameAfter(_type, value) {
+    if (value == "extends") return cont(expression);
+  }
+  function objlit(type) {
+    if (type == "{") return contCommasep(objprop, "}");
+  }
+  function afterModule(type, value) {
+    if (type == "string") return cont(statement);
+    if (type == "variable") { register(value); return cont(maybeFrom); }
+  }
+  function afterExport(_type, value) {
+    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
+    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
+    return pass(statement);
+  }
+  function afterImport(type) {
+    if (type == "string") return cont();
+    return pass(importSpec, maybeFrom);
+  }
+  function importSpec(type, value) {
+    if (type == "{") return contCommasep(importSpec, "}");
+    if (type == "variable") register(value);
+    return cont();
+  }
+  function maybeFrom(_type, value) {
+    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
+  }
+  function arrayLiteral(type) {
+    if (type == "]") return cont();
+    return pass(expressionNoComma, maybeArrayComprehension);
+  }
+  function maybeArrayComprehension(type) {
+    if (type == "for") return pass(comprehension);
+    if (type == ",") return cont(commasep(expressionNoComma, "]"));
+    return pass(commasep(expressionNoComma, "]"));
   }
   }
-  function funarg(type, value) {
-    if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
+  function comprehension(type) {
+    if (type == "for") return cont(forspec, comprehension);
+    if (type == "if") return cont(expression, comprehension);
   }
   }
 
 
   // Interface
   // Interface
 
 
   return {
   return {
     startState: function(basecolumn) {
     startState: function(basecolumn) {
-      return {
-        tokenize: jsTokenBase,
-        lastType: null,
+      var state = {
+        tokenize: tokenBase,
+        lastType: "sof",
         cc: [],
         cc: [],
         lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
         lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
         localVars: parserConfig.localVars,
         localVars: parserConfig.localVars,
-        globalVars: parserConfig.globalVars,
         context: parserConfig.localVars && {vars: parserConfig.localVars},
         context: parserConfig.localVars && {vars: parserConfig.localVars},
         indented: 0
         indented: 0
       };
       };
+      if (parserConfig.globalVars) state.globalVars = parserConfig.globalVars;
+      return state;
     },
     },
 
 
     token: function(stream, state) {
     token: function(stream, state) {
@@ -425,8 +569,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
         if (!state.lexical.hasOwnProperty("align"))
         if (!state.lexical.hasOwnProperty("align"))
           state.lexical.align = false;
           state.lexical.align = false;
         state.indented = stream.indentation();
         state.indented = stream.indentation();
+        findFatArrow(stream, state);
       }
       }
-      if (state.tokenize != jsTokenComment && stream.eatSpace()) return null;
+      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
       var style = state.tokenize(stream, state);
       var style = state.tokenize(stream, state);
       if (type == "comment") return style;
       if (type == "comment") return style;
       state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
       state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
@@ -434,21 +579,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     },
     },
 
 
     indent: function(state, textAfter) {
     indent: function(state, textAfter) {
-      if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
-      if (state.tokenize != jsTokenBase) return 0;
+      if (state.tokenize == tokenComment) return CodeMirror.Pass;
+      if (state.tokenize != tokenBase) return 0;
       var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
       var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
       // Kludge to prevent 'maybelse' from blocking lexical scope pops
       // Kludge to prevent 'maybelse' from blocking lexical scope pops
       for (var i = state.cc.length - 1; i >= 0; --i) {
       for (var i = state.cc.length - 1; i >= 0; --i) {
         var c = state.cc[i];
         var c = state.cc[i];
         if (c == poplex) lexical = lexical.prev;
         if (c == poplex) lexical = lexical.prev;
-        else if (c != maybeelse || /^else\b/.test(textAfter)) break;
+        else if (c != maybeelse) break;
       }
       }
       if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
       if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
       if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
       if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
         lexical = lexical.prev;
         lexical = lexical.prev;
       var type = lexical.type, closing = firstChar == type;
       var type = lexical.type, closing = firstChar == type;
 
 
-      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
+      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
       else if (type == "form" && firstChar == "{") return lexical.indented;
       else if (type == "form" && firstChar == "{") return lexical.indented;
       else if (type == "form") return lexical.indented + indentUnit;
       else if (type == "form") return lexical.indented + indentUnit;
       else if (type == "stat")
       else if (type == "stat")

+ 1 - 1
mylib/CodeMirror/search.js

@@ -42,7 +42,7 @@
 			dialog = wrap.appendChild(document.createElement("div"));
 			dialog = wrap.appendChild(document.createElement("div"));
 			dialog.className='CodeMirror-dialog';
 			dialog.className='CodeMirror-dialog';
 		}
 		}
-		var text=_('labelSearch')+'<input class=CodeMirror-search placeholder="Search for"><button class=CodeMirror-findNext>&gt;</button><button class=CodeMirror-findPrev>&lt;</button><button class=CodeMirror-cancel>&times;</button>',closed=false,iS,iR;
+		var text=_('labelSearch')+'<input class=CodeMirror-search placeholder="Search for"><button class=CodeMirror-findPrev>&lt;</button><button class=CodeMirror-findNext>&gt;</button><button class=CodeMirror-cancel>&times;</button>',closed=false,iS,iR;
 		if(rep) text+='<br>'+_('labelReplace')+'<input class=CodeMirror-replace placeholder="Replace with"><button class=CodeMirror-replaceNext>'+_('buttonReplace')+'</button><button class=CodeMirror-replaceAll>'+_('buttonReplaceAll')+'</button>';
 		if(rep) text+='<br>'+_('labelReplace')+'<input class=CodeMirror-replace placeholder="Replace with"><button class=CodeMirror-replaceNext>'+_('buttonReplace')+'</button><button class=CodeMirror-replaceAll>'+_('buttonReplaceAll')+'</button>';
 		dialog.innerHTML=text;
 		dialog.innerHTML=text;
 		iS=dialog.querySelector('.CodeMirror-search');
 		iS=dialog.querySelector('.CodeMirror-search');

+ 47 - 47
options.html

@@ -6,20 +6,20 @@
 		<link rel="stylesheet" type="text/css" href="style.css" />
 		<link rel="stylesheet" type="text/css" href="style.css" />
 		<script type="text/javascript" src="lib/zip.js/zip.js"></script>
 		<script type="text/javascript" src="lib/zip.js/zip.js"></script>
 		<script type="text/javascript" src="load.js"></script>
 		<script type="text/javascript" src="load.js"></script>
-		<title class=i18n>extName</title>
+		<title data-i18n=extName></title>
 	</head>
 	</head>
 	<body class=fill>
 	<body class=fill>
-		<table id=main class="frame hide">
-			<tr><td><h1 class=center><span class=i18n>labelOptions</span> - <span class=i18n>extName</span></h1></td></tr>
+		<table id=main class=frame>
+			<tr><td><h1 class=center><span data-i18n=labelOptions></span> - <span data-i18n=extName></span></h1></td></tr>
 			<tr>
 			<tr>
 				<td>
 				<td>
 					<div class=left>
 					<div class=left>
-						<button id=bNew class=i18n>buttonNew</button>
-						<span class=i18n>anchorGetMoreScripts</span>
+						<button id=bNew data-i18n=buttonNew></button>
+						<span data-i18n=anchorGetMoreScripts></span>
 					</div>
 					</div>
 					<div class=right>
 					<div class=right>
-						<a id=bUpdate class=i18n href=#>anchorUpdateAll</a>
-						<button id=bAdvanced class=i18n>buttonAdvanced</button>
+						<a id=bUpdate href=# data-i18n=anchorUpdateAll></a>
+						<button id=bAdvanced data-i18n=buttonAdvanced></button>
 					</div>
 					</div>
 					<div class=x></div>
 					<div class=x></div>
 				</td>
 				</td>
@@ -29,30 +29,30 @@
 					<div class=expanda>
 					<div class=expanda>
 						<fieldset>
 						<fieldset>
 							<legend>
 							<legend>
-								<span class=i18n>labelInstalledScripts</span>
-								<label><input type=checkbox id=cDetail><span class=i18n>labelShowDetails</span></label>
+								<span data-i18n=labelInstalledScripts></span>
+								<label><input type=checkbox id=cDetail><span data-i18n=labelShowDetails></span></label>
 							</legend>
 							</legend>
-							<div id=sList><span class="hint i18n">msgLoading</span></div>
+							<div id=sList><span class=hint data-i18n=msgLoading></span></div>
 						</fieldset>
 						</fieldset>
 					</div>
 					</div>
 				</td>
 				</td>
 			</tr>
 			</tr>
-			<tr><td class=center><span class=i18n>anchorSupportPage</span> - <span class=i18n>anchorAuthor</span> - 2013</td></tr>
+			<tr><td class=center><span data-i18n=anchorSupportPage></span> - <span data-i18n=anchorAuthor></span> - 2013</td></tr>
 		</table>
 		</table>
 		<div id=overlay class=hide></div>
 		<div id=overlay class=hide></div>
 		<table id=editor class="frame hide">
 		<table id=editor class="frame hide">
 			<tr>
 			<tr>
-				<td><h2 class=i18n>labelScriptEditor</h2></td>
-				<td class=buttons><button id=bcustom class=i18n>buttonCustomMeta</button></td>
+				<td><h2 data-i18n=labelScriptEditor></h2></td>
+				<td class=buttons><button id=bcustom data-i18n=buttonCustomMeta></button></td>
 			</tr>
 			</tr>
 			<tr class=expand><td id=eCode colspan=2 class=expandr></td></tr>
 			<tr class=expand><td id=eCode colspan=2 class=expandr></td></tr>
 			<tr>
 			<tr>
 				<td colspan=2>
 				<td colspan=2>
-					<label><input type=checkbox id=eUpdate><span class=i18n>labelAllowUpdate</span></label>
+					<label><input type=checkbox id=eUpdate><span data-i18n=labelAllowUpdate></span></label>
 					<div class=right>
 					<div class=right>
-						<button id=eSave class=i18n>buttonSave</button>
-						<button id=eSaveClose class=i18n>buttonSaveClose</button>
-						<button id=eClose class=i18n>buttonClose</button>
+						<button id=eSave data-i18n=buttonSave></button>
+						<button id=eSaveClose data-i18n=buttonSaveClose></button>
+						<button id=eClose data-i18n=buttonClose></button>
 					</div>
 					</div>
 				</td>
 				</td>
 			</tr>
 			</tr>
@@ -60,72 +60,72 @@
 		<div id=meta class="float hide">
 		<div id=meta class="float hide">
 			<table>
 			<table>
 				<tr>
 				<tr>
-					<td title="@name" class=i18n>labelName</td><td class=expand><input type=text id=mName></td>
-					<td title="@run-at" class=i18n>labelRunAt</td><td>
+					<td title="@name" data-i18n=labelName></td><td class=expand><input type=text id=mName></td>
+					<td title="@run-at" data-i18n=labelRunAt></td><td>
 						<select id=mRunAt>
 						<select id=mRunAt>
-							<option class=i18n value=default>labelRunAtDefault</option>
+							<option value=default data-i18n=labelRunAtDefault></option>
 							<option value=start>document-start</option>
 							<option value=start>document-start</option>
 							<option value=idle>document-idle</option>
 							<option value=idle>document-idle</option>
 							<option value=end>document-end</option>
 							<option value=end>document-end</option>
 						</select>
 						</select>
 					</td>
 					</td>
 				</tr>
 				</tr>
-				<tr title="@homepage"><td class=i18n>labelHomepage</td><td colspan=3 class=expand><input type=text id=mHomepage></td></tr>
+				<tr title="@homepage"><td data-i18n=labelHomepage></td><td colspan=3 class=expand><input type=text id=mHomepage></td></tr>
 			</table>
 			</table>
 			<table>
 			<table>
-				<tr title="@updateURL"><td class=i18n>labelUpdateURL</td><td class=expand><input type=text id=mUpdateURL></td></tr>
-				<tr title="@downloadURL"><td class=i18n>labelDownloadURL</td><td class=expand><input type=text id=mDownloadURL></td></tr>
+				<tr title="@updateURL"><td data-i18n=labelUpdateURL></td><td class=expand><input type=text id=mUpdateURL></td></tr>
+				<tr title="@downloadURL"><td data-i18n=labelDownloadURL></td><td class=expand><input type=text id=mDownloadURL></td></tr>
 			</table>
 			</table>
 			<fieldset title="@include">
 			<fieldset title="@include">
-				<legend class=i18n>labelInclude</legend>
-				<label><input type=checkbox id=cInclude><span class=i18n>labelKeepInclude</span></label>
-				<span class=i18n>labelCustomInclude</span>
+				<legend data-i18n=labelInclude></legend>
+				<label><input type=checkbox id=cInclude><span data-i18n=labelKeepInclude></span></label>
+				<span data-i18n=labelCustomInclude></span>
 				<textarea id=mInclude></textarea>
 				<textarea id=mInclude></textarea>
 			</fieldset>
 			</fieldset>
 			<fieldset title="@match">
 			<fieldset title="@match">
-				<legend class=i18n>labelMatch</legend>
-				<label><input type=checkbox id=cMatch><span class=i18n>labelKeepMatch</span></label>
-				<span class=i18n>labelCustomMatch</span>
+				<legend data-i18n=labelMatch></legend>
+				<label><input type=checkbox id=cMatch><span data-i18n=labelKeepMatch></span></label>
+				<span data-i18n=labelCustomMatch></span>
 				<textarea id=mMatch></textarea>
 				<textarea id=mMatch></textarea>
 			</fieldset>
 			</fieldset>
 			<fieldset title="@exclude">
 			<fieldset title="@exclude">
-				<legend class=i18n>labelExclude</legend>
-				<label><input type=checkbox id=cExclude><span class=i18n>labelKeepExclude</span></label>
-				<span class=i18n>labelCustomExclude</span>
+				<legend data-i18n=labelExclude></legend>
+				<label><input type=checkbox id=cExclude><span data-i18n=labelKeepExclude></span></label>
+				<span data-i18n=labelCustomExclude></span>
 				<textarea id=mExclude></textarea>
 				<textarea id=mExclude></textarea>
 			</fieldset>
 			</fieldset>
-			<div class=right><button id=mOK class=i18n>buttonOK</button> <button id=mCancel class=i18n>buttonCancel</button></div>
+			<div class=right><button id=mOK data-i18n=buttonOK></button> <button id=mCancel data-i18n=buttonCancel></button></div>
 		</div>
 		</div>
 		<div id=export class="float hide">
 		<div id=export class="float hide">
-			<h2 class=i18n>labelExport</h2>
-			<span class=i18n>labelInstalledScripts</span>
+			<h2 data-i18n=labelExport></h2>
+			<span data-i18n=labelInstalledScripts></span>
 			<div id=xOptions class=right>
 			<div id=xOptions class=right>
-				<a class=i18n>anchorExportOptions</a>
+				<a data-i18n=anchorExportOptions></a>
 				<div>
 				<div>
-					<label><input type=checkbox id=cWithData><span class=i18n>labelExportScriptData</span></label>
+					<label><input type=checkbox id=cWithData><span data-i18n=labelExportScriptData></span></label>
 				</div>
 				</div>
 			</div>
 			</div>
 			<div id=xList class=list></div>
 			<div id=xList class=list></div>
-			<div class=left><button id=bSelect class=i18n>buttonAllNone</button></div>
+			<div class=left><button id=bSelect data-i18n=buttonAllNone></button></div>
 			<div class=right>
 			<div class=right>
 				<button id=bExport></button>
 				<button id=bExport></button>
-				<button id=bClose class=i18n>buttonClose</button>
+				<button id=bClose data-i18n=buttonClose></button>
 			</div>
 			</div>
 		</div>
 		</div>
 		<div id=advanced class="float hide">
 		<div id=advanced class="float hide">
-			<h2 class=i18n>labelAdvanced</h2>
-			<label><input type=checkbox id=cUpdate><span class=i18n>labelAutoUpdate</span></label>
+			<h2 data-i18n=labelAdvanced></h2>
+			<label><input type=checkbox id=cUpdate><span data-i18n=labelAutoUpdate></span></label>
 			<div>
 			<div>
-				<span class=i18n>labelSearchLink</span> <button id=bDefSearch class=i18n>buttonDefault</button><br>
+				<span data-i18n=labelSearchLink></span> <button id=bDefSearch data-i18n=buttonDefault></button><br>
 				<textarea id=tSearch></textarea><br>
 				<textarea id=tSearch></textarea><br>
 			</div>
 			</div>
 			<fieldset>
 			<fieldset>
-				<legend class=i18n>labelData</legend>
-				<button id=aExport class=i18n>buttonExportData</button>
-				<button id=aImport class=i18n>buttonImportData</button><input type=file class=hide id=iImport>
-				<button id=aVacuum class=i18n>buttonVacuumData</button>
+				<legend data-i18n=labelData></legend>
+				<button id=aExport data-i18n=buttonExportData></button>
+				<button id=aImport data-i18n=buttonImportData></button><input type=file class=hide id=iImport>
+				<button id=aVacuum data-i18n=buttonVacuumData></button>
 			</fieldset>
 			</fieldset>
-			<div class=right><button id=aClose class=i18n>buttonClose</button></div>
+			<div class=right><button id=aClose data-i18n=buttonClose></button></div>
 		</div>
 		</div>
 	</body>
 	</body>
 </html>
 </html>

+ 1 - 1
options.js

@@ -150,7 +150,7 @@ O.onclick=function(){
 function confirmCancel(dirty){
 function confirmCancel(dirty){
 	return !dirty||confirm(_('confirmNotSaved'));
 	return !dirty||confirm(_('confirmNotSaved'));
 }
 }
-initCSS();initI18n(function(){switchTo(N);});
+initCSS();initI18n();
 
 
 // Advanced
 // Advanced
 var A=$('advanced'),H=$('iImport');
 var A=$('advanced'),H=$('iImport');