Преглед на файлове

Improve compatibility with Internet Explorer (#63)

* include promise polyfill

* remove es6

* add viewBox to every icon

* change self closing to explicitly closing tags

* replace window.scroll.. with document.documentElement.scroll..

* verify document.activeElement.blur() is a function before calling

* fix visuals of color chooser (colorpicker still sometimes buggy)

* do not attach to mouseleave event in IE

* attempt removal with usecapture true in IE as well, this fixes the tool switching issue

* fix text tool for IE

* fix cursor for IE

* change curMode to boolean and rename to 'active'

* replace minified canvascolor with unminified code

* replace if else structure with switch statement

* compile polyfill for each browser individually

* remove static polyfills from board.js

* Fix broken js reference

* Fix unused variable and empty console log

* Allow serving non-minified polyfills for easier debugging

* Add proper caching for polyfill.js

This request is on the critical path, we should try to make it fast

* Include more polyfills

All the DOM polyfills were missing

* Update dependencies

* Add missing polyfill

Our custom polyfill was deleted, but the one from polyfill-library hadn't been added

* Remove feature detection for Node.contains

We now have the function in the polyfills

* Update polyfill caching logic

* Remove content-Length header

Co-authored-by: @lovasoa
dev_h
finnboeger преди 5 години
родител
ревизия
57a02c42d6
No account linked to committer's email address

+ 11
- 0
client-data/board.css Целия файл

@@ -255,4 +255,15 @@ circle.opcursor {
255 255
 
256 256
 #cursor-me {
257 257
 	transition: 0s;
258
+}
259
+
260
+
261
+/* Internet Explorer specific CSS */
262
+@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
263
+	#chooseColor {
264
+		color: transparent;
265
+	}
266
+	label.tool-name[for=chooseColor] {
267
+		line-height: 10px;
268
+	}
258 269
 }

+ 7
- 6
client-data/board.html Целия файл

