--- ../webGerber-orig/webGerber.js	2018-09-22 06:35:44.000000000 +0200
+++ webGerber.js	2018-09-27 07:41:36.000000000 +0200
@@ -40,17 +40,21 @@
 // Board = (54-47, 98-76, 10-3)*2
 // Board = (7*2, 22*2, 7*2)
 // Board = (14, 44, 14) = #0e2c0e
-wG.colors[wG.BOARD]  = '#0e2c0e';//'#203020';//'#255005';
+//wG.colors[wG.BOARD]  = '#0e2c0e';//'#203020';//'#255005';
+wG.colors[wG.BOARD]  = '#0e0e2c';//'#203020';//'#255005';
 // Copper = #b87333
-wG.colors[wG.COPPER] = '#b87333';//'#c0b030'
+//wG.colors[wG.COPPER] = '#b87333';//'#c0b030'
+wG.colors[wG.COPPER] = '#cc9933';//'#c0b030'
 // Copper + SolderMask = #8b861d
 // SolderMask*.5 + (184*.5, 115*.5, 51*.5) = #8b861d
 // SolderMask = (#8b861d - (184/2, 115/2, 51/2))/.5
 // SolderMask = (139-92, 134-58, 29-26)*2
 // SolderMask = (47*2, 76*2, 3*2) = (94, 152, 6)
-wG.colors[wG.SOLDER] = 'rgba(94, 152, 6, .5)';//'rgba(37, 80, 5, .7)';
+//wG.colors[wG.SOLDER] = 'rgba(94, 152, 6, .5)';//'rgba(37, 80, 5, .7)';
+wG.colors[wG.SOLDER] = 'rgba(94, 6, 152, .6)';//'rgba(37, 80, 5, .7)';
 wG.colors[wG.PASTE]  = '#e6e8fa';
-wG.colors[wG.SILK]   = '#ffffff';
+//wG.colors[wG.SILK]   = '#ffffff';
+wG.colors[wG.SILK]   = 'rgba(255, 255, 255, .8)';
 
 // Guesses a layer's type from its filename.
 wG.guessLayer = function guessLayer(f) {
@@ -75,8 +79,46 @@
         return [wG.TOP, wG.PASTE];
     if(f.match(/\.gto|\.plc/) || f.match(/top/) && f.match(/si?lk/))
         return [wG.TOP, wG.SILK];
+
+	if(f.match(/\.gbr/)) {
+		if(f.match(/layer1/))
+			return [wG.TOP, wG.COPPER];
+		if(f.match(/layer2/))
+			return [wG.BOTTOM, wG.COPPER];
+		if(f.match(/mask1/))
+			return [wG.TOP, wG.SOLDER];
+		if(f.match(/mask2/))
+			return [wG.BOTTOM, wG.SOLDER];
+		if(f.match(/silk1/))
+			return [wG.TOP, wG.SILK];
+		if(f.match(/silk2/))
+			return [wG.BOTTOM, wG.SILK];
+		if(f.match(/spaste1/))
+			return [wG.TOP, wG.PASTE];
+		if(f.match(/spaste2/))
+			return [wG.BOTTOM, wG.PASTE];
+	}
+
 };
 
+wG.loadHoleReport = function loadHoleReport(text) {
+	text = text.replace(/^\s*Tool\s+Size\s+Count\s+Plated.*/, '');
+	text = text.replace(/[^\S\n]+/g, ' ');
+	text = text.replace(/^\s+/, '');
+
+	var g = {offA: 0, offB: 0, shapes: [], cmds: [], scale: 1, holereport: 1}, shape, body = false, prevX = 0, prevY = 0;
+	var tools = text.split(/\n[\t ]*/);
+	for(var i = 0; i < tools.length; i++) {
+        var d = tools[i];
+		if(d[0] == 'T') {
+			var r = /^T(\d+)\s+([\d.]+)\s+\d+\s+([a-zA-Z]+)/.exec(d); // assert(r);
+			var plated = r[3].match(/^y/i) ? 1 : 0;
+			g.shapes[parseInt(r[1], 10)] = ['C', +r[2], plated];
+		}
+	}
+	return(g);
+}
+
 // Loads a Excellon drill file.
 wG.loadDrill = function loadDrill(text) {
     text = text.replace(/^[\s%]*M48/, '');
@@ -100,14 +142,17 @@
     
     var cmds = text.split('\n');
     
-    var g = {offA: 0, offB: 0, shapes: [], cmds: [], scale: 1}, shape, body = false, prevX = 0, prevY = 0;
+    var g = {offA: 0, offB: 0, shapes: [], cmds: [], scale: 1, drillfile: 1}, shape, body = false, prevX = 0, prevY = 0;
     
     for(var i = 0; i < cmds.length; i++) {
         var d = cmds[i];
         if(!body) {
             if(d[0] == 'T') {
                 var r = /^T(\d+)[^C]*C([\d.]+)/.exec(d); // assert(r);
-                g.shapes[parseInt(r[1], 10)] = ['C', +r[2]];
+				if(!g.shapes[parseInt(r[1], 10)])
+				{
+					g.shapes[parseInt(r[1], 10)] = ['C', +r[2], 1];
+				}
             }
             else if(d == 'METRIC,LZ')
                 g.scale = 1, g.omitLead = false, g.int = 3, g.dec = 3, g.num = 6;
@@ -161,7 +206,10 @@
 wG.load = function load(text) {
     if(text.match(/^[\s%]*M48/))
         return wG.loadDrill(text);
-    
+//    if(text.match(/^\s*Tool\s+Size\s+Count\s+Plated\s*$/))
+    if(text.match(/^\s*Tool\s+Size\s+Count\s+Plated.*/))
+		return wG.loadHoleReport(text);
+
     text = text.replace(/\s+/g, ''); // Get rid of any spaces/newlines.
     //text = text.replace(/%%+/g, ''); // Compact parameters.
     
@@ -259,6 +307,7 @@
                     g.scale = 25.4;
                 else if(d == 'MOMM') // Specify MMs.
                     g.scale = 1;
+                else if(d[0] == 'I' && d[1] == 'N'){} // File (ignored).
                 else
                     console.log(d);
             } else { // Function codes.
@@ -708,6 +757,13 @@
         wG.touchLimits(layers[i], limits);
         layers[i].enabled = true;
     }
+/* JB: Make outline layer display correctly... */
+    for(var i = 0; i < outlineLayers.length; i++) {
+        wG.touchLimits(outlineLayers[i], limits);
+        outlineLayers[i].enabled = true;
+    }
+/* ... end of outline-layer fix. */
+
     var w = limits.maxX-limits.minX, h = limits.maxY-limits.minY;
     
     var renderer, has3D = true;
@@ -766,9 +822,14 @@
     boardSides = new THREE.Mesh(boardSides, boardMaterial);
     board.add(boardSides);
     
+//    var platedMaterial = new Material({shininess: 80, ambient: 0x333333, specular: 0xcccccc, color: wG.colors[wG.COPPER] /* 0x255005*/ });
+    var platedMaterial = new Material({shininess: 80, ambient: 0x333333, specular: 0xcccccc, color: 0xffff00 /* 0xcc9933 */ /* 0x255005*/ });
     // Create all the holes.
-    var holeMaterial = boardMaterial.clone();
-    holeMaterial.side = THREE.BackSide;
+//    var holeMaterial = boardMaterial.clone();
+    var holeMaterial = [boardMaterial.clone(), platedMaterial.clone()];
+
+    holeMaterial[0].side = THREE.BackSide;
+    holeMaterial[1].side = THREE.BackSide;
     for(var i = 0; i < layers.length; i++)
         if(!layers[i].type)
             for(var j = 0; j < layers[i].cmds.length; j++) {
@@ -776,9 +837,10 @@
                 if(cmd[0] != ((1<<2) | 3))
                     continue;
                 var r = layers[i].scale*layers[i].shapes[cmd[1]][1]/2;
+				var plated = layers[i].shapes[cmd[1]][2];
                 var hole = new THREE.CylinderGeometry(r, r, 1.54, 32, 0, true);
                 //hole.computeVertexNormals();
-                hole = new THREE.Mesh(hole, holeMaterial);
+                hole = new THREE.Mesh(hole, holeMaterial[plated]);
                 hole.position.x = (cmd[2]*layers[i].scale-limits.minX)-w/2;
                 hole.position.z = h/2-(cmd[3]*layers[i].scale-limits.minY);
                 board.add(hole);
@@ -1041,6 +1103,8 @@
                 return alert('There has to be at one selected layer!');
             
             var layers = [];
+			var holereport = null;
+			var drillfile = null;
             for(var i = 0; i < filePairs.length; i++) {
                 var f = filePairs[i], which = f[1].val();
                 if(!which)
@@ -1058,6 +1122,15 @@
                         console.log(e.stack);
                         return;
                     }
+					if(g.holereport)		/* this is an addendum to the drill file */
+					{
+						holereport = g;
+					}
+					if(g.drillfile)
+					{
+						drillfile = g;
+					}
+
                     g.type = this[2];
                     g.side = this[1];
                     g.name = this[0];
@@ -1066,6 +1139,17 @@
                     if(layers.push(g) >= layerNum) {
                         main.remove();
                         loadingOverlay.hide();
+						/* JB: Apply the plating information from the HoleReport.txt file: */
+						if(drillfile && holereport)
+						{
+							for(var key in holereport.shapes)
+							{
+								if(holereport.shapes.hasOwnProperty(key))
+								{
+									drillfile.shapes[key][2] = holereport.shapes[key][2];
+								}
+							}
+						}
                         setTimeout(function() {
                             init(layers);
                         }, 0);