@@ -20,6 +20,7 @@
20 20
 	{{#languages}}
21 21
 	<link rel="alternate" hreflang="{{.}}" href="{{../boardUriComponent}}?lang={{.}}" />
22 22
 	{{/languages}}
23
+	<script src="../polyfill.min.js"></script>
23 24
 </head>
24 25
 
25 26
 <body>
@@ -62,12 +63,12 @@
62 63
 					<span class="tool-icon">
63 64
 						<svg viewBox="0 0 8 8">
64 65
 							<pattern id="opacityPattern" x="0" y="0" width="4" height="4" patternUnits="userSpaceOnUse">
65
-								<rect x=0 y=0 width=2 height=2 fill=black />
66
-								<rect x=2 y=2 width=2 height=2 fill=black />
67
-								<rect x=2 y=0 width=2 height=2 fill=#eeeeee />
68
-								<rect x=0 y=2 width=2 height=2 fill=#eeeeee />
66
+								<rect x=0 y=0 width=2 height=2 fill=black></rect>
67
+								<rect x=2 y=2 width=2 height=2 fill=black></rect>
68
+								<rect x=2 y=0 width=2 height=2 fill=#eeeeee></rect>
69
+								<rect x=0 y=2 width=2 height=2 fill=#eeeeee></rect>
69 70
 							</pattern>
70
-							<circle cx=4 cy=4 id="opacityIndicator" r=3.5 fill="url(#opacityPattern)" />
71
+							<circle cx=4 cy=4 id="opacityIndicator" r=3.5 fill="url(#opacityPattern)"></circle>
71 72
 						</svg>
72 73
 					</span>
73 74
 					<label class="tool-name slider" for="chooseOpacity">
@@ -96,7 +97,7 @@
96 97
 	<script src="../tools/hand/hand.js"></script>
97 98
 	<script src="../tools/grid/grid.js"></script>
98 99
 	<script src="../tools/zoom/zoom.js"></script>
99
-	<script src="../js/canvascolor/canvascolor.min.js"></script>
100
+	<script src="../js/canvascolor.js"></script>
100 101
 </body>
101 102
 
102 103
 </html>

+ 9
- 18
client-data/js/board.js Целия файл

@@ -49,6 +49,8 @@ Tools.showMarker = true;
49 49
 Tools.showOtherCursors = true;
50 50
 Tools.showMyCursor = true;
51 51
 
52
+Tools.isIE = /MSIE|Trident/.test(window.navigator.userAgent);
53
+
52 54
 Tools.socket = null;
53 55
 Tools.connect = function () {
54 56
 	var self = this;
@@ -106,7 +108,7 @@ Tools.HTML = {
106 108
 		};
107 109
 		this.addShortcut(toolShortcut, function () {
108 110
 			Tools.change(toolName);
109
-			document.activeElement.blur();
111
+			document.activeElement.blur && document.activeElement.blur();
110 112
 		});
111 113
 		return this.template.add(function (elem) {
112 114
 			elem.addEventListener("click", callback);
@@ -251,6 +253,8 @@ Tools.removeToolListeners = function removeToolListeners(tool) {
251 253
 		var listener = tool.compiledListeners[event];
252 254
 		var target = listener.target || Tools.board;
253 255
 		target.removeEventListener(event, listener);
256
+		// also attempt to remove with capture = true in IE
257
+		if (Tools.isIE) target.removeEventListener(event, listener, true);
254 258
 	}
255 259
 }
256 260
 
@@ -340,8 +344,8 @@ function updateDocumentTitle() {
340 344
 	var scrollTimeout, lastStateUpdate = Date.now();
341 345
 
342 346
 	window.addEventListener("scroll", function onScroll() {
343
-		var x = window.scrollX / Tools.getScale(),
344
-			y = window.scrollY / Tools.getScale();
347
+		var x = document.documentElement.scrollLeft / Tools.getScale(),
348
+			y = document.documentElement.scrollTop / Tools.getScale();
345 349
 
346 350
 		clearTimeout(scrollTimeout);
347 351
 		scrollTimeout = setTimeout(function updateHistory() {
@@ -455,7 +459,7 @@ Tools.toolHooks = [
455 459
 
456 460
 		function wrapUnsetHover(f, toolName) {
457 461
 			return (function unsetHover(evt) {
458
-				document.activeElement && document.activeElement.blur();
462
+				document.activeElement && document.activeElement.blur && document.activeElement.blur();
459 463
 				return f(evt);
460 464
 			});
461 465
 		}
@@ -472,7 +476,7 @@ Tools.toolHooks = [
472 476
 			var release = compile(listeners.release),
473 477
 				releaseTouch = compileTouch(listeners.release);
474 478
 			compiled["mouseup"] = release;
475
-			compiled["mouseleave"] = release;
479
+			if (!Tools.isIE) compiled["mouseleave"] = release;
476 480
 			compiled["touchleave"] = releaseTouch;
477 481
 			compiled["touchend"] = releaseTouch;
478 482
 			compiled["touchcancel"] = releaseTouch;
@@ -582,19 +586,6 @@ Tools.getOpacity = (function opacity() {
582 586
 Tools.svg.width.baseVal.value = document.body.clientWidth;
583 587
 Tools.svg.height.baseVal.value = document.body.clientHeight;
584 588
 
585
-/***********  Polyfills  ***********/
586
-if (!window.performance || !window.performance.now) {
587
-	window.performance = {
588
-		"now": Date.now
589
-	}
590
-}
591
-if (!Math.hypot) {
592
-	Math.hypot = function (x, y) {
593
-		//The true Math.hypot accepts any number of parameters
594
-		return Math.sqrt(x * x + y * y);
595
-	}
596
-}
597
-
598 589
 /**
599 590
  What does a "tool" object look like?
600 591
  newtool = {

+ 242
- 0
client-data/js/canvascolor.js Целия файл

@@ -0,0 +1,242 @@
1
+/**
2
+ *                CANVASCOLOR color picker
3
+ *********************************************************
4
+ * @licstart  The following is the entire license notice for the
5
+ *  JavaScript code in this page.
6
+ *
7
+ * Copyright (C) 2013-2014  Ophir LOJKINE
8
+ *
9
+ *
10
+ * The JavaScript code in this page is free software: you can
11
+ * redistribute it and/or modify it under the terms of the GNU
12
+ * General Public License (GNU GPL) as published by the Free Software
13
+ * Foundation, either version 3 of the License, or (at your option)
14
+ * any later version.  The code is distributed WITHOUT ANY WARRANTY;
15
+ * without even the implied warranty of MERCHANTABILITY or FITNESS
16
+ * FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
17
+ *
18
+ * As additional permission under GNU GPL version 3 section 7, you
19
+ * may distribute non-source (e.g., minimized or compacted) forms of
20
+ * that code without the copy of the GNU GPL normally required by
21
+ * section 4, provided you include this license notice and a URL
22
+ * through which recipients can access the Corresponding Source.
23
+ *
24
+ * @licend
25
+ */
26
+
27
+
28
+/*jshint bitwise:false*/
29
+
30
+// ==ClosureCompiler==
31
+// @output_file_name canvascolor.js
32
+// @compilation_level ADVANCED_OPTIMIZATIONS
33
+// @js_externs var canvascolor;
34
+// @language ecmascript5_strict
35
+// @use_types_for_optimization true
36
+// ==/ClosureCompiler==
37
+
38
+var canvascolor = (function() {//Code Isolation
39
+    "use strict";
40
+
41
+    (function addCSS () {
42
+        var styleTag = document.createElement("style");
43
+        styleTag.innerHTML = [".canvascolor-container{",
44
+            "background-color:black;",
45
+            "border-radius:5px;",
46
+            "overflow:hidden;",
47
+            "width:179px;",
48
+            "padding:2px;",
49
+            "display:none;",
50
+            "}",
51
+            ".canvascolor-container canvas{",
52
+            "cursor:crosshair;",
53
+            "}",
54
+            ".canvascolor-history{",
55
+            "overflow:auto;",
56
+            "}",
57
+            ".canvascolor-history > div{",
58
+            "margin:2px;",
59
+            "display:inline-block;",
60
+            "}"].join("");
61
+        document.head.appendChild(styleTag);
62
+    })();
63
+
64
+    function hsv2rgb (h,s,v) {
65
+        if( s === 0 ) return [v,v,v]; // achromatic (grey)
66
+
67
+        h /= (Math.PI/6);			// sector 0 to 5
68
+        var i = h|0,
69
+            f = h - i,			// factorial part of h
70
+            p = v * ( 1 - s ),
71
+            q = v * ( 1 - s * f ),
72
+            t = v * ( 1 - s * ( 1 - f ) );
73
+        switch( i%6 ) {
74
+            case 0: return [v,t,p];
75
+            case 1: return [q,v,p];
76
+            case 2: return [p,v,t];
77
+            case 3: return [p,q,v];
78
+            case 4: return [t,p,v];
79
+            case 5:return [v,p,q];
80
+        }
81
+    }
82
+
83
+    function isFixedPosition(elem) {
84
+        do {
85
+            if (getComputedStyle(elem).position === "fixed") return true;
86
+        } while ( (elem = elem.parentElement) !== null );
87
+        return false;
88
+    }
89
+
90
+    var containerTemplate;
91
+    (function createContainer(){
92
+        containerTemplate = document.createElement("div");
93
+        containerTemplate.className = "canvascolor-container";
94
+        var canvas = document.createElement("canvas");
95
+        var historyDiv = document.createElement("div");
96
+        historyDiv.className = "canvascolor-history";
97
+        containerTemplate.appendChild(canvas);
98
+        containerTemplate.appendChild(historyDiv);
99
+    })();
100
+
101
+    function canvascolor(elem) {
102
+        var curcolor = elem.value || "#000";
103
+
104
+        var w=200, h=w/2;
105
+
106
+        var container = containerTemplate.cloneNode(true);
107
+        container.style.width = w+"px";
108
+        container.style.position = isFixedPosition(elem) ? "fixed" : "absolute";
109
+        var canvas = container.getElementsByTagName("canvas")[0];
110
+        var ctx = canvas.getContext("2d");
111
+        canvas.width = w; canvas.height=h;
112
+
113
+        var prevcolorsDiv = container.getElementsByClassName("canvascolor-history")[0];
114
+        prevcolorsDiv.style.width=w+"px";
115
+        prevcolorsDiv.style.maxHeight=h+"px";
116
+
117
+        var previewdiv = createColorDiv(curcolor);
118
+        previewdiv.style.border = "1px solid white";
119
+        previewdiv.style.borderRadius = "5px";
120
+
121
+        document.body.appendChild(container);
122
+
123
+        function displayContainer(){
124
+            var rect = elem.getBoundingClientRect();
125
+            var conttop=(rect.top+rect.height+3),
126
+                contleft=rect.left;
127
+            if (container.style.position !== "fixed") {
128
+                conttop += document.documentElement.scrollTop;
129
+                contleft += document.documentElement.scrollLeft;
130
+            }
131
+            container.style.top = conttop+"px";
132
+            container.style.left = contleft+"px";
133
+            container.style.display = "block";
134
+        }
135
+        function hideContainer(){
136
+            container.style.display = "none";
137
+        }
138
+
139
+        elem.addEventListener("mouseover", displayContainer, true);
140
+        container.addEventListener("mouseleave", hideContainer, false);
141
+        elem.addEventListener("keyup", function(){
142
+            changeColor(elem.value, true);
143
+        }, true);
144
+
145
+        changeColor(elem.value, true);
146
+
147
+        var idata = ctx.createImageData(w,h);
148
+
149
+        function rgb2hex (rgb) {
150
+            function num2hex (c) {return (c*15/255|0).toString(16);}
151
+            return "#"+num2hex(rgb[0])+num2hex(rgb[1])+num2hex(rgb[2]);
152
+        }
153
+
154
+        function colorAt(coords) {
155
+            var x=coords[0], y=coords[1];
156
+            return hsv2rgb(x/w*Math.PI, 1, (1-y/h)*255);
157
+        }
158
+
159
+        function render() {
160
+            for (var x=0; x<w; x++) {
161
+                for (var y=0;y<h; y++) {
162
+                    var i = 4*(x+y*w);
163
+                    var rgb = colorAt([x,y]);
164
+                    idata.data[i] = rgb[0];//Red
165
+                    idata.data[i+1] = rgb[1];//Green
166
+                    idata.data[i+2] = rgb[2];//Blue
167
+                    idata.data[i+3] = 255;
168
+                }
169
+            }
170
+            ctx.putImageData(idata,0,0);
171
+        }
172
+
173
+        render();
174
+
175
+
176
+        /** Changes the current color (the value of the input field) and updates other variables accordingly
177
+         * @param {string} color The new color. Must be a valid CSS color string if ensureValid is not specified
178
+         * @param {boolean} [ensureValid=false] Do not make the change if color is not a valid CSS color
179
+         */
180
+        function changeColor(color, ensureValid) {
181
+            elem.style.backgroundColor = color;
182
+            if (ensureValid && elem.style.backgroundColor.length === 0) {
183
+                elem.style.backgroundColor = curcolor;
184
+                return;
185
+            }
186
+            previewdiv.style.backgroundColor = color;
187
+            curcolor = color;
188
+            elem.value = color;
189
+            elem.focus();
190
+        }
191
+
192
+        function createColorDiv (color) {
193
+            var div = document.createElement("div");
194
+            div.style.width = (w/3-10)+"px";
195
+            div.style.height = (h/3-8)+"px";
196
+            div.style.backgroundColor = color;
197
+            div.addEventListener("click", function(){
198
+                changeColor(color);
199
+            }, true);
200
+            if (prevcolorsDiv.childElementCount <= 1) prevcolorsDiv.appendChild(div);
201
+            else prevcolorsDiv.insertBefore(div,prevcolorsDiv.children[1]);
202
+            return div;
203
+        }
204
+
205
+        function canvasPos(evt) {
206
+            var canvasrect = canvas.getBoundingClientRect();
207
+            return [evt.clientX - canvasrect.left, evt.clientY - canvasrect.top];
208
+        }
209
+
210
+        canvas.addEventListener("mousemove", function(evt){
211
+            var coords = canvasPos(evt);
212
+            previewdiv.style.backgroundColor = rgb2hex(colorAt(coords));
213
+        }, true);
214
+
215
+        canvas.addEventListener("click", function(evt){
216
+            var coords = canvasPos(evt);
217
+            var color = rgb2hex(colorAt(coords));
218
+            createColorDiv(color);
219
+            changeColor(color);
220
+        }, true);
221
+
222
+        canvas.addEventListener("mouseleave", function(){
223
+            previewdiv.style.backgroundColor = curcolor;
224
+        }, true);
225
+    }
226
+
227
+
228
+    //Put a color picker on every input[type=color] if the browser doesn't support this input type
229
+    //and on every input with the class canvascolor
230
+    var pickers = document.querySelectorAll("input.canvascolor, input[type=color]");
231
+    for (var i=0;i <pickers.length; i++) {
232
+        var input = pickers.item(i);
233
+        //If the browser supports native color picker and the user didn't
234
+        //explicitly added canvascolor to the element, we do not add a custom color picker
235
+        if (input.type !== "color" ||
236
+            input.className.split(" ").indexOf("canvascolor") !== -1) {
237
+            canvascolor(input);
238
+        }
239
+    }
240
+
241
+    return canvascolor;
242
+}());

+ 0
- 2
client-data/js/canvascolor/canvascolor.min.js Целия файл

@@ -1,2 +0,0 @@
1
-//gist.github.com/lovasoa/8141243
2
-var canvascolor=(function(){function x(a,c){a/=Math.PI/6;var k=a|0,d=a-k,f=0*c,l=c*(1-1*d),d=c*(1-1*(1-d));switch(k%6){case 0:return[c,d,f];case 1:return[l,c,f];case 2:return[f,c,d];case 3:return[f,l,c];case 4:return[d,f,c];case 5:return[c,f,l]}}function y(a){do if("fixed"===getComputedStyle(a).position)return!0;while(null!==(a=a.parentElement));return!1}function v(a){function c(b){return"#"+(15*b[0]/255|0).toString(16)+(15*b[1]/255|0).toString(16)+(15*b[2]/255|0).toString(16)}function k(b){return x(b[0]/ g*Math.PI,255*(1-b[1]/m))}function d(b,z){a.style.backgroundColor=b;z&&0===a.style.backgroundColor.length?a.style.backgroundColor=t:(t=q.style.backgroundColor=b,a.value=b)}function f(b){var a=document.createElement("div");a.style.width=g/3-10+"px";a.style.height=m/3-8+"px";a.style.backgroundColor=b;a.addEventListener("click",function(){d(b)},!0);1>=n.childElementCount?n.appendChild(a):n.insertBefore(a,n.children[1]);return a}function l(b){var a=h.getBoundingClientRect();return[b.clientX- a.left,b.clientY-a.top]}var t=a.value||"#000",g=200,m=g/2,e=r.cloneNode(!0);e.style.width=g+"px";e.style.position=y(a)?"fixed":"absolute";var h=e.getElementsByTagName("canvas")[0],p=h.getContext("2d");h.width=g;h.height=m;var n=e.getElementsByClassName("canvascolor-history")[0];n.style.width=g+"px";n.style.maxHeight=m+"px";var q=f(t);q.style.border="1px solid white";q.style.a="5px";document.body.appendChild(e);a.addEventListener("mouseover",function(){var b=a.getBoundingClientRect(),c=b.top+b.height+ 3,b=b.left;"fixed"!==e.style.position&&(c+=window.scrollY,b+=window.scrollX);e.style.top=c+"px";e.style.left=b+"px";e.style.display="block"},!0);e.addEventListener("mouseleave",function(){e.style.display="none"},!1);a.addEventListener("keyup",function(){d(a.value,!0)},!0);d(a.value,!0);var s=p.createImageData(g,m);(function(){for(var b=0;b<g;b++)for(var a=0;a<m;a++){var c=4*(b+a*g),d=k([b,a]);s.data[c]=d[0];s.data[c+1]=d[1];s.data[c+2]=d[2];s.data[c+3]=255}p.putImageData(s,0,0)})();h.addEventListener("mousemove", function(a){a=l(a);q.style.backgroundColor=c(k(a))},!0);h.addEventListener("click",function(a){a=l(a);a=c(k(a));f(a);d(a)},!0);h.addEventListener("mouseleave",function(){q.style.backgroundColor=t},!0)}(function(){var a=document.createElement("style");a.innerHTML=".canvascolor-container{background-color:black;border-radius:5px;overflow:hidden;width:179px;padding:2px;display:none;}.canvascolor-container canvas{cursor:crosshair;}.canvascolor-history{overflow:auto;}.canvascolor-history > div{margin:2px;display:inline-block;}"; document.head.appendChild(a)})();var r;(function(){r=document.createElement("div");r.className="canvascolor-container";var a=document.createElement("canvas"),c=document.createElement("div");c.className="canvascolor-history";r.appendChild(a);r.appendChild(c)})();for(var w=document.querySelectorAll("input.canvascolor, input[type=color]"),p=0;p<w.length;p++){var u=w.item(p);"color"===u.type&&-1===u.className.split(" ").indexOf("canvascolor")||v(u)}return v})();

+ 2
- 1
client-data/tools/cursor/cursor.js Целия файл

@@ -106,7 +106,8 @@
106 106
     function draw(message) {
107 107
         var cursor = getCursor("cursor-" + (message.socket || 'me'));
108 108
         cursor.style.transform = "translate(" + message.x + "px, " + message.y + "px)";
109
+        if (Tools.isIE) cursor.setAttributeNS(null, "transform", "translate(" + message.x + " " + message.y + ")");
109 110
         cursor.setAttributeNS(null, "fill", message.color);
110 111
         cursor.setAttributeNS(null, "r", message.size / 2);
111 112
     }
112
-})()
113
+})();

+ 1
- 1
client-data/tools/ellipse/icon-circle.svg Целия файл

@@ -1,3 +1,3 @@
1
-<svg width="60" height="60" xmlns="http://www.w3.org/2000/svg">
1
+<svg width="60" height="60" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
2 2
     <circle cx="30" cy="30" r="15" stroke="black" stroke-width="2" fill="none" />
3 3
 </svg>

+ 1
- 1
client-data/tools/ellipse/icon-ellipse.svg Целия файл

@@ -1,3 +1,3 @@
1
-<svg width="60" height="60" xmlns="http://www.w3.org/2000/svg">
1
+<svg width="60" height="60" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
2 2
     <ellipse cx="30" cy="30" rx="15" ry="10" stroke="black" stroke-width="2" fill="none" />
3 3
 </svg>

+ 1
- 12
client-data/tools/eraser/eraser.js Целия файл

@@ -41,18 +41,7 @@
41 41
 	};
42 42
 
43 43
 	function inDrawingArea(elem) {
44
-		if (Tools.drawingArea.contains) {
45
-			return Tools.drawingArea.contains(elem);
46
-		} else {
47
-			var node = elem.parentNode;
48
-			while (node != null) {
49
-				if (node === Tools.drawingArea) {
50
-					return true;
51
-				}
52
-				node = node.parentNode;
53
-			}
54
-			return false;
55
-		}
44
+		return Tools.drawingArea.contains(elem);
56 45
 	}
57 46
 
58 47
 	function erase(x, y, evt) {

+ 2
- 2
client-data/tools/hand/hand.js Целия файл

@@ -31,8 +31,8 @@
31 31
 	function press(x, y, evt, isTouchEvent) {
32 32
 		if (!isTouchEvent) {
33 33
 			pressed = true;
34
-			orig.x = scrollX + evt.clientX;
35
-			orig.y = scrollY + evt.clientY;
34
+			orig.x = document.documentElement.scrollLeft + evt.clientX;
35
+			orig.y = document.documentElement.scrollTop + evt.clientY;
36 36
 		}
37 37
 	}
38 38
 	function move(x, y, evt, isTouchEvent) {

+ 1
- 1
client-data/tools/line/icon.svg Целия файл

@@ -1,3 +1,3 @@
1
-<svg xmlns="http://www.w3.org/2000/svg" height="60" width="60">
1
+<svg xmlns="http://www.w3.org/2000/svg" height="60" width="60" viewBox="0 0 60 60">
2 2
     <path d="M12.06 50.69l23.1-14.25-12.3-16.36 18.8-13.02" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3 3
 </svg>

+ 1
- 1
client-data/tools/rect/icon.svg Целия файл

@@ -1,3 +1,3 @@
1
-<svg width="60" height="60" xmlns="http://www.w3.org/2000/svg">
1
+<svg width="60" height="60" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
2 2
     <path fill="none" stroke="#000" stroke-width="2" d="M15 20h30v20H15z"/>
3 3
 </svg>

+ 14
- 6
client-data/tools/text/text.js Целия файл

@@ -45,6 +45,8 @@
45 45
 		"lastSending": 0
46 46
 	};
47 47
 
48
+	var active = false;
49
+
48 50
 
49 51
 	function onStart() {
50 52
 		curText.oldSize = Tools.getSize();
@@ -52,6 +54,7 @@
52 54
 	}
53 55
 
54 56
 	function onQuit() {
57
+		stopEdit();
55 58
 		Tools.setSize(curText.oldSize);
56 59
 	}
57 60
 
@@ -78,8 +81,8 @@
78 81
 	function editOldText(elem) {
79 82
 		curText.id = elem.id;
80 83
 		var r = elem.getBoundingClientRect();
81
-		var x = (r.x + document.documentElement.scrollLeft) / Tools.scale;
82
-		var y = (r.y + r.height + document.documentElement.scrollTop) / Tools.scale;
84
+		var x = (r.left + document.documentElement.scrollLeft) / Tools.scale;
85
+		var y = (r.top + r.height + document.documentElement.scrollTop) / Tools.scale;
83 86
 
84 87
 		curText.x = x;
85 88
 		curText.y = y;
@@ -91,17 +94,18 @@
91 94
 	}
92 95
 
93 96
 	function startEdit() {
97
+		active = true;
94 98
 		if (!input.parentNode) board.appendChild(input);
95 99
 		input.value = "";
96
-		var left = curText.x - scrollX + 'px';
100
+		var left = curText.x - document.documentElement.scrollLeft + 'px';
97 101
 		var clientW = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
98
-		var x = curText.x * Tools.scale - scrollX;
102
+		var x = curText.x * Tools.scale - document.documentElement.scrollLeft;
99 103
 		if (x + 250 > clientW) {
100 104
 			x = Math.max(60, clientW - 260)
101 105
 		}
102 106
 
103 107
 		input.style.left = x + 'px';
104
-		input.style.top = curText.y * Tools.scale - scrollY + 20 + 'px';
108
+		input.style.top = curText.y * Tools.scale - document.documentElement.scrollTop + 20 + 'px';
105 109
 		input.focus();
106 110
 		input.addEventListener("keyup", textChangeHandler);
107 111
 		input.addEventListener("blur", textChangeHandler);
@@ -109,13 +113,17 @@
109 113
 	}
110 114
 
111 115
 	function stopEdit() {
112
-		input.blur();
116
+		try { input.blur(); } catch (e) { /* Internet Explorer */ }
117
+		active = false;
118
+		blur();
113 119
 		curText.id = 0;
114 120
 		curText.sentText = "";
121
+		input.value = "";
115 122
 		input.removeEventListener("keyup", textChangeHandler);
116 123
 	}
117 124
 
118 125
 	function blur() {
126
+		if (active) return;
119 127
 		input.style.top = '-1000px';
120 128
 	}
121 129
 

+ 1
- 1
client-data/tools/zoom/icon.svg Целия файл

@@ -1,4 +1,4 @@
1
-<svg width="60" height="60" xmlns="http://www.w3.org/2000/svg">
1
+<svg width="60" height="60" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
2 2
     <g fill="none" stroke="#000" stroke-linecap="round" stroke-width="2">
3 3
         <path d="M25.65 31.33l4.04 2.58L20.06 49a2.36 2.36 0 01-3.26.66 2.36 2.36 0 01-.78-3.24zM42.2 9.77a13.43 13.43 0 014.1 18.54 13.43 13.43 0 01-18.54 4.1 13.43 13.43 0 01-4.1-18.54 13.43 13.43 0 0118.54-4.1z" stroke-linejoin="round" stroke-miterlimit="10" />
4 4
         <path d="M34.83 28.9V12.98m-7.97 8.2H42.8" />

+ 6
- 6
client-data/tools/zoom/zoom.js Целия файл

@@ -27,8 +27,8 @@
27 27
 (function () { //Code isolation
28 28
     var ZOOM_FACTOR = .5;
29 29
     var origin = {
30
-        scrollX: window.scrollX,
31
-        scrollY: window.scrollY,
30
+        scrollX: document.documentElement.scrollLeft,
31
+        scrollY: document.documentElement.scrollTop,
32 32
         x: 0.0,
33 33
         y: 0.0,
34 34
         clientY: 0,
@@ -54,8 +54,8 @@
54 54
     }
55 55
 
56 56
     function setOrigin(x, y, evt, isTouchEvent) {
57
-        origin.scrollX = window.scrollX;
58
-        origin.scrollY = window.scrollY;
57
+        origin.scrollX = document.documentElement.scrollLeft;
58
+        origin.scrollY = document.documentElement.scrollTop;
59 59
         origin.x = x;
60 60
         origin.y = y;
61 61
         origin.clientY = getClientY(evt, isTouchEvent);
@@ -95,10 +95,10 @@
95 95
             Tools.setSize(Tools.getSize() - ((evt.deltaY > 0) - (evt.deltaY < 0)) * change);
96 96
         } else if (evt.shiftKey) {
97 97
             // scroll horizontally
98
-            window.scrollTo(window.scrollX + evt.deltaY, window.scrollY + evt.deltaX);
98
+            window.scrollTo(document.documentElement.scrollLeft + evt.deltaY, document.documentElement.scrollTop + evt.deltaX);
99 99
         } else {
100 100
             // regular scrolling
101
-            window.scrollTo(window.scrollX + evt.deltaX, window.scrollY + evt.deltaY);
101
+            window.scrollTo(document.documentElement.scrollLeft + evt.deltaX, document.documentElement.scrollTop + evt.deltaY);
102 102
         }
103 103
     }
104 104
     Tools.board.addEventListener("wheel", onwheel, { passive: false });

+ 795
- 369
package-lock.json
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 2
- 1
package.json Целия файл

@@ -10,6 +10,7 @@
10 10
    "dependencies": {
11 11
       "accept-language-parser": "^1.5.0",
12 12
       "handlebars": "^4.7.6",
13
+      "polyfill-library": "^3.89.4",
13 14
       "serve-static": "^1.14.1",
14 15
       "socket.io": "^2.3.0"
15 16
    },
@@ -24,6 +25,6 @@
24 25
    },
25 26
    "devDependencies": {
26 27
       "geckodriver": "^1.19.1",
27
-      "nightwatch": "^1.3.4"
28
+      "nightwatch": "^1.3.5"
28 29
    }
29 30
 }

+ 87
- 56
server/server.js Целия файл

@@ -8,7 +8,8 @@ var app = require('http').createServer(handler)
8 8
 	, serveStatic = require("serve-static")
9 9
 	, createSVG = require("./createSVG.js")
10 10
 	, templating = require("./templating.js")
11
-	, config = require("./configuration.js");
11
+	, config = require("./configuration.js")
12
+	, polyfillLibrary = require('polyfill-library');
12 13
 
13 14
 
14 15
 var MIN_NODE_VERSION = 10.0;
@@ -72,63 +73,93 @@ function handleRequest(request, response) {
72 73
 	var parts = parsedUrl.pathname.split('/');
73 74
 	if (parts[0] === '') parts.shift();
74 75
 
75
-	if (parts[0] === "boards") {
76
-		// "boards" refers to the root directory
77
-		if (parts.length === 1 && parsedUrl.query.board) {
78
-			// '/boards?board=...' This allows html forms to point to boards
79
-			var headers = { Location: 'boards/' + encodeURIComponent(parsedUrl.query.board) };
80
-			response.writeHead(301, headers);
81
-			response.end();
82
-		} else if (parts.length === 2 && request.url.indexOf('.') === -1) {
83
-			// If there is no dot and no directory, parts[1] is the board name
84
-			boardTemplate.serve(request, response);
85
-		} else { // Else, it's a resource
86
-			request.url = parts.slice(1).join('/');
87
-			fileserver(request, response, serveError(request, response));
88
-		}
89
-	} else if (parts[0] === "download") {
90
-		var boardName = encodeURIComponent(parts[1]),
91
-			history_file = path.join(config.HISTORY_DIR, "board-" + boardName + ".json");
92
-		if (parts.length > 2 && /^[0-9A-Za-z.\-]+$/.test(parts[2])) {
93
-			history_file += '.' + parts[2] + '.bak';
94
-		}
95
-		log("download", { "file": history_file });
96
-		fs.readFile(history_file, function (err, data) {
97
-			if (err) return serveError(request, response)(err);
98
-			response.writeHead(200, {
99
-				"Content-Type": "application/json",
100
-				"Content-Disposition": 'attachment; filename="' + boardName + '.wbo"',
101
-				"Content-Length": data.length,
102
-			});
103
-			response.end(data);
104
-		});
105
-	} else if (parts[0] === "preview") {
106
-		var boardName = encodeURIComponent(parts[1]),
107
-			history_file = path.join(config.HISTORY_DIR, "board-" + boardName + ".json");
108
-		createSVG.renderBoard(history_file, function (err, svg) {
109
-			if (err) {
110
-				log(err);
111
-				response.writeHead(404, { 'Content-Type': 'application/json' });
112
-				return response.end(JSON.stringify(err));
76
+	switch (parts[0]) {
77
+		case "boards":
78
+			// "boards" refers to the root directory
79
+			if (parts.length === 1 && parsedUrl.query.board) {
80
+				// '/boards?board=...' This allows html forms to point to boards
81
+				var headers = { Location: 'boards/' + encodeURIComponent(parsedUrl.query.board) };
82
+				response.writeHead(301, headers);
83
+				response.end();
84
+			} else if (parts.length === 2 && request.url.indexOf('.') === -1) {
85
+				// If there is no dot and no directory, parts[1] is the board name
86
+				boardTemplate.serve(request, response);
87
+			} else { // Else, it's a resource
88
+				request.url = parts.slice(1).join('/');
89
+				fileserver(request, response, serveError(request, response));
113 90
 			}
114
-			response.writeHead(200, {
115
-				"Content-Type": "image/svg+xml",
116
-				"Content-Security-Policy": CSP,
117
-				"Content-Length": Buffer.byteLength(svg),
118
-				"Cache-Control": "public, max-age=7200",
91
+			break;
92
+
93
+		case "download":
94
+			var boardName = encodeURIComponent(parts[1]),
95
+				history_file = path.join(config.HISTORY_DIR, "board-" + boardName + ".json");
96
+			if (parts.length > 2 && /^[0-9A-Za-z.\-]+$/.test(parts[2])) {
97
+				history_file += '.' + parts[2] + '.bak';
98
+			}
99
+			log("download", { "file": history_file });
100
+			fs.readFile(history_file, function (err, data) {
101
+				if (err) return serveError(request, response)(err);
102
+				response.writeHead(200, {
103
+					"Content-Type": "application/json",
104
+					"Content-Disposition": 'attachment; filename="' + boardName + '.wbo"',
105
+					"Content-Length": data.length,
106
+				});
107
+				response.end(data);
108
+			});
109
+			break;
110
+
111
+		case "preview":
112
+			var boardName = encodeURIComponent(parts[1]),
113
+				history_file = path.join(config.HISTORY_DIR, "board-" + boardName + ".json");
114
+			createSVG.renderBoard(history_file, function (err, svg) {
115
+				if (err) {
116
+					log(err);
117
+					response.writeHead(404, { 'Content-Type': 'application/json' });
118
+					return response.end(JSON.stringify(err));
119
+				}
120
+				response.writeHead(200, {
121
+					"Content-Type": "image/svg+xml",
122
+					"Content-Security-Policy": CSP,
123
+					"Content-Length": Buffer.byteLength(svg),
124
+					"Cache-Control": "public, max-age=7200",
125
+				});
126
+				response.end(svg);
119 127
 			});
120
-			response.end(svg);
121
-		});
122
-	} else if (parts[0] === "random") {
123
-		var name = crypto.randomBytes(32).toString('base64').replace(/[^\w]/g, '-');
124
-		response.writeHead(307, { 'Location': 'boards/' + name });
125
-		response.end(name);
126
-
127
-	} else if (parts[0] === "") { // Index page
128
-		logRequest(request);
129
-		indexTemplate.serve(request, response);
130
-	} else {
131
-		fileserver(request, response, serveError(request, response));
128
+			break;
129
+
130
+		case "random":
131
+			var name = crypto.randomBytes(32).toString('base64').replace(/[^\w]/g, '-');
132
+			response.writeHead(307, { 'Location': 'boards/' + name });
133
+			response.end(name);
134
+			break;
135
+
136
+		case "polyfill.js": // serve tailored polyfills
137
+		case "polyfill.min.js":
138
+			polyfillLibrary.getPolyfillString({
139
+				uaString: request.headers['user-agent'],
140
+				minify: request.url.endsWith(".min.js"),
141
+				features: {
142
+					'default': { flags: ['gated'] },
143
+					'es5': { flags: ['gated'] },
144
+					'es6': { flags: ['gated'] },
145
+					'es7': { flags: ['gated'] },
146
+					'performance.now': { flags: ['gated'] },
147
+				}
148
+			}).then(function (bundleString) {
149
+				response.setHeader('Cache-Control', 'public, max-age=172800, stale-while-revalidate=1728000');
150
+				response.setHeader('Vary', 'User-Agent');
151
+				response.setHeader('Content-Type', 'application/javascript');
152
+				response.end(bundleString);
153
+			});
154
+			break;
155
+
156
+		case "": // Index page
157
+			logRequest(request);
158
+			indexTemplate.serve(request, response);
159
+			break;
160
+
161
+		default:
162
+			fileserver(request, response, serveError(request, response));
132 163
 	}
133 164
 }
134 165
 

Loading…
Отказ
Запис