graph wip
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1 +1,4 @@ | |||||||
| media_import/* | media_import/*.img | ||||||
|  | media_import/*.size | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								media_import/placeholder
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | placeholder | ||||||
|  |  | ||||||
| @@ -2293,14 +2293,14 @@ class ClonOS | |||||||
| 			), | 			), | ||||||
| 		); | 		); | ||||||
|  |  | ||||||
| 		$result='0 mb'; | 		$result='0 MB'; | ||||||
| 		foreach($arBytes as $arItem) | 		foreach($arBytes as $arItem) | ||||||
| 		{ | 		{ | ||||||
| 			if($bytes >= $arItem["VALUE"]) | 			if($bytes >= $arItem["VALUE"]) | ||||||
| 			{ | 			{ | ||||||
| 				$result = $bytes / $arItem["VALUE"]; | 				$result = $bytes / $arItem["VALUE"]; | ||||||
| 				if($round) $result=round($result); | 				if($round) $result=round($result); | ||||||
| 				$result = str_replace(".", "," , strval(round($result, 2)))." ".$arItem["UNIT"]; | 				$result = str_replace(".", "," , strval(round($result, 2)))." ".strtoupper($arItem["UNIT"]); | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -359,6 +359,16 @@ p.log-p:before { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | .row2col:after { | ||||||
|  | 	content: ""; | ||||||
|  | 	display: table; | ||||||
|  | 	clear: both; | ||||||
|  | } | ||||||
|  | .row2col .column { | ||||||
|  | 	float: left; | ||||||
|  | 	width: 50%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* Таблицы: http://html5book.ru/examples/demo-tables.html */ | /* Таблицы: http://html5book.ru/examples/demo-tables.html */ | ||||||
|  |  | ||||||
| @@ -629,6 +639,29 @@ tr.busy.maintenance .ops span { | |||||||
| .vnc-grey { | .vnc-grey { | ||||||
| 	color:gray; | 	color:gray; | ||||||
| } | } | ||||||
|  | .vnc-wait { | ||||||
|  | 	position:absolute; | ||||||
|  | 	width:100%; | ||||||
|  | 	background-color:white; | ||||||
|  | 	opacity:0.7; | ||||||
|  | 	top:25px; | ||||||
|  | 	bottom:0; | ||||||
|  | 	font-weight:bold; | ||||||
|  | 	font-size:150%; | ||||||
|  | } | ||||||
|  | .vnc-wait .outer { | ||||||
|  | 	height:100%; | ||||||
|  | 	text-align:center; | ||||||
|  | } | ||||||
|  | .vnc-wait .inner { | ||||||
|  |     margin: auto; | ||||||
|  |     height: 30px; | ||||||
|  |     position: absolute; | ||||||
|  |     top: 0; | ||||||
|  |     left: 0; | ||||||
|  |     bottom: 0; | ||||||
|  |     right: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* td.ops span.icon-play:before { | /* td.ops span.icon-play:before { | ||||||
| 	margin-left:.2em; | 	margin-left:.2em; | ||||||
| @@ -1483,12 +1516,6 @@ div.smoothie-chart-tooltip { | |||||||
|     box-shadow:3px 3px 3px 0px rgba(0,0,0,0.1); |     box-shadow:3px 3px 3px 0px rgba(0,0,0,0.1); | ||||||
| 	line-height: 120%; | 	line-height: 120%; | ||||||
| } | } | ||||||
| .smoothie-chart-tooltip :nth-child(2):before { |  | ||||||
| 	content:'cpu: ' |  | ||||||
| } |  | ||||||
| .smoothie-chart-tooltip :nth-child(4):before { |  | ||||||
| 	content:'mem: ' |  | ||||||
| } |  | ||||||
| .smoothie-chart-tooltip :nth-child(2):after, | .smoothie-chart-tooltip :nth-child(2):after, | ||||||
| .smoothie-chart-tooltip :nth-child(4):after { | .smoothie-chart-tooltip :nth-child(4):after { | ||||||
| 	content:'%' | 	content:'%' | ||||||
| @@ -1496,3 +1523,7 @@ div.smoothie-chart-tooltip { | |||||||
| td.graph { | td.graph { | ||||||
| 	/*background-color:black;*/ | 	/*background-color:black;*/ | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .row2col div.smoothie-chart-tooltip { | ||||||
|  | 	background:gray; | ||||||
|  | } | ||||||
| @@ -7,5 +7,13 @@ | |||||||
| 			<span class="dialog-close"></span> | 			<span class="dialog-close"></span> | ||||||
| 		</span> | 		</span> | ||||||
| 	</div> | 	</div> | ||||||
|  | 	<div class="vnc-wait"> | ||||||
|  | 		<div class="outer"> | ||||||
|  | 			<div class="inner"> | ||||||
|  | 				<span class="icon-spin5 animate-spin"></span>Please, wait for initialize Virtual Machine | ||||||
|  | 				<br /><small style="display: block;font-size: small;margin-top: 20px;">Нужно найти куда воткнуть событие, что машина инициализирована и вызвать мою функцию, которая закроет перектывающий фон.</small> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
| 	<iframe src="about:blank" id="vnc-iframe" border="0" width="1026" height="802"></iframe> | 	<iframe src="about:blank" id="vnc-iframe" border="0" width="1026" height="802"></iframe> | ||||||
| </dialog> | </dialog> | ||||||
| @@ -90,6 +90,7 @@ var clonos={ | |||||||
| 		if(this.manual_close_menu) return; | 		if(this.manual_close_menu) return; | ||||||
| 		var wdt=$(window).width(); | 		var wdt=$(window).width(); | ||||||
| 		if(wdt<800) $('body').addClass('gadget'); else $('body').removeClass('gadget'); | 		if(wdt<800) $('body').addClass('gadget'); else $('body').removeClass('gadget'); | ||||||
|  | 		setTimeout(graphs.onResize,500); | ||||||
| 	}, | 	}, | ||||||
| 	closerClick:function(event) | 	closerClick:function(event) | ||||||
| 	{ | 	{ | ||||||
| @@ -937,6 +938,13 @@ var clonos={ | |||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 			for(id in data) $('#'+id).html(data[id]); | 			for(id in data) $('#'+id).html(data[id]); | ||||||
|  | 			 | ||||||
|  | 			var razd=location.pathname; | ||||||
|  | 			if(['/overview/'].indexOf(razd)!=-1) | ||||||
|  | 			{ | ||||||
|  | 				clonos.createGraphs(); | ||||||
|  | 				console.log(data.id); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 	 | 	 | ||||||
| @@ -986,7 +994,7 @@ var clonos={ | |||||||
| 		// --- | 		// --- | ||||||
|  |  | ||||||
| 		// Если мы в нужной таблице, то рисуем графики | 		// Если мы в нужной таблице, то рисуем графики | ||||||
| 		if(data.id=='bhyveslist' || data.id=='jailslist') | 		if(['bhyveslist','jailslist'].indexOf(data.id)!=-1) | ||||||
| 		{ | 		{ | ||||||
| 			clonos.createGraphs(); | 			clonos.createGraphs(); | ||||||
| 		} | 		} | ||||||
| @@ -2643,7 +2651,7 @@ var clonos={ | |||||||
| 				{ | 				{ | ||||||
| 					data.data['vm_ram']=ram.replace(/^\d+([gmt])$/gi,function(orig,lett) | 					data.data['vm_ram']=ram.replace(/^\d+([gmt])$/gi,function(orig,lett) | ||||||
| 					{ | 					{ | ||||||
| 						var a={'m':' MB','g':' GB','t':' TB'}[lett.toLowerCase()]; | 						var a={'m':' MB','g':' GB','t':' TB'}[lett.toUpperCase()];	//!!! | ||||||
| 						return orig.replace(lett,a); | 						return orig.replace(lett,a); | ||||||
| 					}); | 					}); | ||||||
| 				}else{ | 				}else{ | ||||||
| @@ -3044,7 +3052,7 @@ var clonos={ | |||||||
| 	 | 	 | ||||||
| 	createGraphs:function() | 	createGraphs:function() | ||||||
| 	{ | 	{ | ||||||
| 		var grs=$('td.graph'); | 		var grs=$('.graph'); | ||||||
| 		for(n=0,nl=grs.length;n<nl;n++) | 		for(n=0,nl=grs.length;n<nl;n++) | ||||||
| 		{ | 		{ | ||||||
| 			var gr=grs[n]; | 			var gr=grs[n]; | ||||||
| @@ -3058,14 +3066,23 @@ var clonos={ | |||||||
| 		var cl=$(gr).attr('class'); | 		var cl=$(gr).attr('class'); | ||||||
| 		var width=$(gr).width(); | 		var width=$(gr).width(); | ||||||
| 		var height=$(gr).height(); | 		var height=$(gr).height(); | ||||||
| 		var res=cl.match(/g-([^ ]+)/); | 		var tooltip1=''; | ||||||
|  | 		var tooltip2=''; | ||||||
|  | 		res=cl.match(/\bl-([^ ]+)\b/); | ||||||
|  | 		if(res!=null) | ||||||
|  | 		{ | ||||||
|  | 			res=res[1].split(','); | ||||||
|  | 			tooltip1=res[0]; | ||||||
|  | 			tooltip2=res[1]; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		var res=cl.match(/\bg-([^ ]+)\b/); | ||||||
| 		if(res!=null) | 		if(res!=null) | ||||||
| 		{ | 		{ | ||||||
| 			var name=res[1]; | 			var name=res[1]; | ||||||
| 			var g=new graph(name,width,height,gr); | 			var g=new graph(name,width,height,gr,tooltip1,tooltip2); | ||||||
| 			g.create(); | 			g.create(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	}, | 	}, | ||||||
| 	 | 	 | ||||||
| 	createGraphById:function(id) | 	createGraphById:function(id) | ||||||
| @@ -3097,7 +3114,7 @@ graphs={ | |||||||
| 	wsconnect:function() | 	wsconnect:function() | ||||||
| 	{ | 	{ | ||||||
| 		this.client_id=this.name; | 		this.client_id=this.name; | ||||||
| 		this.socket = new WebSocket("ws://"+_server_name+":8024/graph/client-"+Math.random()); | 		this.socket = new WebSocket("ws://"+_server_name+":8024/graph"+location.pathname+"client-"+Math.random()); | ||||||
| 		$(this.socket).on('open',$.proxy(this.wsopen,this)) | 		$(this.socket).on('open',$.proxy(this.wsopen,this)) | ||||||
| 			.on('close',$.proxy(this.wsclose,this)) | 			.on('close',$.proxy(this.wsclose,this)) | ||||||
| 			.on('error',$.proxy(this.wserror,this)) | 			.on('error',$.proxy(this.wserror,this)) | ||||||
| @@ -3135,6 +3152,42 @@ graphs={ | |||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
|  | 		if(typeof msg.__all!='undefined') | ||||||
|  | 		{ | ||||||
|  | 			for(a in msg.__all) | ||||||
|  | 			{ | ||||||
|  | 				var items=msg.__all[a]; | ||||||
|  | 				for(i in items) | ||||||
|  | 				{ | ||||||
|  | 					var item=items[i]; | ||||||
|  | 					var gr=this.list[item.name]; | ||||||
|  | 					var date = new Date(); | ||||||
|  | 					date.setTime(item.time*1000); | ||||||
|  | 					if(gr) | ||||||
|  | 					{ | ||||||
|  | 						var cpu,mem; | ||||||
|  | 						if(typeof item.pcpu!='undefined') | ||||||
|  | 						{ | ||||||
|  | 							gr.line1.append(date.getTime(), item.pcpu); | ||||||
|  | 						} | ||||||
|  | 						if(typeof item.pmem!='undefined') | ||||||
|  | 						{ | ||||||
|  | 							gr.line2.append(date.getTime(), item.pmem); | ||||||
|  | 						} | ||||||
|  | 					}else{ | ||||||
|  | 						var gr=this.list[item.name+'-pcpu']; | ||||||
|  | 						{ | ||||||
|  | 							if(gr) gr.line1.append(date.getTime(), item.pcpu); | ||||||
|  | 						} | ||||||
|  | 						var gr=this.list[item.name+'-pmem']; | ||||||
|  | 						{ | ||||||
|  | 							if(gr) gr.line1.append(date.getTime(), item.pmem); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}else{ | ||||||
|  | 			 | ||||||
| 			var larr=[]; | 			var larr=[]; | ||||||
| 			for(l in this.list) | 			for(l in this.list) | ||||||
| 				larr.push(this.list[l].name) | 				larr.push(this.list[l].name) | ||||||
| @@ -3146,19 +3199,30 @@ graphs={ | |||||||
| 					var inf=msg[n]; | 					var inf=msg[n]; | ||||||
| 					var name=inf['name']; | 					var name=inf['name']; | ||||||
| 					var gr=this.list[name]; | 					var gr=this.list[name]; | ||||||
|  | 					var date = new Date(); | ||||||
|  | 					date.setTime(inf.time*1000); | ||||||
| 					if(gr) | 					if(gr) | ||||||
| 					{ | 					{ | ||||||
| 						var cpu=inf.pcpu, mem=inf.pmem; | 						var cpu=inf.pcpu, mem=inf.pmem; | ||||||
| 					//cpu=cpu+(cpu%2); | 						gr.line1.append(date.getTime(), cpu); | ||||||
| 					//mem=mem+(mem%2); | 						gr.line2.append(date.getTime(), mem); | ||||||
| 					//cpu=Math.floor((Math.random() * 100) + 1); |  | ||||||
| 					//mem=Math.floor((Math.random() * 100) + 1); |  | ||||||
| 					gr.line1.append(new Date().getTime(), cpu); |  | ||||||
| 					gr.line2.append(new Date().getTime(), mem); |  | ||||||
| 						var res=larr.indexOf(name); | 						var res=larr.indexOf(name); | ||||||
| 						if(res>-1) larr.splice(res,1); | 						if(res>-1) larr.splice(res,1); | ||||||
| 					 | 					}else{ | ||||||
| 					//console.log(name,cpu,mem); | 						var nname=name+'-pcpu'; | ||||||
|  | 						var gr=this.list[nname]; | ||||||
|  | 						{ | ||||||
|  | 							if(gr) gr.line1.append(date.getTime(), inf.pcpu); | ||||||
|  | 							var res=larr.indexOf(nname); | ||||||
|  | 							if(res>-1) larr.splice(res,1); | ||||||
|  | 						} | ||||||
|  | 						var nname=name+'-pmem'; | ||||||
|  | 						var gr=this.list[nname]; | ||||||
|  | 						{ | ||||||
|  | 							if(gr) gr.line1.append(date.getTime(), inf.pmem); | ||||||
|  | 							var res=larr.indexOf(nname); | ||||||
|  | 							if(res>-1) larr.splice(res,1); | ||||||
|  | 						} | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @@ -3168,9 +3232,24 @@ graphs={ | |||||||
| 				this.list[larr[n]].line1.append(new Date().getTime(), 0); | 				this.list[larr[n]].line1.append(new Date().getTime(), 0); | ||||||
| 				this.list[larr[n]].line2.append(new Date().getTime(), 0); | 				this.list[larr[n]].line2.append(new Date().getTime(), 0); | ||||||
| 			} | 			} | ||||||
|  | 			 | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	onResize:function() | ||||||
|  | 	{ | ||||||
|  | 		// ресайз не работает. Нужно найти нормальный способ | ||||||
|  | 		/* | ||||||
|  | 		var graphs=$('.graph'); | ||||||
|  | 		for(n=0,nl=graphs.length;n<nl;n++) | ||||||
|  | 		{ | ||||||
|  | 			var gpar=$(graphs[n]).parent(); | ||||||
|  | 			var width=$(gpar).width(); | ||||||
|  | 			$('canvas',graphs[n]).width(width); | ||||||
|  | 		} | ||||||
|  | 		*/ | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| function graph(name,width,height,el_parent) | function graph(name,width,height,el_parent,tooltip1,tooltip2) | ||||||
| { | { | ||||||
| 	this.name=name; | 	this.name=name; | ||||||
| 	this.height=height; | 	this.height=height; | ||||||
| @@ -3181,10 +3260,74 @@ function graph(name,width,height,el_parent) | |||||||
| 	this.line1=null; | 	this.line1=null; | ||||||
| 	this.line2=null; | 	this.line2=null; | ||||||
| 	this.connected=false; | 	this.connected=false; | ||||||
|  | 	this.tooltip1=tooltip1; | ||||||
|  | 	this.tooltip2=tooltip2; | ||||||
|  | 	 | ||||||
|  | 	this.graphView={ | ||||||
|  | 		white:{ | ||||||
|  | 			view:{ | ||||||
|  | 				interpolation:'linear', | ||||||
|  | 				grid:{ | ||||||
|  | 					fillStyle:'rgba(0,0,0,0.02)', | ||||||
|  | 					sharpLines:true, | ||||||
|  | 					strokeStyle:'transparent', | ||||||
|  | 					borderVisible:true | ||||||
|  | 				}, | ||||||
|  | 				labels:{ | ||||||
|  | 					fontSize:8, | ||||||
|  | 					disabled:true, | ||||||
|  | 					fillStyle:'#000000', | ||||||
|  | 					precision:0 | ||||||
|  | 				}, | ||||||
|  | 				millisPerPixel:1000, | ||||||
|  | 				enableDpiScaling:false, | ||||||
|  | 				tooltip:true, | ||||||
|  | 				maxValue:100, | ||||||
|  | 				minValue:0 | ||||||
|  | 			}, | ||||||
|  | 			colors:{ | ||||||
|  | 				line1_color:'rgb(17,125,187)', | ||||||
|  | 				line1_fillStyle:'rgba(17,125,187,0.03)', | ||||||
|  | 				line2_color:'rgb(149,40,180)', | ||||||
|  | 				line2_fillStyle:'rgba(149,40,180,0.03)' | ||||||
|  | 			}, | ||||||
|  | 			lineWidth:0.5, | ||||||
|  | 		}, | ||||||
|  | 		black:{ | ||||||
|  | 			view:{ | ||||||
|  | 				interpolation:'linear', | ||||||
|  | 				grid:{ | ||||||
|  | 					//fillStyle:'rgba(100,100,100,0.02)', | ||||||
|  | 					sharpLines:true, | ||||||
|  | 					//strokeStyle:'transparent', | ||||||
|  | 					borderVisible:true, | ||||||
|  | 					verticalSections:4, | ||||||
|  | 				}, | ||||||
|  | 				labels:{ | ||||||
|  | 					//fontSize:8, | ||||||
|  | 					//disabled:true, | ||||||
|  | 					//fillStyle:'#000000', | ||||||
|  | 					precision:0, | ||||||
|  | 				}, | ||||||
|  | 				millisPerPixel:100, | ||||||
|  | 				enableDpiScaling:false, | ||||||
|  | 				tooltip:true, | ||||||
|  | 				maxValue:100, | ||||||
|  | 				minValue:0, | ||||||
|  | 				tooltipLabel:'test', | ||||||
|  | 			}, | ||||||
|  | 			colors:{ | ||||||
|  | 				line1_color:'rgb(0,255,0)', | ||||||
|  | 				line1_fillStyle:'rgba(0,255,0,0.4)', | ||||||
|  | 				line2_color:'rgb(255,0,255)', | ||||||
|  | 				line2_fillStyle:'rgba(255,0,255,0.3)' | ||||||
|  | 			}, | ||||||
|  | 			lineWidth:1, | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
| } | } | ||||||
| graph.prototype.create=function() | graph.prototype.create=function() | ||||||
| { | { | ||||||
| 	 |  | ||||||
| 	var el_parent=$(this.el_parent); | 	var el_parent=$(this.el_parent); | ||||||
| 	if(typeof el_parent=='undefined') | 	if(typeof el_parent=='undefined') | ||||||
| 	{ | 	{ | ||||||
| @@ -3195,19 +3338,30 @@ graph.prototype.create=function() | |||||||
| 	} | 	} | ||||||
| 	$(el_parent).append('<canvas id="g-'+this.name+'" width="'+this.width+'" height="'+this.height+'" vertical-align="middle"></canvas>'); | 	$(el_parent).append('<canvas id="g-'+this.name+'" width="'+this.width+'" height="'+this.height+'" vertical-align="middle"></canvas>'); | ||||||
| 	 | 	 | ||||||
|  | 	var view; | ||||||
|  | 	var cl=$(el_parent).attr('class'); | ||||||
|  | 	var res=cl.match(/v-([a-z]+)/); | ||||||
|  | 	if(res!=null) | ||||||
|  | 	{ | ||||||
|  | 		view=res[1]; | ||||||
|  | 	}else{ | ||||||
|  | 		view='white'; | ||||||
|  | 	} | ||||||
|  | 	var varr=this.graphView[view]; | ||||||
|  | 	 | ||||||
| 	this.line1 = new TimeSeries({resetBounds:false,resetBoundsInterval:3000}); | 	this.line1 = new TimeSeries({resetBounds:false,resetBoundsInterval:3000}); | ||||||
| 	this.line2 = new TimeSeries({resetBounds:false,resetBoundsInterval:3000}); | 	this.line2 = new TimeSeries({resetBounds:false,resetBoundsInterval:3000}); | ||||||
| 	 | 	 | ||||||
| 	var back_color='rgba(0,0,0,0.02)'; | 	var back_color='rgba(0,0,0,0.02)'; | ||||||
| 	var line1_color='rgb(17,125,187)'; //'blue';	//'rgb(0,0,255)'; | 	var line1_color=varr.colors.line1_color; | ||||||
| 	var line1_fillStyle='rgba(17,125,187,0.03)'; //'rgba(241, 240, 255, 0.4)'; | 	var line1_fillStyle=varr.colors.line1_fillStyle; | ||||||
| 	var line2_color='rgb(149,40,180)'; //'green'; //'rgb(0,255,0)'; | 	var line2_color=varr.colors.line2_color; | ||||||
| 	var line2_fillStyle='rgba(149,40,180,0.03)'; //'rgba(222,245,222,0.4)'; | 	var line2_fillStyle=varr.colors.line2_fillStyle; | ||||||
| 	 | 	 | ||||||
| 	this.smoothie = new SmoothieChart({interpolation:'bezier',grid:{fillStyle:back_color,sharpLines:true,strokeStyle:'transparent',borderVisible:true},labels:{fontSize:8,disabled:true,fillStyle:'#000000',precision:0},millisPerPixel:1000,enableDpiScaling:true,tooltip:true,maxValue:100,minValue:0}); | 	this.smoothie = new SmoothieChart(varr.view); | ||||||
|  |  | ||||||
| 	this.smoothie.addTimeSeries(this.line1, { strokeStyle: line1_color, lineWidth:0.5, fillStyle:line1_fillStyle }); //, fillStyle: 'rgba(0, 255, 0, 0.4)' | 	this.smoothie.addTimeSeries(this.line1, { strokeStyle: line1_color, lineWidth:varr.lineWidth, fillStyle:line1_fillStyle, tooltipLabel:this.tooltip1+':' }); | ||||||
| 	this.smoothie.addTimeSeries(this.line2, { strokeStyle: line2_color, lineWidth:0.5, fillStyle:line2_fillStyle }); //, fillStyle: 'rgba(255, 0, 255, 0.3)' | 	this.smoothie.addTimeSeries(this.line2, { strokeStyle: line2_color, lineWidth:varr.lineWidth, fillStyle:line2_fillStyle, tooltipLabel:this.tooltip2+':' }); | ||||||
|  |  | ||||||
| 	this.smoothie.streamTo(document.getElementById('g-'+this.name), 1000); | 	this.smoothie.streamTo(document.getElementById('g-'+this.name), 1000); | ||||||
| 	graphs.list[this.name]=this; | 	graphs.list[this.name]=this; | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								public/novnc/CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,54 @@ | |||||||
|  | How to contribute to noVNC | ||||||
|  | ========================== | ||||||
|  |  | ||||||
|  | We accept code via pull requests on GitHub.  There are several guidelines that | ||||||
|  | we expect contributors submitting code requests to follow.  If you have issues | ||||||
|  | following any of these guidelines, feel free to drop us a line by leaving a | ||||||
|  | comment in the code request or sending us an email. | ||||||
|  |  | ||||||
|  | Contributing Guidelines | ||||||
|  | ----------------------- | ||||||
|  |  | ||||||
|  | * While we don't have an official coding style guide, please try to follow | ||||||
|  |   the general coding style of the existing code. | ||||||
|  | ** Use four spaces instead of tabs  | ||||||
|  | ** prefix private variables and functions with an `_` | ||||||
|  |  | ||||||
|  | * Please try to include unit tests for your code.  For instance, if you | ||||||
|  |   introduce a new encoding, add a test to `tests/test.rfb.js` under the | ||||||
|  |   "Encoding Handlers" section (basically, input a small pattern in your | ||||||
|  |   encoding and make sure the pattern gets displayed correctly).  If you | ||||||
|  |   fix a bug, try to add a unit test that would have caught that bug | ||||||
|  |   (if possible -- some bugs, especially visual ones, are hard to test for). | ||||||
|  |  | ||||||
|  | * Squash your commits down in to a clean commit history.  For instance, there | ||||||
|  |   should not be "cleanup" commits where you fix issues in previous commits in | ||||||
|  |   the same pull request.  Before you go to commit, use `git rebase -i` to | ||||||
|  |   squash these changes into the relevant commits.  For instance, a good commit | ||||||
|  |   history might look like "Added support for FOO encoding, Added support for | ||||||
|  |   BAR message, Placed Button in UI to Trigger BAR" (where each comma denotes | ||||||
|  |   a separate commit). | ||||||
|  |  | ||||||
|  | * Add both a title and description to your commit, if possible.  Place more | ||||||
|  |   detail on what you did in the description. | ||||||
|  |  | ||||||
|  | Running the unit tests | ||||||
|  | ---------------------- | ||||||
|  |  | ||||||
|  | There are two ways to run the unit tests.  For both ways, you should first run | ||||||
|  | `npm install` (not as root). | ||||||
|  |  | ||||||
|  | The first way to run the tests is to run `npm test`.  This will run all the | ||||||
|  | tests in the headless PhantomJS browser (which uses WebKit). | ||||||
|  |  | ||||||
|  | The second way to run the tests is using the `tests/run_from_console.js` file. | ||||||
|  | This way is a bit more flexible, and can provide more information about what | ||||||
|  | went wrong.  To run all the tests, simply run `tests/run_from_console.js`. | ||||||
|  | To run a specific test file, you can use the `-t path/to/test/file.js` option. | ||||||
|  | If you wish to simply generate the HTML for the test, use the `-g` option, and | ||||||
|  | the path to the temporary HTML file will be written to standard out.  To open | ||||||
|  | this file in your default browser automatically, pass the `-o` option as well. | ||||||
|  | More information can be found by passing the `--help` or `-h` option. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Thanks, and happy coding! | ||||||
							
								
								
									
										84
									
								
								public/novnc/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,84 @@ | |||||||
|  | noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org> | ||||||
|  |  | ||||||
|  | The noVNC core library files are licensed under the MPL 2.0 (Mozilla | ||||||
|  | Public License 2.0). The noVNC core library is composed of the | ||||||
|  | Javascript code necessary for full noVNC operation. This includes (but | ||||||
|  | is not limited to): | ||||||
|  |  | ||||||
|  |     include/base64.js | ||||||
|  |     include/des.js | ||||||
|  |     include/display.js | ||||||
|  |     include/input.js | ||||||
|  |     include/keysym.js | ||||||
|  |     include/logo.js | ||||||
|  |     include/playback.js | ||||||
|  |     include/rfb.js | ||||||
|  |     include/ui.js | ||||||
|  |     include/util.js | ||||||
|  |     include/websock.js | ||||||
|  |     include/webutil.js | ||||||
|  |  | ||||||
|  | The HTML, CSS, font and images files that included with the noVNC | ||||||
|  | source distibution (or repository) are not considered part of the | ||||||
|  | noVNC core library and are licensed under more permissive licenses. | ||||||
|  | The intent is to allow easy integration of noVNC into existing web | ||||||
|  | sites and web applications. | ||||||
|  |  | ||||||
|  | The HTML, CSS, font and image files are licensed as follows: | ||||||
|  |  | ||||||
|  |     *.html                     : 2-Clause BSD license | ||||||
|  |  | ||||||
|  |     include/*.css              : 2-Clause BSD license | ||||||
|  |  | ||||||
|  |     include/Orbitron*          : SIL Open Font License 1.1 | ||||||
|  |                                  (Copyright 2009 Matt McInerney) | ||||||
|  |  | ||||||
|  |     images/                    : Creative Commons Attribution-ShareAlike | ||||||
|  |                                  http://creativecommons.org/licenses/by-sa/3.0/ | ||||||
|  |  | ||||||
|  | Some portions of noVNC are copyright to their individual authors. | ||||||
|  | Please refer to the individual source files and/or to the noVNC commit | ||||||
|  | history: https://github.com/kanaka/noVNC/commits/master | ||||||
|  |  | ||||||
|  | The are several files and projects that have been incorporated into | ||||||
|  | the noVNC core library. Here is a list of those files and the original | ||||||
|  | licenses (all MPL 2.0 compatible): | ||||||
|  |  | ||||||
|  |     include/base64.js          : MPL 2.0 | ||||||
|  |     | ||||||
|  |     include/des.js             : Various BSD style licenses | ||||||
|  |  | ||||||
|  |     include/chrome-app/tcp-stream.js | ||||||
|  |                                : Apache 2.0 license | ||||||
|  |  | ||||||
|  |     utils/websockify | ||||||
|  |     utils/websocket.py         : LGPL 3 | ||||||
|  |      | ||||||
|  |     utils/inflator.partial.js | ||||||
|  |     include/inflator.js        : MIT (for pako) | ||||||
|  |  | ||||||
|  | Any other files not mentioned above are typically marked with | ||||||
|  | a copyright/license header at the top of the file. The default noVNC | ||||||
|  | license is MPL-2.0. | ||||||
|  |  | ||||||
|  | The following license texts are included: | ||||||
|  |  | ||||||
|  |     docs/LICENSE.MPL-2.0 | ||||||
|  |     docs/LICENSE.LGPL-3 and | ||||||
|  |     docs/LICENSE.GPL-3 | ||||||
|  |     docs/LICENSE.OFL-1.1 | ||||||
|  |     docs/LICENSE.BSD-3-Clause (New BSD) | ||||||
|  |     docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD) | ||||||
|  |     docs/LICENSE.zlib | ||||||
|  |     docs/LICENSE.Apache-2.0 | ||||||
|  |     docs/LICENSE.pako | ||||||
|  |  | ||||||
|  | Or alternatively the license texts may be found here: | ||||||
|  |  | ||||||
|  |     http://www.mozilla.org/MPL/2.0/ | ||||||
|  |     http://www.gnu.org/licenses/lgpl.html and | ||||||
|  |     http://www.gnu.org/licenses/gpl.html | ||||||
|  |     http://scripts.sil.org/OFL | ||||||
|  |     http://en.wikipedia.org/wiki/BSD_licenses | ||||||
|  |     http://www.gzip.org/zlib/zlib_license.html | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0.html | ||||||
							
								
								
									
										132
									
								
								public/novnc/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,132 @@ | |||||||
|  | ## noVNC: HTML5 VNC Client | ||||||
|  |  | ||||||
|  | [](https://travis-ci.org/kanaka/noVNC) | ||||||
|  |  | ||||||
|  | ### Description | ||||||
|  |  | ||||||
|  | noVNC is a HTML5 VNC client that runs well in any modern browser | ||||||
|  | including mobile browsers (iPhone/iPad and Android). | ||||||
|  |  | ||||||
|  | Many companies/projects have integrated noVNC including [Ganeti Web | ||||||
|  | Manager](http://code.osuosl.org/projects/ganeti-webmgr), | ||||||
|  | [OpenStack](http://www.openstack.org), | ||||||
|  | [OpenNebula](http://opennebula.org/), and | ||||||
|  | [LibVNCServer](http://libvncserver.sourceforge.net). See [the Projects | ||||||
|  | and Companies wiki | ||||||
|  | page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC) | ||||||
|  | for a more complete list with additional info and links. | ||||||
|  |  | ||||||
|  | ### News/help/contact | ||||||
|  |  | ||||||
|  | Notable commits, announcements and news are posted to | ||||||
|  | <a href="http://www.twitter.com/noVNC">@noVNC</a> | ||||||
|  |  | ||||||
|  | If you are a noVNC developer/integrator/user (or want to be) please | ||||||
|  | join the <a | ||||||
|  | href="https://groups.google.com/forum/?fromgroups#!forum/novnc">noVNC | ||||||
|  | discussion group</a> | ||||||
|  |  | ||||||
|  | Bugs and feature requests can be submitted via [github | ||||||
|  | issues](https://github.com/kanaka/noVNC/issues). If you are looking | ||||||
|  | for a place to start contributing to noVNC, a good place to start | ||||||
|  | would be the issues that are marked as | ||||||
|  | ["patchwelcome"](https://github.com/kanaka/noVNC/issues?labels=patchwelcome). | ||||||
|  |  | ||||||
|  | If you want to show appreciation for noVNC you could donate to a great | ||||||
|  | non-profits such as: [Compassion | ||||||
|  | International](http://www.compassion.com/), [SIL](http://www.sil.org), | ||||||
|  | [Habitat for Humanity](http://www.habitat.org), [Electronic Frontier | ||||||
|  | Foundation](https://www.eff.org/), [Against Malaria | ||||||
|  | Foundation](http://www.againstmalaria.com/), [Nothing But | ||||||
|  | Nets](http://www.nothingbutnets.net/), etc. Please tweet <a | ||||||
|  | href="http://www.twitter.com/noVNC">@noVNC</a> if you do. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Features | ||||||
|  |  | ||||||
|  | * Supports all modern browsers including mobile (iOS, Android) | ||||||
|  | * Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG | ||||||
|  | * WebSocket SSL/TLS encryption (i.e. "wss://") support | ||||||
|  | * 24-bit true color and 8 bit colour mapped | ||||||
|  | * Supports desktop resize notification/pseudo-encoding | ||||||
|  | * Local or remote cursor | ||||||
|  | * Clipboard copy/paste | ||||||
|  | * Clipping or scolling modes for large remote screens | ||||||
|  | * Easy site integration and theming (3 example themes included) | ||||||
|  | * Licensed under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/) | ||||||
|  |  | ||||||
|  | ### Screenshots | ||||||
|  |  | ||||||
|  | Running in Chrome before and after connecting: | ||||||
|  |  | ||||||
|  | <img src="http://kanaka.github.com/noVNC/img/noVNC-5.png" width=400> <img src="http://kanaka.github.com/noVNC/img/noVNC-7.jpg" width=400> | ||||||
|  |  | ||||||
|  | See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">here</a>. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Browser Requirements | ||||||
|  |  | ||||||
|  | * HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS | ||||||
|  |   Safari, Opera 11+, Internet Explorer 9+, etc. | ||||||
|  |  | ||||||
|  | * HTML5 WebSockets and Typed Arrays | ||||||
|  |  | ||||||
|  | * Fast Javascript Engine: this is not strictly a requirement, but | ||||||
|  |   without a fast Javascript engine, noVNC might be painfully slow. | ||||||
|  |  | ||||||
|  | * See the more detailed [browser compatibility wiki page](https://github.com/kanaka/noVNC/wiki/Browser-support). | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Server Requirements | ||||||
|  |  | ||||||
|  | Unless you are using a VNC server with support for WebSockets | ||||||
|  | connections (such as | ||||||
|  | [x11vnc/libvncserver](http://libvncserver.sourceforge.net/), | ||||||
|  | [QEMU](http://www.qemu.org/), or | ||||||
|  | [PocketVNC](http://www.pocketvnc.com/blog/?page_id=866)), you need to | ||||||
|  | use a WebSockets to TCP socket proxy. There is a python proxy included | ||||||
|  | ('websockify'). | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Quick Start | ||||||
|  |  | ||||||
|  | * Use the launch script to start a mini-webserver and the WebSockets | ||||||
|  |   proxy (websockify). The `--vnc` option is used to specify the location of | ||||||
|  |   a running VNC server: | ||||||
|  |  | ||||||
|  |     `./utils/launch.sh --vnc localhost:5901` | ||||||
|  |  | ||||||
|  | * Point your browser to the cut-and-paste URL that is output by the | ||||||
|  |   launch script. Enter a password if the VNC server has one | ||||||
|  |   configured. Hit the Connect button and enjoy! | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Other Pages | ||||||
|  |  | ||||||
|  | * [Encrypted Connections](https://github.com/kanaka/websockify/wiki/Encrypted-Connections). How to setup websockify so that you can use encrypted connections from noVNC. | ||||||
|  |  | ||||||
|  | * [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Starting a VNC server, advanced websockify usage, etc. | ||||||
|  |  | ||||||
|  | * [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects. | ||||||
|  |  | ||||||
|  | * [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Authors/Contributors | ||||||
|  |  | ||||||
|  | * Core team: | ||||||
|  |     * [Joel Martin](https://github.com/kanaka) | ||||||
|  |     * [Samuel Mannehed](https://github.com/samhed) (Cendio) | ||||||
|  |     * [Peter Åstrand](https://github.com/astrand) (Cendio) | ||||||
|  |     * [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack) | ||||||
|  |  | ||||||
|  | * Notable contributions: | ||||||
|  |     * UI and Icons : Chris Gordon | ||||||
|  |     * Original Logo : Michael Sersen | ||||||
|  |     * tight encoding : Michael Tinglof (Mercuri.ca) | ||||||
|  |  | ||||||
|  | * Included libraries: | ||||||
|  |     * as3crypto : Henri Torgemane (code.google.com/p/as3crypto) | ||||||
|  |     * base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net) | ||||||
|  |     * DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs) | ||||||
|  |     * Pako : Vitaly Puzrin (https://github.com/nodeca/pako) | ||||||
							
								
								
									
										202
									
								
								public/novnc/docs/LICENSE.Apache-2.0
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,202 @@ | |||||||
|  |  | ||||||
|  |                                  Apache License | ||||||
|  |                            Version 2.0, January 2004 | ||||||
|  |                         http://www.apache.org/licenses/ | ||||||
|  |  | ||||||
|  |    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||||
|  |  | ||||||
|  |    1. Definitions. | ||||||
|  |  | ||||||
|  |       "License" shall mean the terms and conditions for use, reproduction, | ||||||
|  |       and distribution as defined by Sections 1 through 9 of this document. | ||||||
|  |  | ||||||
|  |       "Licensor" shall mean the copyright owner or entity authorized by | ||||||
|  |       the copyright owner that is granting the License. | ||||||
|  |  | ||||||
|  |       "Legal Entity" shall mean the union of the acting entity and all | ||||||
|  |       other entities that control, are controlled by, or are under common | ||||||
|  |       control with that entity. For the purposes of this definition, | ||||||
|  |       "control" means (i) the power, direct or indirect, to cause the | ||||||
|  |       direction or management of such entity, whether by contract or | ||||||
|  |       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||||
|  |       outstanding shares, or (iii) beneficial ownership of such entity. | ||||||
|  |  | ||||||
|  |       "You" (or "Your") shall mean an individual or Legal Entity | ||||||
|  |       exercising permissions granted by this License. | ||||||
|  |  | ||||||
|  |       "Source" form shall mean the preferred form for making modifications, | ||||||
|  |       including but not limited to software source code, documentation | ||||||
|  |       source, and configuration files. | ||||||
|  |  | ||||||
|  |       "Object" form shall mean any form resulting from mechanical | ||||||
|  |       transformation or translation of a Source form, including but | ||||||
|  |       not limited to compiled object code, generated documentation, | ||||||
|  |       and conversions to other media types. | ||||||
|  |  | ||||||
|  |       "Work" shall mean the work of authorship, whether in Source or | ||||||
|  |       Object form, made available under the License, as indicated by a | ||||||
|  |       copyright notice that is included in or attached to the work | ||||||
|  |       (an example is provided in the Appendix below). | ||||||
|  |  | ||||||
|  |       "Derivative Works" shall mean any work, whether in Source or Object | ||||||
|  |       form, that is based on (or derived from) the Work and for which the | ||||||
|  |       editorial revisions, annotations, elaborations, or other modifications | ||||||
|  |       represent, as a whole, an original work of authorship. For the purposes | ||||||
|  |       of this License, Derivative Works shall not include works that remain | ||||||
|  |       separable from, or merely link (or bind by name) to the interfaces of, | ||||||
|  |       the Work and Derivative Works thereof. | ||||||
|  |  | ||||||
|  |       "Contribution" shall mean any work of authorship, including | ||||||
|  |       the original version of the Work and any modifications or additions | ||||||
|  |       to that Work or Derivative Works thereof, that is intentionally | ||||||
|  |       submitted to Licensor for inclusion in the Work by the copyright owner | ||||||
|  |       or by an individual or Legal Entity authorized to submit on behalf of | ||||||
|  |       the copyright owner. For the purposes of this definition, "submitted" | ||||||
|  |       means any form of electronic, verbal, or written communication sent | ||||||
|  |       to the Licensor or its representatives, including but not limited to | ||||||
|  |       communication on electronic mailing lists, source code control systems, | ||||||
|  |       and issue tracking systems that are managed by, or on behalf of, the | ||||||
|  |       Licensor for the purpose of discussing and improving the Work, but | ||||||
|  |       excluding communication that is conspicuously marked or otherwise | ||||||
|  |       designated in writing by the copyright owner as "Not a Contribution." | ||||||
|  |  | ||||||
|  |       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||||
|  |       on behalf of whom a Contribution has been received by Licensor and | ||||||
|  |       subsequently incorporated within the Work. | ||||||
|  |  | ||||||
|  |    2. Grant of Copyright License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       copyright license to reproduce, prepare Derivative Works of, | ||||||
|  |       publicly display, publicly perform, sublicense, and distribute the | ||||||
|  |       Work and such Derivative Works in Source or Object form. | ||||||
|  |  | ||||||
|  |    3. Grant of Patent License. Subject to the terms and conditions of | ||||||
|  |       this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |       (except as stated in this section) patent license to make, have made, | ||||||
|  |       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||||
|  |       where such license applies only to those patent claims licensable | ||||||
|  |       by such Contributor that are necessarily infringed by their | ||||||
|  |       Contribution(s) alone or by combination of their Contribution(s) | ||||||
|  |       with the Work to which such Contribution(s) was submitted. If You | ||||||
|  |       institute patent litigation against any entity (including a | ||||||
|  |       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||||
|  |       or a Contribution incorporated within the Work constitutes direct | ||||||
|  |       or contributory patent infringement, then any patent licenses | ||||||
|  |       granted to You under this License for that Work shall terminate | ||||||
|  |       as of the date such litigation is filed. | ||||||
|  |  | ||||||
|  |    4. Redistribution. You may reproduce and distribute copies of the | ||||||
|  |       Work or Derivative Works thereof in any medium, with or without | ||||||
|  |       modifications, and in Source or Object form, provided that You | ||||||
|  |       meet the following conditions: | ||||||
|  |  | ||||||
|  |       (a) You must give any other recipients of the Work or | ||||||
|  |           Derivative Works a copy of this License; and | ||||||
|  |  | ||||||
|  |       (b) You must cause any modified files to carry prominent notices | ||||||
|  |           stating that You changed the files; and | ||||||
|  |  | ||||||
|  |       (c) You must retain, in the Source form of any Derivative Works | ||||||
|  |           that You distribute, all copyright, patent, trademark, and | ||||||
|  |           attribution notices from the Source form of the Work, | ||||||
|  |           excluding those notices that do not pertain to any part of | ||||||
|  |           the Derivative Works; and | ||||||
|  |  | ||||||
|  |       (d) If the Work includes a "NOTICE" text file as part of its | ||||||
|  |           distribution, then any Derivative Works that You distribute must | ||||||
|  |           include a readable copy of the attribution notices contained | ||||||
|  |           within such NOTICE file, excluding those notices that do not | ||||||
|  |           pertain to any part of the Derivative Works, in at least one | ||||||
|  |           of the following places: within a NOTICE text file distributed | ||||||
|  |           as part of the Derivative Works; within the Source form or | ||||||
|  |           documentation, if provided along with the Derivative Works; or, | ||||||
|  |           within a display generated by the Derivative Works, if and | ||||||
|  |           wherever such third-party notices normally appear. The contents | ||||||
|  |           of the NOTICE file are for informational purposes only and | ||||||
|  |           do not modify the License. You may add Your own attribution | ||||||
|  |           notices within Derivative Works that You distribute, alongside | ||||||
|  |           or as an addendum to the NOTICE text from the Work, provided | ||||||
|  |           that such additional attribution notices cannot be construed | ||||||
|  |           as modifying the License. | ||||||
|  |  | ||||||
|  |       You may add Your own copyright statement to Your modifications and | ||||||
|  |       may provide additional or different license terms and conditions | ||||||
|  |       for use, reproduction, or distribution of Your modifications, or | ||||||
|  |       for any such Derivative Works as a whole, provided Your use, | ||||||
|  |       reproduction, and distribution of the Work otherwise complies with | ||||||
|  |       the conditions stated in this License. | ||||||
|  |  | ||||||
|  |    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||||
|  |       any Contribution intentionally submitted for inclusion in the Work | ||||||
|  |       by You to the Licensor shall be under the terms and conditions of | ||||||
|  |       this License, without any additional terms or conditions. | ||||||
|  |       Notwithstanding the above, nothing herein shall supersede or modify | ||||||
|  |       the terms of any separate license agreement you may have executed | ||||||
|  |       with Licensor regarding such Contributions. | ||||||
|  |  | ||||||
|  |    6. Trademarks. This License does not grant permission to use the trade | ||||||
|  |       names, trademarks, service marks, or product names of the Licensor, | ||||||
|  |       except as required for reasonable and customary use in describing the | ||||||
|  |       origin of the Work and reproducing the content of the NOTICE file. | ||||||
|  |  | ||||||
|  |    7. Disclaimer of Warranty. Unless required by applicable law or | ||||||
|  |       agreed to in writing, Licensor provides the Work (and each | ||||||
|  |       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||||
|  |       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||||
|  |       implied, including, without limitation, any warranties or conditions | ||||||
|  |       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||||
|  |       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||||
|  |       appropriateness of using or redistributing the Work and assume any | ||||||
|  |       risks associated with Your exercise of permissions under this License. | ||||||
|  |  | ||||||
|  |    8. Limitation of Liability. In no event and under no legal theory, | ||||||
|  |       whether in tort (including negligence), contract, or otherwise, | ||||||
|  |       unless required by applicable law (such as deliberate and grossly | ||||||
|  |       negligent acts) or agreed to in writing, shall any Contributor be | ||||||
|  |       liable to You for damages, including any direct, indirect, special, | ||||||
|  |       incidental, or consequential damages of any character arising as a | ||||||
|  |       result of this License or out of the use or inability to use the | ||||||
|  |       Work (including but not limited to damages for loss of goodwill, | ||||||
|  |       work stoppage, computer failure or malfunction, or any and all | ||||||
|  |       other commercial damages or losses), even if such Contributor | ||||||
|  |       has been advised of the possibility of such damages. | ||||||
|  |  | ||||||
|  |    9. Accepting Warranty or Additional Liability. While redistributing | ||||||
|  |       the Work or Derivative Works thereof, You may choose to offer, | ||||||
|  |       and charge a fee for, acceptance of support, warranty, indemnity, | ||||||
|  |       or other liability obligations and/or rights consistent with this | ||||||
|  |       License. However, in accepting such obligations, You may act only | ||||||
|  |       on Your own behalf and on Your sole responsibility, not on behalf | ||||||
|  |       of any other Contributor, and only if You agree to indemnify, | ||||||
|  |       defend, and hold each Contributor harmless for any liability | ||||||
|  |       incurred by, or claims asserted against, such Contributor by reason | ||||||
|  |       of your accepting any such warranty or additional liability. | ||||||
|  |  | ||||||
|  |    END OF TERMS AND CONDITIONS | ||||||
|  |  | ||||||
|  |    APPENDIX: How to apply the Apache License to your work. | ||||||
|  |  | ||||||
|  |       To apply the Apache License to your work, attach the following | ||||||
|  |       boilerplate notice, with the fields enclosed by brackets "[]" | ||||||
|  |       replaced with your own identifying information. (Don't include | ||||||
|  |       the brackets!)  The text should be enclosed in the appropriate | ||||||
|  |       comment syntax for the file format. We also recommend that a | ||||||
|  |       file or class name and description of purpose be included on the | ||||||
|  |       same "printed page" as the copyright notice for easier | ||||||
|  |       identification within third-party archives. | ||||||
|  |  | ||||||
|  |    Copyright [yyyy] [name of copyright owner] | ||||||
|  |  | ||||||
|  |    Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |    you may not use this file except in compliance with the License. | ||||||
|  |    You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |        http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  |    Unless required by applicable law or agreed to in writing, software | ||||||
|  |    distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |    See the License for the specific language governing permissions and | ||||||
|  |    limitations under the License. | ||||||
							
								
								
									
										22
									
								
								public/novnc/docs/LICENSE.BSD-2-Clause
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,22 @@ | |||||||
|  | Copyright (c) <year>, <copyright holder> | ||||||
|  | All rights reserved. | ||||||
|  |  | ||||||
|  | Redistribution and use in source and binary forms, with or without | ||||||
|  | modification, are permitted provided that the following conditions are met: | ||||||
|  |  | ||||||
|  |     * Redistributions of source code must retain the above copyright | ||||||
|  |       notice, this list of conditions and the following disclaimer. | ||||||
|  |     * Redistributions in binary form must reproduce the above copyright | ||||||
|  |       notice, this list of conditions and the following disclaimer in the | ||||||
|  |       documentation and/or other materials provided with the distribution. | ||||||
|  |  | ||||||
|  | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||||
|  | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  | DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | ||||||
|  | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||||
|  | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||||
|  | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||||
|  | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
							
								
								
									
										24
									
								
								public/novnc/docs/LICENSE.BSD-3-Clause
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,24 @@ | |||||||
|  | Copyright (c) <year>, <copyright holder> | ||||||
|  | All rights reserved. | ||||||
|  |  | ||||||
|  | Redistribution and use in source and binary forms, with or without | ||||||
|  | modification, are permitted provided that the following conditions are met: | ||||||
|  |     * Redistributions of source code must retain the above copyright | ||||||
|  |       notice, this list of conditions and the following disclaimer. | ||||||
|  |     * Redistributions in binary form must reproduce the above copyright | ||||||
|  |       notice, this list of conditions and the following disclaimer in the | ||||||
|  |       documentation and/or other materials provided with the distribution. | ||||||
|  |     * Neither the name of the <organization> nor the | ||||||
|  |       names of its contributors may be used to endorse or promote products | ||||||
|  |       derived from this software without specific prior written permission. | ||||||
|  |  | ||||||
|  | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||||
|  | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
|  | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  | DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | ||||||
|  | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||||
|  | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||||
|  | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||||
|  | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
							
								
								
									
										621
									
								
								public/novnc/docs/LICENSE.GPL-3
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,621 @@ | |||||||
|  |                     GNU GENERAL PUBLIC LICENSE | ||||||
|  |                        Version 3, 29 June 2007 | ||||||
|  |  | ||||||
|  |  Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | ||||||
|  |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  |  of this license document, but changing it is not allowed. | ||||||
|  |  | ||||||
|  |                             Preamble | ||||||
|  |  | ||||||
|  |   The GNU General Public License is a free, copyleft license for | ||||||
|  | software and other kinds of works. | ||||||
|  |  | ||||||
|  |   The licenses for most software and other practical works are designed | ||||||
|  | to take away your freedom to share and change the works.  By contrast, | ||||||
|  | the GNU General Public License is intended to guarantee your freedom to | ||||||
|  | share and change all versions of a program--to make sure it remains free | ||||||
|  | software for all its users.  We, the Free Software Foundation, use the | ||||||
|  | GNU General Public License for most of our software; it applies also to | ||||||
|  | any other work released this way by its authors.  You can apply it to | ||||||
|  | your programs, too. | ||||||
|  |  | ||||||
|  |   When we speak of free software, we are referring to freedom, not | ||||||
|  | price.  Our General Public Licenses are designed to make sure that you | ||||||
|  | have the freedom to distribute copies of free software (and charge for | ||||||
|  | them if you wish), that you receive source code or can get it if you | ||||||
|  | want it, that you can change the software or use pieces of it in new | ||||||
|  | free programs, and that you know you can do these things. | ||||||
|  |  | ||||||
|  |   To protect your rights, we need to prevent others from denying you | ||||||
|  | these rights or asking you to surrender the rights.  Therefore, you have | ||||||
|  | certain responsibilities if you distribute copies of the software, or if | ||||||
|  | you modify it: responsibilities to respect the freedom of others. | ||||||
|  |  | ||||||
|  |   For example, if you distribute copies of such a program, whether | ||||||
|  | gratis or for a fee, you must pass on to the recipients the same | ||||||
|  | freedoms that you received.  You must make sure that they, too, receive | ||||||
|  | or can get the source code.  And you must show them these terms so they | ||||||
|  | know their rights. | ||||||
|  |  | ||||||
|  |   Developers that use the GNU GPL protect your rights with two steps: | ||||||
|  | (1) assert copyright on the software, and (2) offer you this License | ||||||
|  | giving you legal permission to copy, distribute and/or modify it. | ||||||
|  |  | ||||||
|  |   For the developers' and authors' protection, the GPL clearly explains | ||||||
|  | that there is no warranty for this free software.  For both users' and | ||||||
|  | authors' sake, the GPL requires that modified versions be marked as | ||||||
|  | changed, so that their problems will not be attributed erroneously to | ||||||
|  | authors of previous versions. | ||||||
|  |  | ||||||
|  |   Some devices are designed to deny users access to install or run | ||||||
|  | modified versions of the software inside them, although the manufacturer | ||||||
|  | can do so.  This is fundamentally incompatible with the aim of | ||||||
|  | protecting users' freedom to change the software.  The systematic | ||||||
|  | pattern of such abuse occurs in the area of products for individuals to | ||||||
|  | use, which is precisely where it is most unacceptable.  Therefore, we | ||||||
|  | have designed this version of the GPL to prohibit the practice for those | ||||||
|  | products.  If such problems arise substantially in other domains, we | ||||||
|  | stand ready to extend this provision to those domains in future versions | ||||||
|  | of the GPL, as needed to protect the freedom of users. | ||||||
|  |  | ||||||
|  |   Finally, every program is threatened constantly by software patents. | ||||||
|  | States should not allow patents to restrict development and use of | ||||||
|  | software on general-purpose computers, but in those that do, we wish to | ||||||
|  | avoid the special danger that patents applied to a free program could | ||||||
|  | make it effectively proprietary.  To prevent this, the GPL assures that | ||||||
|  | patents cannot be used to render the program non-free. | ||||||
|  |  | ||||||
|  |   The precise terms and conditions for copying, distribution and | ||||||
|  | modification follow. | ||||||
|  |  | ||||||
|  |                        TERMS AND CONDITIONS | ||||||
|  |  | ||||||
|  |   0. Definitions. | ||||||
|  |  | ||||||
|  |   "This License" refers to version 3 of the GNU General Public License. | ||||||
|  |  | ||||||
|  |   "Copyright" also means copyright-like laws that apply to other kinds of | ||||||
|  | works, such as semiconductor masks. | ||||||
|  |  | ||||||
|  |   "The Program" refers to any copyrightable work licensed under this | ||||||
|  | License.  Each licensee is addressed as "you".  "Licensees" and | ||||||
|  | "recipients" may be individuals or organizations. | ||||||
|  |  | ||||||
|  |   To "modify" a work means to copy from or adapt all or part of the work | ||||||
|  | in a fashion requiring copyright permission, other than the making of an | ||||||
|  | exact copy.  The resulting work is called a "modified version" of the | ||||||
|  | earlier work or a work "based on" the earlier work. | ||||||
|  |  | ||||||
|  |   A "covered work" means either the unmodified Program or a work based | ||||||
|  | on the Program. | ||||||
|  |  | ||||||
|  |   To "propagate" a work means to do anything with it that, without | ||||||
|  | permission, would make you directly or secondarily liable for | ||||||
|  | infringement under applicable copyright law, except executing it on a | ||||||
|  | computer or modifying a private copy.  Propagation includes copying, | ||||||
|  | distribution (with or without modification), making available to the | ||||||
|  | public, and in some countries other activities as well. | ||||||
|  |  | ||||||
|  |   To "convey" a work means any kind of propagation that enables other | ||||||
|  | parties to make or receive copies.  Mere interaction with a user through | ||||||
|  | a computer network, with no transfer of a copy, is not conveying. | ||||||
|  |  | ||||||
|  |   An interactive user interface displays "Appropriate Legal Notices" | ||||||
|  | to the extent that it includes a convenient and prominently visible | ||||||
|  | feature that (1) displays an appropriate copyright notice, and (2) | ||||||
|  | tells the user that there is no warranty for the work (except to the | ||||||
|  | extent that warranties are provided), that licensees may convey the | ||||||
|  | work under this License, and how to view a copy of this License.  If | ||||||
|  | the interface presents a list of user commands or options, such as a | ||||||
|  | menu, a prominent item in the list meets this criterion. | ||||||
|  |  | ||||||
|  |   1. Source Code. | ||||||
|  |  | ||||||
|  |   The "source code" for a work means the preferred form of the work | ||||||
|  | for making modifications to it.  "Object code" means any non-source | ||||||
|  | form of a work. | ||||||
|  |  | ||||||
|  |   A "Standard Interface" means an interface that either is an official | ||||||
|  | standard defined by a recognized standards body, or, in the case of | ||||||
|  | interfaces specified for a particular programming language, one that | ||||||
|  | is widely used among developers working in that language. | ||||||
|  |  | ||||||
|  |   The "System Libraries" of an executable work include anything, other | ||||||
|  | than the work as a whole, that (a) is included in the normal form of | ||||||
|  | packaging a Major Component, but which is not part of that Major | ||||||
|  | Component, and (b) serves only to enable use of the work with that | ||||||
|  | Major Component, or to implement a Standard Interface for which an | ||||||
|  | implementation is available to the public in source code form.  A | ||||||
|  | "Major Component", in this context, means a major essential component | ||||||
|  | (kernel, window system, and so on) of the specific operating system | ||||||
|  | (if any) on which the executable work runs, or a compiler used to | ||||||
|  | produce the work, or an object code interpreter used to run it. | ||||||
|  |  | ||||||
|  |   The "Corresponding Source" for a work in object code form means all | ||||||
|  | the source code needed to generate, install, and (for an executable | ||||||
|  | work) run the object code and to modify the work, including scripts to | ||||||
|  | control those activities.  However, it does not include the work's | ||||||
|  | System Libraries, or general-purpose tools or generally available free | ||||||
|  | programs which are used unmodified in performing those activities but | ||||||
|  | which are not part of the work.  For example, Corresponding Source | ||||||
|  | includes interface definition files associated with source files for | ||||||
|  | the work, and the source code for shared libraries and dynamically | ||||||
|  | linked subprograms that the work is specifically designed to require, | ||||||
|  | such as by intimate data communication or control flow between those | ||||||
|  | subprograms and other parts of the work. | ||||||
|  |  | ||||||
|  |   The Corresponding Source need not include anything that users | ||||||
|  | can regenerate automatically from other parts of the Corresponding | ||||||
|  | Source. | ||||||
|  |  | ||||||
|  |   The Corresponding Source for a work in source code form is that | ||||||
|  | same work. | ||||||
|  |  | ||||||
|  |   2. Basic Permissions. | ||||||
|  |  | ||||||
|  |   All rights granted under this License are granted for the term of | ||||||
|  | copyright on the Program, and are irrevocable provided the stated | ||||||
|  | conditions are met.  This License explicitly affirms your unlimited | ||||||
|  | permission to run the unmodified Program.  The output from running a | ||||||
|  | covered work is covered by this License only if the output, given its | ||||||
|  | content, constitutes a covered work.  This License acknowledges your | ||||||
|  | rights of fair use or other equivalent, as provided by copyright law. | ||||||
|  |  | ||||||
|  |   You may make, run and propagate covered works that you do not | ||||||
|  | convey, without conditions so long as your license otherwise remains | ||||||
|  | in force.  You may convey covered works to others for the sole purpose | ||||||
|  | of having them make modifications exclusively for you, or provide you | ||||||
|  | with facilities for running those works, provided that you comply with | ||||||
|  | the terms of this License in conveying all material for which you do | ||||||
|  | not control copyright.  Those thus making or running the covered works | ||||||
|  | for you must do so exclusively on your behalf, under your direction | ||||||
|  | and control, on terms that prohibit them from making any copies of | ||||||
|  | your copyrighted material outside their relationship with you. | ||||||
|  |  | ||||||
|  |   Conveying under any other circumstances is permitted solely under | ||||||
|  | the conditions stated below.  Sublicensing is not allowed; section 10 | ||||||
|  | makes it unnecessary. | ||||||
|  |  | ||||||
|  |   3. Protecting Users' Legal Rights From Anti-Circumvention Law. | ||||||
|  |  | ||||||
|  |   No covered work shall be deemed part of an effective technological | ||||||
|  | measure under any applicable law fulfilling obligations under article | ||||||
|  | 11 of the WIPO copyright treaty adopted on 20 December 1996, or | ||||||
|  | similar laws prohibiting or restricting circumvention of such | ||||||
|  | measures. | ||||||
|  |  | ||||||
|  |   When you convey a covered work, you waive any legal power to forbid | ||||||
|  | circumvention of technological measures to the extent such circumvention | ||||||
|  | is effected by exercising rights under this License with respect to | ||||||
|  | the covered work, and you disclaim any intention to limit operation or | ||||||
|  | modification of the work as a means of enforcing, against the work's | ||||||
|  | users, your or third parties' legal rights to forbid circumvention of | ||||||
|  | technological measures. | ||||||
|  |  | ||||||
|  |   4. Conveying Verbatim Copies. | ||||||
|  |  | ||||||
|  |   You may convey verbatim copies of the Program's source code as you | ||||||
|  | receive it, in any medium, provided that you conspicuously and | ||||||
|  | appropriately publish on each copy an appropriate copyright notice; | ||||||
|  | keep intact all notices stating that this License and any | ||||||
|  | non-permissive terms added in accord with section 7 apply to the code; | ||||||
|  | keep intact all notices of the absence of any warranty; and give all | ||||||
|  | recipients a copy of this License along with the Program. | ||||||
|  |  | ||||||
|  |   You may charge any price or no price for each copy that you convey, | ||||||
|  | and you may offer support or warranty protection for a fee. | ||||||
|  |  | ||||||
|  |   5. Conveying Modified Source Versions. | ||||||
|  |  | ||||||
|  |   You may convey a work based on the Program, or the modifications to | ||||||
|  | produce it from the Program, in the form of source code under the | ||||||
|  | terms of section 4, provided that you also meet all of these conditions: | ||||||
|  |  | ||||||
|  |     a) The work must carry prominent notices stating that you modified | ||||||
|  |     it, and giving a relevant date. | ||||||
|  |  | ||||||
|  |     b) The work must carry prominent notices stating that it is | ||||||
|  |     released under this License and any conditions added under section | ||||||
|  |     7.  This requirement modifies the requirement in section 4 to | ||||||
|  |     "keep intact all notices". | ||||||
|  |  | ||||||
|  |     c) You must license the entire work, as a whole, under this | ||||||
|  |     License to anyone who comes into possession of a copy.  This | ||||||
|  |     License will therefore apply, along with any applicable section 7 | ||||||
|  |     additional terms, to the whole of the work, and all its parts, | ||||||
|  |     regardless of how they are packaged.  This License gives no | ||||||
|  |     permission to license the work in any other way, but it does not | ||||||
|  |     invalidate such permission if you have separately received it. | ||||||
|  |  | ||||||
|  |     d) If the work has interactive user interfaces, each must display | ||||||
|  |     Appropriate Legal Notices; however, if the Program has interactive | ||||||
|  |     interfaces that do not display Appropriate Legal Notices, your | ||||||
|  |     work need not make them do so. | ||||||
|  |  | ||||||
|  |   A compilation of a covered work with other separate and independent | ||||||
|  | works, which are not by their nature extensions of the covered work, | ||||||
|  | and which are not combined with it such as to form a larger program, | ||||||
|  | in or on a volume of a storage or distribution medium, is called an | ||||||
|  | "aggregate" if the compilation and its resulting copyright are not | ||||||
|  | used to limit the access or legal rights of the compilation's users | ||||||
|  | beyond what the individual works permit.  Inclusion of a covered work | ||||||
|  | in an aggregate does not cause this License to apply to the other | ||||||
|  | parts of the aggregate. | ||||||
|  |  | ||||||
|  |   6. Conveying Non-Source Forms. | ||||||
|  |  | ||||||
|  |   You may convey a covered work in object code form under the terms | ||||||
|  | of sections 4 and 5, provided that you also convey the | ||||||
|  | machine-readable Corresponding Source under the terms of this License, | ||||||
|  | in one of these ways: | ||||||
|  |  | ||||||
|  |     a) Convey the object code in, or embodied in, a physical product | ||||||
|  |     (including a physical distribution medium), accompanied by the | ||||||
|  |     Corresponding Source fixed on a durable physical medium | ||||||
|  |     customarily used for software interchange. | ||||||
|  |  | ||||||
|  |     b) Convey the object code in, or embodied in, a physical product | ||||||
|  |     (including a physical distribution medium), accompanied by a | ||||||
|  |     written offer, valid for at least three years and valid for as | ||||||
|  |     long as you offer spare parts or customer support for that product | ||||||
|  |     model, to give anyone who possesses the object code either (1) a | ||||||
|  |     copy of the Corresponding Source for all the software in the | ||||||
|  |     product that is covered by this License, on a durable physical | ||||||
|  |     medium customarily used for software interchange, for a price no | ||||||
|  |     more than your reasonable cost of physically performing this | ||||||
|  |     conveying of source, or (2) access to copy the | ||||||
|  |     Corresponding Source from a network server at no charge. | ||||||
|  |  | ||||||
|  |     c) Convey individual copies of the object code with a copy of the | ||||||
|  |     written offer to provide the Corresponding Source.  This | ||||||
|  |     alternative is allowed only occasionally and noncommercially, and | ||||||
|  |     only if you received the object code with such an offer, in accord | ||||||
|  |     with subsection 6b. | ||||||
|  |  | ||||||
|  |     d) Convey the object code by offering access from a designated | ||||||
|  |     place (gratis or for a charge), and offer equivalent access to the | ||||||
|  |     Corresponding Source in the same way through the same place at no | ||||||
|  |     further charge.  You need not require recipients to copy the | ||||||
|  |     Corresponding Source along with the object code.  If the place to | ||||||
|  |     copy the object code is a network server, the Corresponding Source | ||||||
|  |     may be on a different server (operated by you or a third party) | ||||||
|  |     that supports equivalent copying facilities, provided you maintain | ||||||
|  |     clear directions next to the object code saying where to find the | ||||||
|  |     Corresponding Source.  Regardless of what server hosts the | ||||||
|  |     Corresponding Source, you remain obligated to ensure that it is | ||||||
|  |     available for as long as needed to satisfy these requirements. | ||||||
|  |  | ||||||
|  |     e) Convey the object code using peer-to-peer transmission, provided | ||||||
|  |     you inform other peers where the object code and Corresponding | ||||||
|  |     Source of the work are being offered to the general public at no | ||||||
|  |     charge under subsection 6d. | ||||||
|  |  | ||||||
|  |   A separable portion of the object code, whose source code is excluded | ||||||
|  | from the Corresponding Source as a System Library, need not be | ||||||
|  | included in conveying the object code work. | ||||||
|  |  | ||||||
|  |   A "User Product" is either (1) a "consumer product", which means any | ||||||
|  | tangible personal property which is normally used for personal, family, | ||||||
|  | or household purposes, or (2) anything designed or sold for incorporation | ||||||
|  | into a dwelling.  In determining whether a product is a consumer product, | ||||||
|  | doubtful cases shall be resolved in favor of coverage.  For a particular | ||||||
|  | product received by a particular user, "normally used" refers to a | ||||||
|  | typical or common use of that class of product, regardless of the status | ||||||
|  | of the particular user or of the way in which the particular user | ||||||
|  | actually uses, or expects or is expected to use, the product.  A product | ||||||
|  | is a consumer product regardless of whether the product has substantial | ||||||
|  | commercial, industrial or non-consumer uses, unless such uses represent | ||||||
|  | the only significant mode of use of the product. | ||||||
|  |  | ||||||
|  |   "Installation Information" for a User Product means any methods, | ||||||
|  | procedures, authorization keys, or other information required to install | ||||||
|  | and execute modified versions of a covered work in that User Product from | ||||||
|  | a modified version of its Corresponding Source.  The information must | ||||||
|  | suffice to ensure that the continued functioning of the modified object | ||||||
|  | code is in no case prevented or interfered with solely because | ||||||
|  | modification has been made. | ||||||
|  |  | ||||||
|  |   If you convey an object code work under this section in, or with, or | ||||||
|  | specifically for use in, a User Product, and the conveying occurs as | ||||||
|  | part of a transaction in which the right of possession and use of the | ||||||
|  | User Product is transferred to the recipient in perpetuity or for a | ||||||
|  | fixed term (regardless of how the transaction is characterized), the | ||||||
|  | Corresponding Source conveyed under this section must be accompanied | ||||||
|  | by the Installation Information.  But this requirement does not apply | ||||||
|  | if neither you nor any third party retains the ability to install | ||||||
|  | modified object code on the User Product (for example, the work has | ||||||
|  | been installed in ROM). | ||||||
|  |  | ||||||
|  |   The requirement to provide Installation Information does not include a | ||||||
|  | requirement to continue to provide support service, warranty, or updates | ||||||
|  | for a work that has been modified or installed by the recipient, or for | ||||||
|  | the User Product in which it has been modified or installed.  Access to a | ||||||
|  | network may be denied when the modification itself materially and | ||||||
|  | adversely affects the operation of the network or violates the rules and | ||||||
|  | protocols for communication across the network. | ||||||
|  |  | ||||||
|  |   Corresponding Source conveyed, and Installation Information provided, | ||||||
|  | in accord with this section must be in a format that is publicly | ||||||
|  | documented (and with an implementation available to the public in | ||||||
|  | source code form), and must require no special password or key for | ||||||
|  | unpacking, reading or copying. | ||||||
|  |  | ||||||
|  |   7. Additional Terms. | ||||||
|  |  | ||||||
|  |   "Additional permissions" are terms that supplement the terms of this | ||||||
|  | License by making exceptions from one or more of its conditions. | ||||||
|  | Additional permissions that are applicable to the entire Program shall | ||||||
|  | be treated as though they were included in this License, to the extent | ||||||
|  | that they are valid under applicable law.  If additional permissions | ||||||
|  | apply only to part of the Program, that part may be used separately | ||||||
|  | under those permissions, but the entire Program remains governed by | ||||||
|  | this License without regard to the additional permissions. | ||||||
|  |  | ||||||
|  |   When you convey a copy of a covered work, you may at your option | ||||||
|  | remove any additional permissions from that copy, or from any part of | ||||||
|  | it.  (Additional permissions may be written to require their own | ||||||
|  | removal in certain cases when you modify the work.)  You may place | ||||||
|  | additional permissions on material, added by you to a covered work, | ||||||
|  | for which you have or can give appropriate copyright permission. | ||||||
|  |  | ||||||
|  |   Notwithstanding any other provision of this License, for material you | ||||||
|  | add to a covered work, you may (if authorized by the copyright holders of | ||||||
|  | that material) supplement the terms of this License with terms: | ||||||
|  |  | ||||||
|  |     a) Disclaiming warranty or limiting liability differently from the | ||||||
|  |     terms of sections 15 and 16 of this License; or | ||||||
|  |  | ||||||
|  |     b) Requiring preservation of specified reasonable legal notices or | ||||||
|  |     author attributions in that material or in the Appropriate Legal | ||||||
|  |     Notices displayed by works containing it; or | ||||||
|  |  | ||||||
|  |     c) Prohibiting misrepresentation of the origin of that material, or | ||||||
|  |     requiring that modified versions of such material be marked in | ||||||
|  |     reasonable ways as different from the original version; or | ||||||
|  |  | ||||||
|  |     d) Limiting the use for publicity purposes of names of licensors or | ||||||
|  |     authors of the material; or | ||||||
|  |  | ||||||
|  |     e) Declining to grant rights under trademark law for use of some | ||||||
|  |     trade names, trademarks, or service marks; or | ||||||
|  |  | ||||||
|  |     f) Requiring indemnification of licensors and authors of that | ||||||
|  |     material by anyone who conveys the material (or modified versions of | ||||||
|  |     it) with contractual assumptions of liability to the recipient, for | ||||||
|  |     any liability that these contractual assumptions directly impose on | ||||||
|  |     those licensors and authors. | ||||||
|  |  | ||||||
|  |   All other non-permissive additional terms are considered "further | ||||||
|  | restrictions" within the meaning of section 10.  If the Program as you | ||||||
|  | received it, or any part of it, contains a notice stating that it is | ||||||
|  | governed by this License along with a term that is a further | ||||||
|  | restriction, you may remove that term.  If a license document contains | ||||||
|  | a further restriction but permits relicensing or conveying under this | ||||||
|  | License, you may add to a covered work material governed by the terms | ||||||
|  | of that license document, provided that the further restriction does | ||||||
|  | not survive such relicensing or conveying. | ||||||
|  |  | ||||||
|  |   If you add terms to a covered work in accord with this section, you | ||||||
|  | must place, in the relevant source files, a statement of the | ||||||
|  | additional terms that apply to those files, or a notice indicating | ||||||
|  | where to find the applicable terms. | ||||||
|  |  | ||||||
|  |   Additional terms, permissive or non-permissive, may be stated in the | ||||||
|  | form of a separately written license, or stated as exceptions; | ||||||
|  | the above requirements apply either way. | ||||||
|  |  | ||||||
|  |   8. Termination. | ||||||
|  |  | ||||||
|  |   You may not propagate or modify a covered work except as expressly | ||||||
|  | provided under this License.  Any attempt otherwise to propagate or | ||||||
|  | modify it is void, and will automatically terminate your rights under | ||||||
|  | this License (including any patent licenses granted under the third | ||||||
|  | paragraph of section 11). | ||||||
|  |  | ||||||
|  |   However, if you cease all violation of this License, then your | ||||||
|  | license from a particular copyright holder is reinstated (a) | ||||||
|  | provisionally, unless and until the copyright holder explicitly and | ||||||
|  | finally terminates your license, and (b) permanently, if the copyright | ||||||
|  | holder fails to notify you of the violation by some reasonable means | ||||||
|  | prior to 60 days after the cessation. | ||||||
|  |  | ||||||
|  |   Moreover, your license from a particular copyright holder is | ||||||
|  | reinstated permanently if the copyright holder notifies you of the | ||||||
|  | violation by some reasonable means, this is the first time you have | ||||||
|  | received notice of violation of this License (for any work) from that | ||||||
|  | copyright holder, and you cure the violation prior to 30 days after | ||||||
|  | your receipt of the notice. | ||||||
|  |  | ||||||
|  |   Termination of your rights under this section does not terminate the | ||||||
|  | licenses of parties who have received copies or rights from you under | ||||||
|  | this License.  If your rights have been terminated and not permanently | ||||||
|  | reinstated, you do not qualify to receive new licenses for the same | ||||||
|  | material under section 10. | ||||||
|  |  | ||||||
|  |   9. Acceptance Not Required for Having Copies. | ||||||
|  |  | ||||||
|  |   You are not required to accept this License in order to receive or | ||||||
|  | run a copy of the Program.  Ancillary propagation of a covered work | ||||||
|  | occurring solely as a consequence of using peer-to-peer transmission | ||||||
|  | to receive a copy likewise does not require acceptance.  However, | ||||||
|  | nothing other than this License grants you permission to propagate or | ||||||
|  | modify any covered work.  These actions infringe copyright if you do | ||||||
|  | not accept this License.  Therefore, by modifying or propagating a | ||||||
|  | covered work, you indicate your acceptance of this License to do so. | ||||||
|  |  | ||||||
|  |   10. Automatic Licensing of Downstream Recipients. | ||||||
|  |  | ||||||
|  |   Each time you convey a covered work, the recipient automatically | ||||||
|  | receives a license from the original licensors, to run, modify and | ||||||
|  | propagate that work, subject to this License.  You are not responsible | ||||||
|  | for enforcing compliance by third parties with this License. | ||||||
|  |  | ||||||
|  |   An "entity transaction" is a transaction transferring control of an | ||||||
|  | organization, or substantially all assets of one, or subdividing an | ||||||
|  | organization, or merging organizations.  If propagation of a covered | ||||||
|  | work results from an entity transaction, each party to that | ||||||
|  | transaction who receives a copy of the work also receives whatever | ||||||
|  | licenses to the work the party's predecessor in interest had or could | ||||||
|  | give under the previous paragraph, plus a right to possession of the | ||||||
|  | Corresponding Source of the work from the predecessor in interest, if | ||||||
|  | the predecessor has it or can get it with reasonable efforts. | ||||||
|  |  | ||||||
|  |   You may not impose any further restrictions on the exercise of the | ||||||
|  | rights granted or affirmed under this License.  For example, you may | ||||||
|  | not impose a license fee, royalty, or other charge for exercise of | ||||||
|  | rights granted under this License, and you may not initiate litigation | ||||||
|  | (including a cross-claim or counterclaim in a lawsuit) alleging that | ||||||
|  | any patent claim is infringed by making, using, selling, offering for | ||||||
|  | sale, or importing the Program or any portion of it. | ||||||
|  |  | ||||||
|  |   11. Patents. | ||||||
|  |  | ||||||
|  |   A "contributor" is a copyright holder who authorizes use under this | ||||||
|  | License of the Program or a work on which the Program is based.  The | ||||||
|  | work thus licensed is called the contributor's "contributor version". | ||||||
|  |  | ||||||
|  |   A contributor's "essential patent claims" are all patent claims | ||||||
|  | owned or controlled by the contributor, whether already acquired or | ||||||
|  | hereafter acquired, that would be infringed by some manner, permitted | ||||||
|  | by this License, of making, using, or selling its contributor version, | ||||||
|  | but do not include claims that would be infringed only as a | ||||||
|  | consequence of further modification of the contributor version.  For | ||||||
|  | purposes of this definition, "control" includes the right to grant | ||||||
|  | patent sublicenses in a manner consistent with the requirements of | ||||||
|  | this License. | ||||||
|  |  | ||||||
|  |   Each contributor grants you a non-exclusive, worldwide, royalty-free | ||||||
|  | patent license under the contributor's essential patent claims, to | ||||||
|  | make, use, sell, offer for sale, import and otherwise run, modify and | ||||||
|  | propagate the contents of its contributor version. | ||||||
|  |  | ||||||
|  |   In the following three paragraphs, a "patent license" is any express | ||||||
|  | agreement or commitment, however denominated, not to enforce a patent | ||||||
|  | (such as an express permission to practice a patent or covenant not to | ||||||
|  | sue for patent infringement).  To "grant" such a patent license to a | ||||||
|  | party means to make such an agreement or commitment not to enforce a | ||||||
|  | patent against the party. | ||||||
|  |  | ||||||
|  |   If you convey a covered work, knowingly relying on a patent license, | ||||||
|  | and the Corresponding Source of the work is not available for anyone | ||||||
|  | to copy, free of charge and under the terms of this License, through a | ||||||
|  | publicly available network server or other readily accessible means, | ||||||
|  | then you must either (1) cause the Corresponding Source to be so | ||||||
|  | available, or (2) arrange to deprive yourself of the benefit of the | ||||||
|  | patent license for this particular work, or (3) arrange, in a manner | ||||||
|  | consistent with the requirements of this License, to extend the patent | ||||||
|  | license to downstream recipients.  "Knowingly relying" means you have | ||||||
|  | actual knowledge that, but for the patent license, your conveying the | ||||||
|  | covered work in a country, or your recipient's use of the covered work | ||||||
|  | in a country, would infringe one or more identifiable patents in that | ||||||
|  | country that you have reason to believe are valid. | ||||||
|  |  | ||||||
|  |   If, pursuant to or in connection with a single transaction or | ||||||
|  | arrangement, you convey, or propagate by procuring conveyance of, a | ||||||
|  | covered work, and grant a patent license to some of the parties | ||||||
|  | receiving the covered work authorizing them to use, propagate, modify | ||||||
|  | or convey a specific copy of the covered work, then the patent license | ||||||
|  | you grant is automatically extended to all recipients of the covered | ||||||
|  | work and works based on it. | ||||||
|  |  | ||||||
|  |   A patent license is "discriminatory" if it does not include within | ||||||
|  | the scope of its coverage, prohibits the exercise of, or is | ||||||
|  | conditioned on the non-exercise of one or more of the rights that are | ||||||
|  | specifically granted under this License.  You may not convey a covered | ||||||
|  | work if you are a party to an arrangement with a third party that is | ||||||
|  | in the business of distributing software, under which you make payment | ||||||
|  | to the third party based on the extent of your activity of conveying | ||||||
|  | the work, and under which the third party grants, to any of the | ||||||
|  | parties who would receive the covered work from you, a discriminatory | ||||||
|  | patent license (a) in connection with copies of the covered work | ||||||
|  | conveyed by you (or copies made from those copies), or (b) primarily | ||||||
|  | for and in connection with specific products or compilations that | ||||||
|  | contain the covered work, unless you entered into that arrangement, | ||||||
|  | or that patent license was granted, prior to 28 March 2007. | ||||||
|  |  | ||||||
|  |   Nothing in this License shall be construed as excluding or limiting | ||||||
|  | any implied license or other defenses to infringement that may | ||||||
|  | otherwise be available to you under applicable patent law. | ||||||
|  |  | ||||||
|  |   12. No Surrender of Others' Freedom. | ||||||
|  |  | ||||||
|  |   If conditions are imposed on you (whether by court order, agreement or | ||||||
|  | otherwise) that contradict the conditions of this License, they do not | ||||||
|  | excuse you from the conditions of this License.  If you cannot convey a | ||||||
|  | covered work so as to satisfy simultaneously your obligations under this | ||||||
|  | License and any other pertinent obligations, then as a consequence you may | ||||||
|  | not convey it at all.  For example, if you agree to terms that obligate you | ||||||
|  | to collect a royalty for further conveying from those to whom you convey | ||||||
|  | the Program, the only way you could satisfy both those terms and this | ||||||
|  | License would be to refrain entirely from conveying the Program. | ||||||
|  |  | ||||||
|  |   13. Use with the GNU Affero General Public License. | ||||||
|  |  | ||||||
|  |   Notwithstanding any other provision of this License, you have | ||||||
|  | permission to link or combine any covered work with a work licensed | ||||||
|  | under version 3 of the GNU Affero General Public License into a single | ||||||
|  | combined work, and to convey the resulting work.  The terms of this | ||||||
|  | License will continue to apply to the part which is the covered work, | ||||||
|  | but the special requirements of the GNU Affero General Public License, | ||||||
|  | section 13, concerning interaction through a network will apply to the | ||||||
|  | combination as such. | ||||||
|  |  | ||||||
|  |   14. Revised Versions of this License. | ||||||
|  |  | ||||||
|  |   The Free Software Foundation may publish revised and/or new versions of | ||||||
|  | the GNU General Public License from time to time.  Such new versions will | ||||||
|  | be similar in spirit to the present version, but may differ in detail to | ||||||
|  | address new problems or concerns. | ||||||
|  |  | ||||||
|  |   Each version is given a distinguishing version number.  If the | ||||||
|  | Program specifies that a certain numbered version of the GNU General | ||||||
|  | Public License "or any later version" applies to it, you have the | ||||||
|  | option of following the terms and conditions either of that numbered | ||||||
|  | version or of any later version published by the Free Software | ||||||
|  | Foundation.  If the Program does not specify a version number of the | ||||||
|  | GNU General Public License, you may choose any version ever published | ||||||
|  | by the Free Software Foundation. | ||||||
|  |  | ||||||
|  |   If the Program specifies that a proxy can decide which future | ||||||
|  | versions of the GNU General Public License can be used, that proxy's | ||||||
|  | public statement of acceptance of a version permanently authorizes you | ||||||
|  | to choose that version for the Program. | ||||||
|  |  | ||||||
|  |   Later license versions may give you additional or different | ||||||
|  | permissions.  However, no additional obligations are imposed on any | ||||||
|  | author or copyright holder as a result of your choosing to follow a | ||||||
|  | later version. | ||||||
|  |  | ||||||
|  |   15. Disclaimer of Warranty. | ||||||
|  |  | ||||||
|  |   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY | ||||||
|  | APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT | ||||||
|  | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY | ||||||
|  | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, | ||||||
|  | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||||||
|  | PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM | ||||||
|  | IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF | ||||||
|  | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | ||||||
|  |  | ||||||
|  |   16. Limitation of Liability. | ||||||
|  |  | ||||||
|  |   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||||
|  | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS | ||||||
|  | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY | ||||||
|  | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE | ||||||
|  | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF | ||||||
|  | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD | ||||||
|  | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), | ||||||
|  | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | ||||||
|  | SUCH DAMAGES. | ||||||
|  |  | ||||||
|  |   17. Interpretation of Sections 15 and 16. | ||||||
|  |  | ||||||
|  |   If the disclaimer of warranty and limitation of liability provided | ||||||
|  | above cannot be given local legal effect according to their terms, | ||||||
|  | reviewing courts shall apply local law that most closely approximates | ||||||
|  | an absolute waiver of all civil liability in connection with the | ||||||
|  | Program, unless a warranty or assumption of liability accompanies a | ||||||
|  | copy of the Program in return for a fee. | ||||||
|  |  | ||||||
|  |                      END OF TERMS AND CONDITIONS | ||||||
							
								
								
									
										165
									
								
								public/novnc/docs/LICENSE.LGPL-3
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,165 @@ | |||||||
|  |                    GNU LESSER GENERAL PUBLIC LICENSE | ||||||
|  |                        Version 3, 29 June 2007 | ||||||
|  |  | ||||||
|  |  Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | ||||||
|  |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  |  of this license document, but changing it is not allowed. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   This version of the GNU Lesser General Public License incorporates | ||||||
|  | the terms and conditions of version 3 of the GNU General Public | ||||||
|  | License, supplemented by the additional permissions listed below. | ||||||
|  |  | ||||||
|  |   0. Additional Definitions. | ||||||
|  |  | ||||||
|  |   As used herein, "this License" refers to version 3 of the GNU Lesser | ||||||
|  | General Public License, and the "GNU GPL" refers to version 3 of the GNU | ||||||
|  | General Public License. | ||||||
|  |  | ||||||
|  |   "The Library" refers to a covered work governed by this License, | ||||||
|  | other than an Application or a Combined Work as defined below. | ||||||
|  |  | ||||||
|  |   An "Application" is any work that makes use of an interface provided | ||||||
|  | by the Library, but which is not otherwise based on the Library. | ||||||
|  | Defining a subclass of a class defined by the Library is deemed a mode | ||||||
|  | of using an interface provided by the Library. | ||||||
|  |  | ||||||
|  |   A "Combined Work" is a work produced by combining or linking an | ||||||
|  | Application with the Library.  The particular version of the Library | ||||||
|  | with which the Combined Work was made is also called the "Linked | ||||||
|  | Version". | ||||||
|  |  | ||||||
|  |   The "Minimal Corresponding Source" for a Combined Work means the | ||||||
|  | Corresponding Source for the Combined Work, excluding any source code | ||||||
|  | for portions of the Combined Work that, considered in isolation, are | ||||||
|  | based on the Application, and not on the Linked Version. | ||||||
|  |  | ||||||
|  |   The "Corresponding Application Code" for a Combined Work means the | ||||||
|  | object code and/or source code for the Application, including any data | ||||||
|  | and utility programs needed for reproducing the Combined Work from the | ||||||
|  | Application, but excluding the System Libraries of the Combined Work. | ||||||
|  |  | ||||||
|  |   1. Exception to Section 3 of the GNU GPL. | ||||||
|  |  | ||||||
|  |   You may convey a covered work under sections 3 and 4 of this License | ||||||
|  | without being bound by section 3 of the GNU GPL. | ||||||
|  |  | ||||||
|  |   2. Conveying Modified Versions. | ||||||
|  |  | ||||||
|  |   If you modify a copy of the Library, and, in your modifications, a | ||||||
|  | facility refers to a function or data to be supplied by an Application | ||||||
|  | that uses the facility (other than as an argument passed when the | ||||||
|  | facility is invoked), then you may convey a copy of the modified | ||||||
|  | version: | ||||||
|  |  | ||||||
|  |    a) under this License, provided that you make a good faith effort to | ||||||
|  |    ensure that, in the event an Application does not supply the | ||||||
|  |    function or data, the facility still operates, and performs | ||||||
|  |    whatever part of its purpose remains meaningful, or | ||||||
|  |  | ||||||
|  |    b) under the GNU GPL, with none of the additional permissions of | ||||||
|  |    this License applicable to that copy. | ||||||
|  |  | ||||||
|  |   3. Object Code Incorporating Material from Library Header Files. | ||||||
|  |  | ||||||
|  |   The object code form of an Application may incorporate material from | ||||||
|  | a header file that is part of the Library.  You may convey such object | ||||||
|  | code under terms of your choice, provided that, if the incorporated | ||||||
|  | material is not limited to numerical parameters, data structure | ||||||
|  | layouts and accessors, or small macros, inline functions and templates | ||||||
|  | (ten or fewer lines in length), you do both of the following: | ||||||
|  |  | ||||||
|  |    a) Give prominent notice with each copy of the object code that the | ||||||
|  |    Library is used in it and that the Library and its use are | ||||||
|  |    covered by this License. | ||||||
|  |  | ||||||
|  |    b) Accompany the object code with a copy of the GNU GPL and this license | ||||||
|  |    document. | ||||||
|  |  | ||||||
|  |   4. Combined Works. | ||||||
|  |  | ||||||
|  |   You may convey a Combined Work under terms of your choice that, | ||||||
|  | taken together, effectively do not restrict modification of the | ||||||
|  | portions of the Library contained in the Combined Work and reverse | ||||||
|  | engineering for debugging such modifications, if you also do each of | ||||||
|  | the following: | ||||||
|  |  | ||||||
|  |    a) Give prominent notice with each copy of the Combined Work that | ||||||
|  |    the Library is used in it and that the Library and its use are | ||||||
|  |    covered by this License. | ||||||
|  |  | ||||||
|  |    b) Accompany the Combined Work with a copy of the GNU GPL and this license | ||||||
|  |    document. | ||||||
|  |  | ||||||
|  |    c) For a Combined Work that displays copyright notices during | ||||||
|  |    execution, include the copyright notice for the Library among | ||||||
|  |    these notices, as well as a reference directing the user to the | ||||||
|  |    copies of the GNU GPL and this license document. | ||||||
|  |  | ||||||
|  |    d) Do one of the following: | ||||||
|  |  | ||||||
|  |        0) Convey the Minimal Corresponding Source under the terms of this | ||||||
|  |        License, and the Corresponding Application Code in a form | ||||||
|  |        suitable for, and under terms that permit, the user to | ||||||
|  |        recombine or relink the Application with a modified version of | ||||||
|  |        the Linked Version to produce a modified Combined Work, in the | ||||||
|  |        manner specified by section 6 of the GNU GPL for conveying | ||||||
|  |        Corresponding Source. | ||||||
|  |  | ||||||
|  |        1) Use a suitable shared library mechanism for linking with the | ||||||
|  |        Library.  A suitable mechanism is one that (a) uses at run time | ||||||
|  |        a copy of the Library already present on the user's computer | ||||||
|  |        system, and (b) will operate properly with a modified version | ||||||
|  |        of the Library that is interface-compatible with the Linked | ||||||
|  |        Version. | ||||||
|  |  | ||||||
|  |    e) Provide Installation Information, but only if you would otherwise | ||||||
|  |    be required to provide such information under section 6 of the | ||||||
|  |    GNU GPL, and only to the extent that such information is | ||||||
|  |    necessary to install and execute a modified version of the | ||||||
|  |    Combined Work produced by recombining or relinking the | ||||||
|  |    Application with a modified version of the Linked Version. (If | ||||||
|  |    you use option 4d0, the Installation Information must accompany | ||||||
|  |    the Minimal Corresponding Source and Corresponding Application | ||||||
|  |    Code. If you use option 4d1, you must provide the Installation | ||||||
|  |    Information in the manner specified by section 6 of the GNU GPL | ||||||
|  |    for conveying Corresponding Source.) | ||||||
|  |  | ||||||
|  |   5. Combined Libraries. | ||||||
|  |  | ||||||
|  |   You may place library facilities that are a work based on the | ||||||
|  | Library side by side in a single library together with other library | ||||||
|  | facilities that are not Applications and are not covered by this | ||||||
|  | License, and convey such a combined library under terms of your | ||||||
|  | choice, if you do both of the following: | ||||||
|  |  | ||||||
|  |    a) Accompany the combined library with a copy of the same work based | ||||||
|  |    on the Library, uncombined with any other library facilities, | ||||||
|  |    conveyed under the terms of this License. | ||||||
|  |  | ||||||
|  |    b) Give prominent notice with the combined library that part of it | ||||||
|  |    is a work based on the Library, and explaining where to find the | ||||||
|  |    accompanying uncombined form of the same work. | ||||||
|  |  | ||||||
|  |   6. Revised Versions of the GNU Lesser General Public License. | ||||||
|  |  | ||||||
|  |   The Free Software Foundation may publish revised and/or new versions | ||||||
|  | of the GNU Lesser General Public License from time to time. Such new | ||||||
|  | versions will be similar in spirit to the present version, but may | ||||||
|  | differ in detail to address new problems or concerns. | ||||||
|  |  | ||||||
|  |   Each version is given a distinguishing version number. If the | ||||||
|  | Library as you received it specifies that a certain numbered version | ||||||
|  | of the GNU Lesser General Public License "or any later version" | ||||||
|  | applies to it, you have the option of following the terms and | ||||||
|  | conditions either of that published version or of any later version | ||||||
|  | published by the Free Software Foundation. If the Library as you | ||||||
|  | received it does not specify a version number of the GNU Lesser | ||||||
|  | General Public License, you may choose any version of the GNU Lesser | ||||||
|  | General Public License ever published by the Free Software Foundation. | ||||||
|  |  | ||||||
|  |   If the Library as you received it specifies that a proxy can decide | ||||||
|  | whether future versions of the GNU Lesser General Public License shall | ||||||
|  | apply, that proxy's public statement of acceptance of any version is | ||||||
|  | permanent authorization for you to choose that version for the | ||||||
|  | Library. | ||||||
							
								
								
									
										373
									
								
								public/novnc/docs/LICENSE.MPL-2.0
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,373 @@ | |||||||
|  | Mozilla Public License Version 2.0 | ||||||
|  | ================================== | ||||||
|  |  | ||||||
|  | 1. Definitions | ||||||
|  | -------------- | ||||||
|  |  | ||||||
|  | 1.1. "Contributor" | ||||||
|  |     means each individual or legal entity that creates, contributes to | ||||||
|  |     the creation of, or owns Covered Software. | ||||||
|  |  | ||||||
|  | 1.2. "Contributor Version" | ||||||
|  |     means the combination of the Contributions of others (if any) used | ||||||
|  |     by a Contributor and that particular Contributor's Contribution. | ||||||
|  |  | ||||||
|  | 1.3. "Contribution" | ||||||
|  |     means Covered Software of a particular Contributor. | ||||||
|  |  | ||||||
|  | 1.4. "Covered Software" | ||||||
|  |     means Source Code Form to which the initial Contributor has attached | ||||||
|  |     the notice in Exhibit A, the Executable Form of such Source Code | ||||||
|  |     Form, and Modifications of such Source Code Form, in each case | ||||||
|  |     including portions thereof. | ||||||
|  |  | ||||||
|  | 1.5. "Incompatible With Secondary Licenses" | ||||||
|  |     means | ||||||
|  |  | ||||||
|  |     (a) that the initial Contributor has attached the notice described | ||||||
|  |         in Exhibit B to the Covered Software; or | ||||||
|  |  | ||||||
|  |     (b) that the Covered Software was made available under the terms of | ||||||
|  |         version 1.1 or earlier of the License, but not also under the | ||||||
|  |         terms of a Secondary License. | ||||||
|  |  | ||||||
|  | 1.6. "Executable Form" | ||||||
|  |     means any form of the work other than Source Code Form. | ||||||
|  |  | ||||||
|  | 1.7. "Larger Work" | ||||||
|  |     means a work that combines Covered Software with other material, in  | ||||||
|  |     a separate file or files, that is not Covered Software. | ||||||
|  |  | ||||||
|  | 1.8. "License" | ||||||
|  |     means this document. | ||||||
|  |  | ||||||
|  | 1.9. "Licensable" | ||||||
|  |     means having the right to grant, to the maximum extent possible, | ||||||
|  |     whether at the time of the initial grant or subsequently, any and | ||||||
|  |     all of the rights conveyed by this License. | ||||||
|  |  | ||||||
|  | 1.10. "Modifications" | ||||||
|  |     means any of the following: | ||||||
|  |  | ||||||
|  |     (a) any file in Source Code Form that results from an addition to, | ||||||
|  |         deletion from, or modification of the contents of Covered | ||||||
|  |         Software; or | ||||||
|  |  | ||||||
|  |     (b) any new file in Source Code Form that contains any Covered | ||||||
|  |         Software. | ||||||
|  |  | ||||||
|  | 1.11. "Patent Claims" of a Contributor | ||||||
|  |     means any patent claim(s), including without limitation, method, | ||||||
|  |     process, and apparatus claims, in any patent Licensable by such | ||||||
|  |     Contributor that would be infringed, but for the grant of the | ||||||
|  |     License, by the making, using, selling, offering for sale, having | ||||||
|  |     made, import, or transfer of either its Contributions or its | ||||||
|  |     Contributor Version. | ||||||
|  |  | ||||||
|  | 1.12. "Secondary License" | ||||||
|  |     means either the GNU General Public License, Version 2.0, the GNU | ||||||
|  |     Lesser General Public License, Version 2.1, the GNU Affero General | ||||||
|  |     Public License, Version 3.0, or any later versions of those | ||||||
|  |     licenses. | ||||||
|  |  | ||||||
|  | 1.13. "Source Code Form" | ||||||
|  |     means the form of the work preferred for making modifications. | ||||||
|  |  | ||||||
|  | 1.14. "You" (or "Your") | ||||||
|  |     means an individual or a legal entity exercising rights under this | ||||||
|  |     License. For legal entities, "You" includes any entity that | ||||||
|  |     controls, is controlled by, or is under common control with You. For | ||||||
|  |     purposes of this definition, "control" means (a) the power, direct | ||||||
|  |     or indirect, to cause the direction or management of such entity, | ||||||
|  |     whether by contract or otherwise, or (b) ownership of more than | ||||||
|  |     fifty percent (50%) of the outstanding shares or beneficial | ||||||
|  |     ownership of such entity. | ||||||
|  |  | ||||||
|  | 2. License Grants and Conditions | ||||||
|  | -------------------------------- | ||||||
|  |  | ||||||
|  | 2.1. Grants | ||||||
|  |  | ||||||
|  | Each Contributor hereby grants You a world-wide, royalty-free, | ||||||
|  | non-exclusive license: | ||||||
|  |  | ||||||
|  | (a) under intellectual property rights (other than patent or trademark) | ||||||
|  |     Licensable by such Contributor to use, reproduce, make available, | ||||||
|  |     modify, display, perform, distribute, and otherwise exploit its | ||||||
|  |     Contributions, either on an unmodified basis, with Modifications, or | ||||||
|  |     as part of a Larger Work; and | ||||||
|  |  | ||||||
|  | (b) under Patent Claims of such Contributor to make, use, sell, offer | ||||||
|  |     for sale, have made, import, and otherwise transfer either its | ||||||
|  |     Contributions or its Contributor Version. | ||||||
|  |  | ||||||
|  | 2.2. Effective Date | ||||||
|  |  | ||||||
|  | The licenses granted in Section 2.1 with respect to any Contribution | ||||||
|  | become effective for each Contribution on the date the Contributor first | ||||||
|  | distributes such Contribution. | ||||||
|  |  | ||||||
|  | 2.3. Limitations on Grant Scope | ||||||
|  |  | ||||||
|  | The licenses granted in this Section 2 are the only rights granted under | ||||||
|  | this License. No additional rights or licenses will be implied from the | ||||||
|  | distribution or licensing of Covered Software under this License. | ||||||
|  | Notwithstanding Section 2.1(b) above, no patent license is granted by a | ||||||
|  | Contributor: | ||||||
|  |  | ||||||
|  | (a) for any code that a Contributor has removed from Covered Software; | ||||||
|  |     or | ||||||
|  |  | ||||||
|  | (b) for infringements caused by: (i) Your and any other third party's | ||||||
|  |     modifications of Covered Software, or (ii) the combination of its | ||||||
|  |     Contributions with other software (except as part of its Contributor | ||||||
|  |     Version); or | ||||||
|  |  | ||||||
|  | (c) under Patent Claims infringed by Covered Software in the absence of | ||||||
|  |     its Contributions. | ||||||
|  |  | ||||||
|  | This License does not grant any rights in the trademarks, service marks, | ||||||
|  | or logos of any Contributor (except as may be necessary to comply with | ||||||
|  | the notice requirements in Section 3.4). | ||||||
|  |  | ||||||
|  | 2.4. Subsequent Licenses | ||||||
|  |  | ||||||
|  | No Contributor makes additional grants as a result of Your choice to | ||||||
|  | distribute the Covered Software under a subsequent version of this | ||||||
|  | License (see Section 10.2) or under the terms of a Secondary License (if | ||||||
|  | permitted under the terms of Section 3.3). | ||||||
|  |  | ||||||
|  | 2.5. Representation | ||||||
|  |  | ||||||
|  | Each Contributor represents that the Contributor believes its | ||||||
|  | Contributions are its original creation(s) or it has sufficient rights | ||||||
|  | to grant the rights to its Contributions conveyed by this License. | ||||||
|  |  | ||||||
|  | 2.6. Fair Use | ||||||
|  |  | ||||||
|  | This License is not intended to limit any rights You have under | ||||||
|  | applicable copyright doctrines of fair use, fair dealing, or other | ||||||
|  | equivalents. | ||||||
|  |  | ||||||
|  | 2.7. Conditions | ||||||
|  |  | ||||||
|  | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted | ||||||
|  | in Section 2.1. | ||||||
|  |  | ||||||
|  | 3. Responsibilities | ||||||
|  | ------------------- | ||||||
|  |  | ||||||
|  | 3.1. Distribution of Source Form | ||||||
|  |  | ||||||
|  | All distribution of Covered Software in Source Code Form, including any | ||||||
|  | Modifications that You create or to which You contribute, must be under | ||||||
|  | the terms of this License. You must inform recipients that the Source | ||||||
|  | Code Form of the Covered Software is governed by the terms of this | ||||||
|  | License, and how they can obtain a copy of this License. You may not | ||||||
|  | attempt to alter or restrict the recipients' rights in the Source Code | ||||||
|  | Form. | ||||||
|  |  | ||||||
|  | 3.2. Distribution of Executable Form | ||||||
|  |  | ||||||
|  | If You distribute Covered Software in Executable Form then: | ||||||
|  |  | ||||||
|  | (a) such Covered Software must also be made available in Source Code | ||||||
|  |     Form, as described in Section 3.1, and You must inform recipients of | ||||||
|  |     the Executable Form how they can obtain a copy of such Source Code | ||||||
|  |     Form by reasonable means in a timely manner, at a charge no more | ||||||
|  |     than the cost of distribution to the recipient; and | ||||||
|  |  | ||||||
|  | (b) You may distribute such Executable Form under the terms of this | ||||||
|  |     License, or sublicense it under different terms, provided that the | ||||||
|  |     license for the Executable Form does not attempt to limit or alter | ||||||
|  |     the recipients' rights in the Source Code Form under this License. | ||||||
|  |  | ||||||
|  | 3.3. Distribution of a Larger Work | ||||||
|  |  | ||||||
|  | You may create and distribute a Larger Work under terms of Your choice, | ||||||
|  | provided that You also comply with the requirements of this License for | ||||||
|  | the Covered Software. If the Larger Work is a combination of Covered | ||||||
|  | Software with a work governed by one or more Secondary Licenses, and the | ||||||
|  | Covered Software is not Incompatible With Secondary Licenses, this | ||||||
|  | License permits You to additionally distribute such Covered Software | ||||||
|  | under the terms of such Secondary License(s), so that the recipient of | ||||||
|  | the Larger Work may, at their option, further distribute the Covered | ||||||
|  | Software under the terms of either this License or such Secondary | ||||||
|  | License(s). | ||||||
|  |  | ||||||
|  | 3.4. Notices | ||||||
|  |  | ||||||
|  | You may not remove or alter the substance of any license notices | ||||||
|  | (including copyright notices, patent notices, disclaimers of warranty, | ||||||
|  | or limitations of liability) contained within the Source Code Form of | ||||||
|  | the Covered Software, except that You may alter any license notices to | ||||||
|  | the extent required to remedy known factual inaccuracies. | ||||||
|  |  | ||||||
|  | 3.5. Application of Additional Terms | ||||||
|  |  | ||||||
|  | You may choose to offer, and to charge a fee for, warranty, support, | ||||||
|  | indemnity or liability obligations to one or more recipients of Covered | ||||||
|  | Software. However, You may do so only on Your own behalf, and not on | ||||||
|  | behalf of any Contributor. You must make it absolutely clear that any | ||||||
|  | such warranty, support, indemnity, or liability obligation is offered by | ||||||
|  | You alone, and You hereby agree to indemnify every Contributor for any | ||||||
|  | liability incurred by such Contributor as a result of warranty, support, | ||||||
|  | indemnity or liability terms You offer. You may include additional | ||||||
|  | disclaimers of warranty and limitations of liability specific to any | ||||||
|  | jurisdiction. | ||||||
|  |  | ||||||
|  | 4. Inability to Comply Due to Statute or Regulation | ||||||
|  | --------------------------------------------------- | ||||||
|  |  | ||||||
|  | If it is impossible for You to comply with any of the terms of this | ||||||
|  | License with respect to some or all of the Covered Software due to | ||||||
|  | statute, judicial order, or regulation then You must: (a) comply with | ||||||
|  | the terms of this License to the maximum extent possible; and (b) | ||||||
|  | describe the limitations and the code they affect. Such description must | ||||||
|  | be placed in a text file included with all distributions of the Covered | ||||||
|  | Software under this License. Except to the extent prohibited by statute | ||||||
|  | or regulation, such description must be sufficiently detailed for a | ||||||
|  | recipient of ordinary skill to be able to understand it. | ||||||
|  |  | ||||||
|  | 5. Termination | ||||||
|  | -------------- | ||||||
|  |  | ||||||
|  | 5.1. The rights granted under this License will terminate automatically | ||||||
|  | if You fail to comply with any of its terms. However, if You become | ||||||
|  | compliant, then the rights granted under this License from a particular | ||||||
|  | Contributor are reinstated (a) provisionally, unless and until such | ||||||
|  | Contributor explicitly and finally terminates Your grants, and (b) on an | ||||||
|  | ongoing basis, if such Contributor fails to notify You of the | ||||||
|  | non-compliance by some reasonable means prior to 60 days after You have | ||||||
|  | come back into compliance. Moreover, Your grants from a particular | ||||||
|  | Contributor are reinstated on an ongoing basis if such Contributor | ||||||
|  | notifies You of the non-compliance by some reasonable means, this is the | ||||||
|  | first time You have received notice of non-compliance with this License | ||||||
|  | from such Contributor, and You become compliant prior to 30 days after | ||||||
|  | Your receipt of the notice. | ||||||
|  |  | ||||||
|  | 5.2. If You initiate litigation against any entity by asserting a patent | ||||||
|  | infringement claim (excluding declaratory judgment actions, | ||||||
|  | counter-claims, and cross-claims) alleging that a Contributor Version | ||||||
|  | directly or indirectly infringes any patent, then the rights granted to | ||||||
|  | You by any and all Contributors for the Covered Software under Section | ||||||
|  | 2.1 of this License shall terminate. | ||||||
|  |  | ||||||
|  | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all | ||||||
|  | end user license agreements (excluding distributors and resellers) which | ||||||
|  | have been validly granted by You or Your distributors under this License | ||||||
|  | prior to termination shall survive termination. | ||||||
|  |  | ||||||
|  | ************************************************************************ | ||||||
|  | *                                                                      * | ||||||
|  | *  6. Disclaimer of Warranty                                           * | ||||||
|  | *  -------------------------                                           * | ||||||
|  | *                                                                      * | ||||||
|  | *  Covered Software is provided under this License on an "as is"       * | ||||||
|  | *  basis, without warranty of any kind, either expressed, implied, or  * | ||||||
|  | *  statutory, including, without limitation, warranties that the       * | ||||||
|  | *  Covered Software is free of defects, merchantable, fit for a        * | ||||||
|  | *  particular purpose or non-infringing. The entire risk as to the     * | ||||||
|  | *  quality and performance of the Covered Software is with You.        * | ||||||
|  | *  Should any Covered Software prove defective in any respect, You     * | ||||||
|  | *  (not any Contributor) assume the cost of any necessary servicing,   * | ||||||
|  | *  repair, or correction. This disclaimer of warranty constitutes an   * | ||||||
|  | *  essential part of this License. No use of any Covered Software is   * | ||||||
|  | *  authorized under this License except under this disclaimer.         * | ||||||
|  | *                                                                      * | ||||||
|  | ************************************************************************ | ||||||
|  |  | ||||||
|  | ************************************************************************ | ||||||
|  | *                                                                      * | ||||||
|  | *  7. Limitation of Liability                                          * | ||||||
|  | *  --------------------------                                          * | ||||||
|  | *                                                                      * | ||||||
|  | *  Under no circumstances and under no legal theory, whether tort      * | ||||||
|  | *  (including negligence), contract, or otherwise, shall any           * | ||||||
|  | *  Contributor, or anyone who distributes Covered Software as          * | ||||||
|  | *  permitted above, be liable to You for any direct, indirect,         * | ||||||
|  | *  special, incidental, or consequential damages of any character      * | ||||||
|  | *  including, without limitation, damages for lost profits, loss of    * | ||||||
|  | *  goodwill, work stoppage, computer failure or malfunction, or any    * | ||||||
|  | *  and all other commercial damages or losses, even if such party      * | ||||||
|  | *  shall have been informed of the possibility of such damages. This   * | ||||||
|  | *  limitation of liability shall not apply to liability for death or   * | ||||||
|  | *  personal injury resulting from such party's negligence to the       * | ||||||
|  | *  extent applicable law prohibits such limitation. Some               * | ||||||
|  | *  jurisdictions do not allow the exclusion or limitation of           * | ||||||
|  | *  incidental or consequential damages, so this exclusion and          * | ||||||
|  | *  limitation may not apply to You.                                    * | ||||||
|  | *                                                                      * | ||||||
|  | ************************************************************************ | ||||||
|  |  | ||||||
|  | 8. Litigation | ||||||
|  | ------------- | ||||||
|  |  | ||||||
|  | Any litigation relating to this License may be brought only in the | ||||||
|  | courts of a jurisdiction where the defendant maintains its principal | ||||||
|  | place of business and such litigation shall be governed by laws of that | ||||||
|  | jurisdiction, without reference to its conflict-of-law provisions. | ||||||
|  | Nothing in this Section shall prevent a party's ability to bring | ||||||
|  | cross-claims or counter-claims. | ||||||
|  |  | ||||||
|  | 9. Miscellaneous | ||||||
|  | ---------------- | ||||||
|  |  | ||||||
|  | This License represents the complete agreement concerning the subject | ||||||
|  | matter hereof. If any provision of this License is held to be | ||||||
|  | unenforceable, such provision shall be reformed only to the extent | ||||||
|  | necessary to make it enforceable. Any law or regulation which provides | ||||||
|  | that the language of a contract shall be construed against the drafter | ||||||
|  | shall not be used to construe this License against a Contributor. | ||||||
|  |  | ||||||
|  | 10. Versions of the License | ||||||
|  | --------------------------- | ||||||
|  |  | ||||||
|  | 10.1. New Versions | ||||||
|  |  | ||||||
|  | Mozilla Foundation is the license steward. Except as provided in Section | ||||||
|  | 10.3, no one other than the license steward has the right to modify or | ||||||
|  | publish new versions of this License. Each version will be given a | ||||||
|  | distinguishing version number. | ||||||
|  |  | ||||||
|  | 10.2. Effect of New Versions | ||||||
|  |  | ||||||
|  | You may distribute the Covered Software under the terms of the version | ||||||
|  | of the License under which You originally received the Covered Software, | ||||||
|  | or under the terms of any subsequent version published by the license | ||||||
|  | steward. | ||||||
|  |  | ||||||
|  | 10.3. Modified Versions | ||||||
|  |  | ||||||
|  | If you create software not governed by this License, and you want to | ||||||
|  | create a new license for such software, you may create and use a | ||||||
|  | modified version of this License if you rename the license and remove | ||||||
|  | any references to the name of the license steward (except to note that | ||||||
|  | such modified license differs from this License). | ||||||
|  |  | ||||||
|  | 10.4. Distributing Source Code Form that is Incompatible With Secondary | ||||||
|  | Licenses | ||||||
|  |  | ||||||
|  | If You choose to distribute Source Code Form that is Incompatible With | ||||||
|  | Secondary Licenses under the terms of this version of the License, the | ||||||
|  | notice described in Exhibit B of this License must be attached. | ||||||
|  |  | ||||||
|  | Exhibit A - Source Code Form License Notice | ||||||
|  | ------------------------------------------- | ||||||
|  |  | ||||||
|  |   This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |   License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |   file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | If it is not possible or desirable to put the notice in a particular | ||||||
|  | file, then You may include the notice in a location (such as a LICENSE | ||||||
|  | file in a relevant directory) where a recipient would be likely to look | ||||||
|  | for such a notice. | ||||||
|  |  | ||||||
|  | You may add additional accurate notices of copyright ownership. | ||||||
|  |  | ||||||
|  | Exhibit B - "Incompatible With Secondary Licenses" Notice | ||||||
|  | --------------------------------------------------------- | ||||||
|  |  | ||||||
|  |   This Source Code Form is "Incompatible With Secondary Licenses", as | ||||||
|  |   defined by the Mozilla Public License, v. 2.0. | ||||||
							
								
								
									
										91
									
								
								public/novnc/docs/LICENSE.OFL-1.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,91 @@ | |||||||
|  | This Font Software is licensed under the SIL Open Font License, Version 1.1. | ||||||
|  | This license is copied below, and is also available with a FAQ at: | ||||||
|  | http://scripts.sil.org/OFL | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------- | ||||||
|  | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 | ||||||
|  | ----------------------------------------------------------- | ||||||
|  |  | ||||||
|  | PREAMBLE | ||||||
|  | The goals of the Open Font License (OFL) are to stimulate worldwide | ||||||
|  | development of collaborative font projects, to support the font creation | ||||||
|  | efforts of academic and linguistic communities, and to provide a free and | ||||||
|  | open framework in which fonts may be shared and improved in partnership | ||||||
|  | with others. | ||||||
|  |  | ||||||
|  | The OFL allows the licensed fonts to be used, studied, modified and | ||||||
|  | redistributed freely as long as they are not sold by themselves. The | ||||||
|  | fonts, including any derivative works, can be bundled, embedded,  | ||||||
|  | redistributed and/or sold with any software provided that any reserved | ||||||
|  | names are not used by derivative works. The fonts and derivatives, | ||||||
|  | however, cannot be released under any other type of license. The | ||||||
|  | requirement for fonts to remain under this license does not apply | ||||||
|  | to any document created using the fonts or their derivatives. | ||||||
|  |  | ||||||
|  | DEFINITIONS | ||||||
|  | "Font Software" refers to the set of files released by the Copyright | ||||||
|  | Holder(s) under this license and clearly marked as such. This may | ||||||
|  | include source files, build scripts and documentation. | ||||||
|  |  | ||||||
|  | "Reserved Font Name" refers to any names specified as such after the | ||||||
|  | copyright statement(s). | ||||||
|  |  | ||||||
|  | "Original Version" refers to the collection of Font Software components as | ||||||
|  | distributed by the Copyright Holder(s). | ||||||
|  |  | ||||||
|  | "Modified Version" refers to any derivative made by adding to, deleting, | ||||||
|  | or substituting -- in part or in whole -- any of the components of the | ||||||
|  | Original Version, by changing formats or by porting the Font Software to a | ||||||
|  | new environment. | ||||||
|  |  | ||||||
|  | "Author" refers to any designer, engineer, programmer, technical | ||||||
|  | writer or other person who contributed to the Font Software. | ||||||
|  |  | ||||||
|  | PERMISSION & CONDITIONS | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining | ||||||
|  | a copy of the Font Software, to use, study, copy, merge, embed, modify, | ||||||
|  | redistribute, and sell modified and unmodified copies of the Font | ||||||
|  | Software, subject to the following conditions: | ||||||
|  |  | ||||||
|  | 1) Neither the Font Software nor any of its individual components, | ||||||
|  | in Original or Modified Versions, may be sold by itself. | ||||||
|  |  | ||||||
|  | 2) Original or Modified Versions of the Font Software may be bundled, | ||||||
|  | redistributed and/or sold with any software, provided that each copy | ||||||
|  | contains the above copyright notice and this license. These can be | ||||||
|  | included either as stand-alone text files, human-readable headers or | ||||||
|  | in the appropriate machine-readable metadata fields within text or | ||||||
|  | binary files as long as those fields can be easily viewed by the user. | ||||||
|  |  | ||||||
|  | 3) No Modified Version of the Font Software may use the Reserved Font | ||||||
|  | Name(s) unless explicit written permission is granted by the corresponding | ||||||
|  | Copyright Holder. This restriction only applies to the primary font name as | ||||||
|  | presented to the users. | ||||||
|  |  | ||||||
|  | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font | ||||||
|  | Software shall not be used to promote, endorse or advertise any | ||||||
|  | Modified Version, except to acknowledge the contribution(s) of the | ||||||
|  | Copyright Holder(s) and the Author(s) or with their explicit written | ||||||
|  | permission. | ||||||
|  |  | ||||||
|  | 5) The Font Software, modified or unmodified, in part or in whole, | ||||||
|  | must be distributed entirely under this license, and must not be | ||||||
|  | distributed under any other license. The requirement for fonts to | ||||||
|  | remain under this license does not apply to any document created | ||||||
|  | using the Font Software. | ||||||
|  |  | ||||||
|  | TERMINATION | ||||||
|  | This license becomes null and void if any of the above conditions are | ||||||
|  | not met. | ||||||
|  |  | ||||||
|  | DISCLAIMER | ||||||
|  | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||||
|  | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF | ||||||
|  | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT | ||||||
|  | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE | ||||||
|  | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||||||
|  | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL | ||||||
|  | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||||
|  | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM | ||||||
|  | OTHER DEALINGS IN THE FONT SOFTWARE. | ||||||
							
								
								
									
										21
									
								
								public/novnc/docs/LICENSE.pako
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | |||||||
|  | (The MIT License) | ||||||
|  |  | ||||||
|  | Copyright (C) 2014 by Vitaly Puzrin | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright notice and this permission notice shall be included in | ||||||
|  | all copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  | THE SOFTWARE. | ||||||
							
								
								
									
										27
									
								
								public/novnc/docs/LICENSE.zlib
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,27 @@ | |||||||
|  | Copyright (c) <year>, <copyright holder> | ||||||
|  | All rights reserved. | ||||||
|  |  | ||||||
|  | This software is provided 'as-is', without any express | ||||||
|  | or implied warranty.  In no event will the authors be | ||||||
|  | held liable for any damages arising from the use of | ||||||
|  | this software. | ||||||
|  |  | ||||||
|  | Permission is granted to anyone to use this software | ||||||
|  | for any purpose, including commercial applications, | ||||||
|  | and to alter it and redistribute it freely, subject to | ||||||
|  | the following restrictions: | ||||||
|  |  | ||||||
|  | 1. The origin of this software must not be | ||||||
|  |    misrepresented; you must not claim that you | ||||||
|  |    wrote the original software. If you use this | ||||||
|  |    software in a product, an acknowledgment in | ||||||
|  |    the product documentation would be appreciated | ||||||
|  |    but is not required. | ||||||
|  |  | ||||||
|  | 2. Altered source versions must be plainly marked | ||||||
|  |    as such, and must not be misrepresented as | ||||||
|  |    being the original software. | ||||||
|  |  | ||||||
|  | 3. This notice may not be removed or altered from | ||||||
|  |    any source distribution. | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								public/novnc/docs/VERSION
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | 0.6.2 | ||||||
							
								
								
									
										4
									
								
								public/novnc/docs/flash_policy.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | Manual setup: | ||||||
|  |  | ||||||
|  | DATA="echo \'<cross-domain-policy><allow-access-from domain=\\\"*\\\" to-ports=\\\"*\\\" /></cross-domain-policy>\'" | ||||||
|  | /usr/bin/socat -T 1 TCP-L:843,reuseaddr,fork,crlf SYSTEM:"$DATA" | ||||||
							
								
								
									
										76
									
								
								public/novnc/docs/links
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,76 @@ | |||||||
|  | New tight PNG protocol: | ||||||
|  |     http://wiki.qemu.org/VNC_Tight_PNG | ||||||
|  |     http://xf.iksaif.net/blog/index.php?post/2010/06/14/QEMU:-Tight-PNG-and-some-profiling | ||||||
|  |  | ||||||
|  | RFB protocol and extensions: | ||||||
|  |     http://tigervnc.org/cgi-bin/rfbproto | ||||||
|  |  | ||||||
|  | Canvas Browser Compatibility: | ||||||
|  |     http://philip.html5.org/tests/canvas/suite/tests/results.html | ||||||
|  |  | ||||||
|  | WebSockets API standard: | ||||||
|  |     http://www.whatwg.org/specs/web-apps/current-work/complete.html#websocket | ||||||
|  |     http://dev.w3.org/html5/websockets/ | ||||||
|  |     http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt | ||||||
|  |  | ||||||
|  | Browser Keyboard Events detailed: | ||||||
|  |     http://unixpapa.com/js/key.html | ||||||
|  |  | ||||||
|  | ActionScript (Flash) WebSocket implementation: | ||||||
|  |     http://github.com/gimite/web-socket-js | ||||||
|  |  | ||||||
|  | ActionScript (Flash) crypto/TLS library: | ||||||
|  |     http://code.google.com/p/as3crypto | ||||||
|  |     http://github.com/lyokato/as3crypto_patched | ||||||
|  |  | ||||||
|  | TLS Protocol: | ||||||
|  |     http://en.wikipedia.org/wiki/Transport_Layer_Security | ||||||
|  |  | ||||||
|  | Generate self-signed certificate: | ||||||
|  |     http://docs.python.org/dev/library/ssl.html#certificates | ||||||
|  |  | ||||||
|  | Cursor appearance/style (for Cursor pseudo-encoding): | ||||||
|  |     http://en.wikipedia.org/wiki/ICO_(file_format) | ||||||
|  |     http://www.daubnet.com/en/file-format-cur | ||||||
|  |     https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property | ||||||
|  |     http://www.fileformat.info/format/bmp/egff.htm | ||||||
|  |  | ||||||
|  | Icon/Cursor file format: | ||||||
|  |     http://msdn.microsoft.com/en-us/library/ms997538 | ||||||
|  |     http://msdn.microsoft.com/en-us/library/aa921550.aspx | ||||||
|  |     http://msdn.microsoft.com/en-us/library/aa930622.aspx | ||||||
|  |  | ||||||
|  |  | ||||||
|  | RDP Protocol specification: | ||||||
|  |     http://msdn.microsoft.com/en-us/library/cc240445(v=PROT.10).aspx | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Related projects: | ||||||
|  |      | ||||||
|  |     guacamole: http://guacamole.sourceforge.net/ | ||||||
|  |  | ||||||
|  |         - Web client, but Java servlet does pre-processing | ||||||
|  |  | ||||||
|  |     jsvnc: http://code.google.com/p/jsvnc/ | ||||||
|  |  | ||||||
|  |         - No releases | ||||||
|  |  | ||||||
|  |     webvnc: http://code.google.com/p/webvnc/ | ||||||
|  |  | ||||||
|  |         - Jetty web server gateway, no updates since April 2008. | ||||||
|  |  | ||||||
|  |     RealVNC Java applet: http://www.realvnc.com/support/javavncviewer.html | ||||||
|  |  | ||||||
|  |         - Java applet | ||||||
|  |  | ||||||
|  |     Flashlight-VNC: http://www.wizhelp.com/flashlight-vnc/ | ||||||
|  |  | ||||||
|  |         - Adobe Flash implementation | ||||||
|  |  | ||||||
|  |     FVNC: http://osflash.org/fvnc | ||||||
|  |  | ||||||
|  |         - Adbove Flash implementation | ||||||
|  |  | ||||||
|  |     CanVNC: http://canvnc.sourceforge.net/ | ||||||
|  |  | ||||||
|  |         - HTML client with REST to VNC python proxy. Mostly vapor. | ||||||
							
								
								
									
										5
									
								
								public/novnc/docs/notes
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | |||||||
|  | Rebuilding inflator.js | ||||||
|  |  | ||||||
|  | - Download pako from npm | ||||||
|  | - Install browserify using npm | ||||||
|  | - browserify utils/inflator.partial.js -o include/inflator.js -s inflator | ||||||
							
								
								
									
										34
									
								
								public/novnc/docs/release.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | |||||||
|  | - Decide a new version number X.Y.Z (follow SemVer) | ||||||
|  | - Update version in package.json | ||||||
|  | - Update version in docs/VERSION | ||||||
|  | - Commit the change with a commit like "Release X.Y.Z" | ||||||
|  | - Add a new release on GitHub called "vX.Y.Z", and populate it with | ||||||
|  |   release notes of the following form (where A.B.C is the last release): | ||||||
|  |  | ||||||
|  | Major Changes Since A.B.C | ||||||
|  | ========================= | ||||||
|  |  | ||||||
|  | *Insert warnings here about incompatibilities* | ||||||
|  |  | ||||||
|  | *Thanks to all the contributors who filed bugs, added features, and fixed bugs | ||||||
|  | during this release :tada:* | ||||||
|  |  | ||||||
|  | App-visible Changes | ||||||
|  | ------------------- | ||||||
|  |  | ||||||
|  | - *feature* a feature which improves the app usage (#PRNUM) | ||||||
|  | - *bugfix* a bug fix which fixes the app usage (#PRNUM) | ||||||
|  | - *refactor* a refactor which changes the app usage (#PRNUM) | ||||||
|  |  | ||||||
|  | Library-visible Changes | ||||||
|  | ----------------------- | ||||||
|  |  | ||||||
|  | - *feature* a feature which improves the noVNC APIs (#PRNUM) | ||||||
|  | - *bugfix* a bug fix which fixes the noVNC APIs (#PRNUM) | ||||||
|  | - *refactor* a refactor which changes the noVNC APIs (#PRNUM) | ||||||
|  |  | ||||||
|  | App-internals Changes | ||||||
|  | --------------------- | ||||||
|  |  | ||||||
|  | - *bugfix* a bug fix with affects the internals of noVNC only (#PRNUM) | ||||||
|  | - *refactor* a refactor which affects the internals of noVNC only (#PRNUM) | ||||||
							
								
								
									
										147
									
								
								public/novnc/docs/rfb_notes
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,147 @@ | |||||||
|  | 5.1.1 ProtocolVersion: 12, 12 bytes | ||||||
|  |  | ||||||
|  |     - Sent by server, max supported | ||||||
|  |         12 ascii - "RFB 003.008\n" | ||||||
|  |     - Response by client, version to use | ||||||
|  |         12 ascii - "RFB 003.003\n" | ||||||
|  |  | ||||||
|  | 5.1.2 Authentication: >=4, [16, 4] bytes | ||||||
|  |  | ||||||
|  |     - Sent by server | ||||||
|  |         CARD32 - authentication-scheme | ||||||
|  |                 0 - connection failed | ||||||
|  |                     CARD32 - length | ||||||
|  |                     length - reason | ||||||
|  |                 1 - no authentication | ||||||
|  |  | ||||||
|  |                 2 - VNC authentication | ||||||
|  |                     16 CARD8 - challenge (random bytes) | ||||||
|  |  | ||||||
|  |     - Response by client (if VNC authentication) | ||||||
|  |         16 CARD8 - client encrypts the challenge with DES, using user | ||||||
|  |                    password as key, sends resulting 16 byte response | ||||||
|  |  | ||||||
|  |     - Response by server (if VNC authentication)  | ||||||
|  |         CARD32 - 0 - OK | ||||||
|  |                  1 - failed | ||||||
|  |                  2 - too-many | ||||||
|  |  | ||||||
|  | 5.1.3 ClientInitialisation: 1 byte | ||||||
|  |     - Sent by client | ||||||
|  |         CARD8 - shared-flag, 0 exclusive, non-zero shared | ||||||
|  |  | ||||||
|  | 5.1.4 ServerInitialisation: >=24 bytes | ||||||
|  |     - Sent by server | ||||||
|  |         CARD16 - framebuffer-width | ||||||
|  |         CARD16 - framebuffer-height | ||||||
|  |         16 byte PIXEL_FORMAT - server-pixel-format | ||||||
|  |             CARD8 - bits-per-pixel | ||||||
|  |             CARD8 - depth | ||||||
|  |             CARD8 - big-endian-flag, non-zero is big endian | ||||||
|  |             CARD8 - true-color-flag, non-zero then next 6 apply | ||||||
|  |             CARD16 - red-max | ||||||
|  |             CARD16 - green-max | ||||||
|  |             CARD16 - blue-max | ||||||
|  |             CARD8 - red-shift | ||||||
|  |             CARD8 - green-shift | ||||||
|  |             CARD8 - blue-shift | ||||||
|  |             3 bytes - padding | ||||||
|  |         CARD32 - name-length | ||||||
|  |  | ||||||
|  |         CARD8[length] - name-string | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Client to Server Messages: | ||||||
|  |  | ||||||
|  | 5.2.1 SetPixelFormat: 20 bytes | ||||||
|  |     CARD8: 0 - message-type | ||||||
|  |     ... | ||||||
|  |  | ||||||
|  | 5.2.2 FixColourMapEntries: >=6 bytes | ||||||
|  |     CARD8: 1 - message-type | ||||||
|  |     ... | ||||||
|  |  | ||||||
|  | 5.2.3 SetEncodings: >=8 bytes | ||||||
|  |     CARD8: 2 - message-type | ||||||
|  |     CARD8    - padding | ||||||
|  |     CARD16   - numer-of-encodings | ||||||
|  |  | ||||||
|  |     CARD32   - encoding-type in preference order | ||||||
|  |         0 - raw | ||||||
|  |         1 - copy-rectangle | ||||||
|  |         2 - RRE | ||||||
|  |         4 - CoRRE | ||||||
|  |         5 - hextile | ||||||
|  |  | ||||||
|  | 5.2.4 FramebufferUpdateRequest (10 bytes) | ||||||
|  |     CARD8: 3 - message-type | ||||||
|  |     CARD8    - incremental (0 for full-update, non-zero for incremental) | ||||||
|  |     CARD16   - x-position | ||||||
|  |     CARD16   - y-position | ||||||
|  |     CARD16   - width | ||||||
|  |     CARD16   - height | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 5.2.5 KeyEvent: 8 bytes | ||||||
|  |     CARD8: 4 - message-type | ||||||
|  |     CARD8    - down-flag | ||||||
|  |     2 bytes  - padding | ||||||
|  |     CARD32   - key (X-Windows keysym values) | ||||||
|  |  | ||||||
|  | 5.2.6 PointerEvent: 6 bytes | ||||||
|  |     CARD8: 5 - message-type | ||||||
|  |     CARD8    - button-mask | ||||||
|  |     CARD16   - x-position | ||||||
|  |     CARD16   - y-position | ||||||
|  |  | ||||||
|  | 5.2.7 ClientCutText: >=9 bytes | ||||||
|  |     CARD8: 6 - message-type | ||||||
|  |     ... | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Server to Client Messages: | ||||||
|  |  | ||||||
|  | 5.3.1 FramebufferUpdate | ||||||
|  |     CARD8: 0 - message-type | ||||||
|  |     1 byte   - padding | ||||||
|  |     CARD16   - number-of-rectangles | ||||||
|  |  | ||||||
|  |     CARD16   - x-position | ||||||
|  |     CARD16   - y-position | ||||||
|  |     CARD16   - width | ||||||
|  |     CARD16   - height | ||||||
|  |     CARD16   - encoding-type: | ||||||
|  |         0 - raw | ||||||
|  |         1 - copy rectangle | ||||||
|  |         2 - RRE | ||||||
|  |         4 - CoRRE | ||||||
|  |         5 - hextile | ||||||
|  |  | ||||||
|  |         raw: | ||||||
|  |             - width x height pixel values | ||||||
|  |  | ||||||
|  |         copy rectangle:  | ||||||
|  |             CARD16 - src-x-position | ||||||
|  |             CARD16 - src-y-position | ||||||
|  |  | ||||||
|  |         RRE: | ||||||
|  |             CARD32  - N number-of-subrectangles | ||||||
|  |             Nxd bytes - background-pixel-value (d bits-per-pixel) | ||||||
|  |  | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  | 5.3.2 SetColourMapEntries (no support) | ||||||
|  |     CARD8: 1 - message-type | ||||||
|  |     ... | ||||||
|  |  | ||||||
|  | 5.3.3 Bell | ||||||
|  |     CARD8: 2 - message-type | ||||||
|  |  | ||||||
|  | 5.3.4 ServerCutText | ||||||
|  |     CARD8: 3 - message-type | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |      | ||||||
							
								
								
									
										
											BIN
										
									
								
								public/novnc/docs/rfbproto-3.3.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/novnc/docs/rfbproto-3.7.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/novnc/docs/rfbproto-3.8.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/novnc/favicon.ico
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | images/favicon.ico | ||||||
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/alt.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 339 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/clipboard.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 501 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/connect.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 404 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/ctrl.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 354 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/ctrlaltdel.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 317 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/disconnect.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/drag.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 963 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/esc.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 385 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/favicon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 453 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/fullscreen.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 851 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/keyboard.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/mouse_left.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 511 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/mouse_middle.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 517 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/mouse_none.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 497 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/mouse_right.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 513 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/power.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 390 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/screen_320x460.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 12 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/screen_57x57.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/screen_700x700.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 18 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/settings.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/tab.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 387 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/images/toggleextrakeys.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 735 B | 
							
								
								
									
										
											BIN
										
									
								
								public/novnc/include/Orbitron700.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/novnc/include/Orbitron700.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										527
									
								
								public/novnc/include/base.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,527 @@ | |||||||
|  | /* | ||||||
|  |  * noVNC base CSS | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Copyright (C) 2016 Samuel Mannehed for Cendio AB | ||||||
|  |  * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) | ||||||
|  |  * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | body { | ||||||
|  |   margin:0; | ||||||
|  |   padding:0; | ||||||
|  |   font-family: Helvetica; | ||||||
|  |   /*Background image with light grey curve.*/ | ||||||
|  |   background-color:#494949; | ||||||
|  |   background-repeat:no-repeat; | ||||||
|  |   background-position:right bottom; | ||||||
|  |   height:100%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | html { | ||||||
|  |   height:100%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_controls ul { | ||||||
|  |   list-style: none; | ||||||
|  |   margin: 0px; | ||||||
|  |   padding: 0px; | ||||||
|  | } | ||||||
|  | #noVNC_controls li { | ||||||
|  |   padding-bottom:8px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_setting_host { | ||||||
|  |   width:150px; | ||||||
|  | } | ||||||
|  | #noVNC_setting_port { | ||||||
|  |   width: 80px; | ||||||
|  | } | ||||||
|  | #noVNC_setting_password { | ||||||
|  |   width: 150px; | ||||||
|  | } | ||||||
|  | #noVNC_setting_encrypt { | ||||||
|  | } | ||||||
|  | #noVNC_setting_path { | ||||||
|  |   width: 100px; | ||||||
|  | } | ||||||
|  | #noVNC_connect_button { | ||||||
|  |   width: 110px; | ||||||
|  |   float:right; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_buttons { | ||||||
|  |   white-space: nowrap; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_view_drag_button { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | #noVNC_sendCtrlAltDel_button { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | #noVNC_fullscreen_button { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | #noVNC_xvp_buttons { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | #noVNC_mobile_buttons { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_extra_keys { | ||||||
|  |   display: inline; | ||||||
|  |   list-style-type: none; | ||||||
|  |   padding: 0px; | ||||||
|  |   margin: 0px; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_buttons_left { | ||||||
|  |   float: left; | ||||||
|  |   z-index: 1; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_buttons_right { | ||||||
|  |   float:right; | ||||||
|  |   right: 0px; | ||||||
|  |   z-index: 2; | ||||||
|  |   position: absolute; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_status { | ||||||
|  |   font-size: 12px; | ||||||
|  |   padding-top: 4px; | ||||||
|  |   height:32px; | ||||||
|  |   text-align: center; | ||||||
|  |   font-weight: bold; | ||||||
|  |   color: #fff; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_settings_menu { | ||||||
|  |   margin: 3px; | ||||||
|  |   text-align: left; | ||||||
|  | } | ||||||
|  | #noVNC_settings_menu ul { | ||||||
|  |   list-style: none; | ||||||
|  |   margin: 0px; | ||||||
|  |   padding: 0px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_settings_apply { | ||||||
|  |   float:right; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_container { | ||||||
|  |   display: table; | ||||||
|  |   width:100%; | ||||||
|  |   height:100%; | ||||||
|  |   background-color:#313131; | ||||||
|  |   border-bottom-right-radius: 800px 600px; | ||||||
|  |   /*border-top-left-radius: 800px 600px;*/ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_screen { | ||||||
|  |   display: none; | ||||||
|  |   position: absolute; | ||||||
|  |   margin: 0px; | ||||||
|  |   padding: 0px; | ||||||
|  |   bottom: 0px; | ||||||
|  |   top: 36px; /* the height of the control bar */ | ||||||
|  |   left: 0px; | ||||||
|  |   right: 0px; | ||||||
|  |   width: auto; | ||||||
|  |   height: auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Do not set width/height for VNC_canvas or incorrect | ||||||
|  |  * scaling will occur. Canvas size depends on remote VNC | ||||||
|  |  * settings and noVNC settings. */ | ||||||
|  | #noVNC_canvas { | ||||||
|  |   position: absolute; | ||||||
|  |   left: 0; | ||||||
|  |   right: 0; | ||||||
|  |   margin-left: auto; | ||||||
|  |   margin-right: auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #VNC_clipboard_clear_button { | ||||||
|  |   float:right; | ||||||
|  | } | ||||||
|  | #VNC_clipboard_text { | ||||||
|  |   font-size: 11px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_clipboard_clear_button { | ||||||
|  |   float:right; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*Bubble contents divs*/ | ||||||
|  | #noVNC_settings { | ||||||
|  |   display:none; | ||||||
|  |   margin-top:73px; | ||||||
|  |   right:20px; | ||||||
|  |   position:fixed; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_controls { | ||||||
|  |   display:none; | ||||||
|  |   margin-top:73px; | ||||||
|  |   right:12px; | ||||||
|  |   position:fixed; | ||||||
|  | } | ||||||
|  | #noVNC_controls.top:after  { | ||||||
|  |   right:15px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_description { | ||||||
|  |   display:none; | ||||||
|  |   position:fixed; | ||||||
|  |  | ||||||
|  |   margin-top:73px; | ||||||
|  |   right:20px; | ||||||
|  |   left:20px; | ||||||
|  |   padding:15px; | ||||||
|  |   color:#000; | ||||||
|  |   background:#eee; /* default background for browsers without gradient support */ | ||||||
|  |  | ||||||
|  |   border:2px solid #E0E0E0; | ||||||
|  |   -webkit-border-radius:10px; | ||||||
|  |   -moz-border-radius:10px; | ||||||
|  |   border-radius:10px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_popup_status { | ||||||
|  |   display:none; | ||||||
|  |   position: fixed; | ||||||
|  |   z-index: 1; | ||||||
|  |  | ||||||
|  |   margin:15px; | ||||||
|  |   margin-top:60px; | ||||||
|  |   padding:15px; | ||||||
|  |   width:auto; | ||||||
|  |  | ||||||
|  |   text-align:center; | ||||||
|  |   font-weight:bold; | ||||||
|  |   word-wrap:break-word; | ||||||
|  |   color:#fff; | ||||||
|  |   background:rgba(0,0,0,0.65); | ||||||
|  |  | ||||||
|  |   -webkit-border-radius:10px; | ||||||
|  |   -moz-border-radius:10px; | ||||||
|  |   border-radius:10px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_xvp { | ||||||
|  |   display:none; | ||||||
|  |   margin-top:73px; | ||||||
|  |   right:30px; | ||||||
|  |   position:fixed; | ||||||
|  | } | ||||||
|  | #noVNC_xvp.top:after { | ||||||
|  |   right:125px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_clipboard { | ||||||
|  |   display:none; | ||||||
|  |   margin-top:73px; | ||||||
|  |   right:30px; | ||||||
|  |   position:fixed; | ||||||
|  | } | ||||||
|  | #noVNC_clipboard.top:after { | ||||||
|  |   right:85px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_keyboardinput { | ||||||
|  |   width:1px; | ||||||
|  |   height:1px; | ||||||
|  |   background-color:#fff; | ||||||
|  |   color:#fff; | ||||||
|  |   border:0; | ||||||
|  |   position: relative; | ||||||
|  |   left: -40px; | ||||||
|  |   z-index: -1; | ||||||
|  |   ime-mode: disabled; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Advanced Styling | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | .noVNC_status_normal { | ||||||
|  |   background: #b2bdcd; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ | ||||||
|  |   background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ | ||||||
|  | } | ||||||
|  | .noVNC_status_error { | ||||||
|  |   background: #f04040; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ | ||||||
|  |   background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ | ||||||
|  | } | ||||||
|  | .noVNC_status_warn { | ||||||
|  |   background: #f0f040; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ | ||||||
|  |   background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Control bar */ | ||||||
|  | #noVNC_control_bar { | ||||||
|  |   position:fixed; | ||||||
|  |    | ||||||
|  |   display:block; | ||||||
|  |   height:36px; | ||||||
|  |   left:0; | ||||||
|  |   top:0; | ||||||
|  |   width:100%; | ||||||
|  |   z-index:200; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_status_button { | ||||||
|  |   padding: 4px 4px; | ||||||
|  |   vertical-align: middle; | ||||||
|  |   border:1px solid #869dbc; | ||||||
|  |   -webkit-border-radius: 6px; | ||||||
|  |   -moz-border-radius: 6px; | ||||||
|  |   border-radius: 6px; | ||||||
|  |   background: #b2bdcd; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ | ||||||
|  |   filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */ | ||||||
|  |   background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ | ||||||
|  |   /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_status_button_selected { | ||||||
|  |   padding: 4px 4px; | ||||||
|  |   vertical-align: middle; | ||||||
|  |   border:1px solid #4366a9; | ||||||
|  |   -webkit-border-radius: 6px; | ||||||
|  |   -moz-border-radius: 6px; | ||||||
|  |   background: #779ced; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */ | ||||||
|  |   filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */ | ||||||
|  |   background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */ | ||||||
|  |  /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_status_button:disabled { | ||||||
|  |   opacity: 0.4; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /*Settings Bubble*/ | ||||||
|  | .triangle-right { | ||||||
|  |   position:relative; | ||||||
|  |   padding:15px; | ||||||
|  |   margin:1em 0 3em; | ||||||
|  |   color:#fff; | ||||||
|  |   background:#fff; /* default background for browsers without gradient support */ | ||||||
|  |   /* css3 */ | ||||||
|  |   /*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698)); | ||||||
|  |   background:-moz-linear-gradient(#2e88c4, #075698); | ||||||
|  |   background:-o-linear-gradient(#2e88c4, #075698); | ||||||
|  |   background:linear-gradient(#2e88c4, #075698);*/ | ||||||
|  |   -webkit-border-radius:10px; | ||||||
|  |   -moz-border-radius:10px; | ||||||
|  |   border-radius:10px; | ||||||
|  |   color:#000; | ||||||
|  |   border:2px solid #E0E0E0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .triangle-right.top:after { | ||||||
|  |   border-color: transparent #E0E0E0; | ||||||
|  |   border-width: 20px 20px 0 0; | ||||||
|  |   bottom: auto; | ||||||
|  |   left: auto; | ||||||
|  |   right: 50px; | ||||||
|  |   top: -20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .triangle-right:after { | ||||||
|  |   content:""; | ||||||
|  |   position:absolute; | ||||||
|  |   bottom:-20px; /* value = - border-top-width - border-bottom-width */ | ||||||
|  |   left:50px; /* controls horizontal position */ | ||||||
|  |   border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */ | ||||||
|  |   border-style:solid; | ||||||
|  |   border-color:#E0E0E0 transparent; | ||||||
|  |   /* reduce the damage in FF3.0 */ | ||||||
|  |   display:block; | ||||||
|  |   width:0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .triangle-right.top:after { | ||||||
|  |   top:-40px; /* value = - border-top-width - border-bottom-width */ | ||||||
|  |   right:50px; /* controls horizontal position */ | ||||||
|  |   bottom:auto; | ||||||
|  |   left:auto; | ||||||
|  |   border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */ | ||||||
|  |   border-color:transparent #E0E0E0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*Default noVNC logo.*/ | ||||||
|  | /* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ | ||||||
|  | @font-face { | ||||||
|  |   font-family: 'Orbitron'; | ||||||
|  |   font-style: normal; | ||||||
|  |   font-weight: 700; | ||||||
|  |   src: local('?'), url('Orbitron700.woff') format('woff'), | ||||||
|  |                    url('Orbitron700.ttf') format('truetype'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_logo { | ||||||
|  |   margin-top: 170px; | ||||||
|  |   margin-left: 10px; | ||||||
|  |   color:yellow; | ||||||
|  |   text-align:left; | ||||||
|  |   font-family: 'Orbitron', 'OrbitronTTF', sans-serif; | ||||||
|  |   line-height:90%; | ||||||
|  |   text-shadow: | ||||||
|  |        5px 5px 0 #000, | ||||||
|  |       -1px -1px 0 #000, | ||||||
|  |        1px -1px 0 #000, | ||||||
|  |       -1px 1px 0 #000, | ||||||
|  |        1px 1px 0 #000; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #noVNC_logo span{ | ||||||
|  |   color:green; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* ---------------------------------------- | ||||||
|  |  * Media sizing | ||||||
|  |  * ---------------------------------------- | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | .noVNC_status_button { | ||||||
|  |   font-size: 12px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_clipboard_text { | ||||||
|  |   width: 500px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_logo { | ||||||
|  |   font-size: 180px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_buttons_left { | ||||||
|  |   padding-left: 10px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_buttons_right { | ||||||
|  |   padding-right: 10px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_status { | ||||||
|  |   z-index: 0; | ||||||
|  |   position: absolute; | ||||||
|  |   width: 100%; | ||||||
|  |   margin-left: 0px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_toggleExtraKeys_button { display: none; } | ||||||
|  | #noVNC_toggleCtrl_button { display: inline; } | ||||||
|  | #noVNC_toggleAlt_button {  display: inline; } | ||||||
|  | #noVNC_sendTab_button { display: inline; } | ||||||
|  | #noVNC_sendEsc_button { display: inline; } | ||||||
|  |  | ||||||
|  | /* left-align the status text on lower resolutions */ | ||||||
|  | @media screen and (max-width: 800px){ | ||||||
|  |   #noVNC_status { | ||||||
|  |     z-index: 1; | ||||||
|  |     position: relative; | ||||||
|  |     width: auto; | ||||||
|  |     float: left; | ||||||
|  |     margin-left: 4px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media screen and (max-width: 640px){ | ||||||
|  |   #noVNC_clipboard_text { | ||||||
|  |     width: 410px; | ||||||
|  |   } | ||||||
|  |   #noVNC_logo { | ||||||
|  |     font-size: 150px; | ||||||
|  |   } | ||||||
|  |   .noVNC_status_button { | ||||||
|  |     font-size: 10px; | ||||||
|  |   } | ||||||
|  |   .noVNC_buttons_left { | ||||||
|  |     padding-left: 0px; | ||||||
|  |   } | ||||||
|  |   .noVNC_buttons_right { | ||||||
|  |     padding-right: 0px; | ||||||
|  |   } | ||||||
|  |   /* collapse the extra keys on lower resolutions */ | ||||||
|  |   #noVNC_toggleExtraKeys_button { | ||||||
|  |     display: inline; | ||||||
|  |   } | ||||||
|  |   #noVNC_toggleCtrl_button { | ||||||
|  |     display: none; | ||||||
|  |     position: absolute; | ||||||
|  |     top: 30px; | ||||||
|  |     left: 0px; | ||||||
|  |   } | ||||||
|  |   #noVNC_toggleAlt_button { | ||||||
|  |     display: none; | ||||||
|  |     position: absolute; | ||||||
|  |     top: 65px; | ||||||
|  |     left: 0px; | ||||||
|  |   } | ||||||
|  |   #noVNC_sendTab_button { | ||||||
|  |     display: none; | ||||||
|  |     position: absolute; | ||||||
|  |     top: 100px; | ||||||
|  |     left: 0px; | ||||||
|  |   } | ||||||
|  |   #noVNC_sendEsc_button { | ||||||
|  |     display: none; | ||||||
|  |     position: absolute; | ||||||
|  |     top: 135px; | ||||||
|  |     left: 0px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media screen and (min-width: 321px) and (max-width: 480px) { | ||||||
|  |   #noVNC_clipboard_text { | ||||||
|  |     width: 250px; | ||||||
|  |   } | ||||||
|  |   #noVNC_logo { | ||||||
|  |     font-size: 110px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media screen and (max-width: 320px) { | ||||||
|  |   .noVNC_status_button { | ||||||
|  |     font-size: 9px; | ||||||
|  |   } | ||||||
|  |   #noVNC_clipboard_text { | ||||||
|  |     width: 220px; | ||||||
|  |   } | ||||||
|  |   #noVNC_logo { | ||||||
|  |     font-size: 90px; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										113
									
								
								public/novnc/include/base64.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,113 @@ | |||||||
|  | /* This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||||
|  |  | ||||||
|  | // From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js | ||||||
|  |  | ||||||
|  | /*jslint white: false */ | ||||||
|  | /*global console */ | ||||||
|  |  | ||||||
|  | var Base64 = { | ||||||
|  |     /* Convert data (an array of integers) to a Base64 string. */ | ||||||
|  |     toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''), | ||||||
|  |     base64Pad     : '=', | ||||||
|  |  | ||||||
|  |     encode: function (data) { | ||||||
|  |         "use strict"; | ||||||
|  |         var result = ''; | ||||||
|  |         var toBase64Table = Base64.toBase64Table; | ||||||
|  |         var length = data.length; | ||||||
|  |         var lengthpad = (length % 3); | ||||||
|  |         // Convert every three bytes to 4 ascii characters. | ||||||
|  |  | ||||||
|  |         for (var i = 0; i < (length - 2); i += 3) { | ||||||
|  |             result += toBase64Table[data[i] >> 2]; | ||||||
|  |             result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; | ||||||
|  |             result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)]; | ||||||
|  |             result += toBase64Table[data[i + 2] & 0x3f]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Convert the remaining 1 or 2 bytes, pad out to 4 characters. | ||||||
|  |         var j = 0; | ||||||
|  |         if (lengthpad === 2) { | ||||||
|  |             j = length - lengthpad; | ||||||
|  |             result += toBase64Table[data[j] >> 2]; | ||||||
|  |             result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)]; | ||||||
|  |             result += toBase64Table[(data[j + 1] & 0x0f) << 2]; | ||||||
|  |             result += toBase64Table[64]; | ||||||
|  |         } else if (lengthpad === 1) { | ||||||
|  |             j = length - lengthpad; | ||||||
|  |             result += toBase64Table[data[j] >> 2]; | ||||||
|  |             result += toBase64Table[(data[j] & 0x03) << 4]; | ||||||
|  |             result += toBase64Table[64]; | ||||||
|  |             result += toBase64Table[64]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result; | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     /* Convert Base64 data to a string */ | ||||||
|  |     /* jshint -W013 */ | ||||||
|  |     toBinaryTable : [ | ||||||
|  |         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, | ||||||
|  |         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, | ||||||
|  |         -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, | ||||||
|  |         52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, | ||||||
|  |         -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14, | ||||||
|  |         15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, | ||||||
|  |         -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, | ||||||
|  |         41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 | ||||||
|  |     ], | ||||||
|  |     /* jshint +W013 */ | ||||||
|  |  | ||||||
|  |     decode: function (data, offset) { | ||||||
|  |         "use strict"; | ||||||
|  |         offset = typeof(offset) !== 'undefined' ? offset : 0; | ||||||
|  |         var toBinaryTable = Base64.toBinaryTable; | ||||||
|  |         var base64Pad = Base64.base64Pad; | ||||||
|  |         var result, result_length; | ||||||
|  |         var leftbits = 0; // number of bits decoded, but yet to be appended | ||||||
|  |         var leftdata = 0; // bits decoded, but yet to be appended | ||||||
|  |         var data_length = data.indexOf('=') - offset; | ||||||
|  |  | ||||||
|  |         if (data_length < 0) { data_length = data.length - offset; } | ||||||
|  |  | ||||||
|  |         /* Every four characters is 3 resulting numbers */ | ||||||
|  |         result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5); | ||||||
|  |         result = new Array(result_length); | ||||||
|  |  | ||||||
|  |         // Convert one by one. | ||||||
|  |         for (var idx = 0, i = offset; i < data.length; i++) { | ||||||
|  |             var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; | ||||||
|  |             var padding = (data.charAt(i) === base64Pad); | ||||||
|  |             // Skip illegal characters and whitespace | ||||||
|  |             if (c === -1) { | ||||||
|  |                 console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |            | ||||||
|  |             // Collect data into leftdata, update bitcount | ||||||
|  |             leftdata = (leftdata << 6) | c; | ||||||
|  |             leftbits += 6; | ||||||
|  |  | ||||||
|  |             // If we have 8 or more bits, append 8 bits to the result | ||||||
|  |             if (leftbits >= 8) { | ||||||
|  |                 leftbits -= 8; | ||||||
|  |                 // Append if not padding. | ||||||
|  |                 if (!padding) { | ||||||
|  |                     result[idx++] = (leftdata >> leftbits) & 0xff; | ||||||
|  |                 } | ||||||
|  |                 leftdata &= (1 << leftbits) - 1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // If there are any bits left, the base64 string was corrupted | ||||||
|  |         if (leftbits) { | ||||||
|  |             err = new Error('Corrupted base64 string'); | ||||||
|  |             err.name = 'Base64-Error'; | ||||||
|  |             throw err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | }; /* End of Base64 namespace */ | ||||||
							
								
								
									
										71
									
								
								public/novnc/include/black.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,71 @@ | |||||||
|  | /* | ||||||
|  |  * noVNC black CSS | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Copyright (C) 2013 Samuel Mannehed for Cendio AB | ||||||
|  |  * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) | ||||||
|  |  * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #noVNC_keyboardinput { | ||||||
|  |   background-color:#000; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_status_normal { | ||||||
|  |   background: #4c4c4c; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ | ||||||
|  |   background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ | ||||||
|  | } | ||||||
|  | .noVNC_status_error { | ||||||
|  |   background: #f04040; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #f04040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ | ||||||
|  |   background: linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ | ||||||
|  | } | ||||||
|  | .noVNC_status_warn { | ||||||
|  |   background: #f0f040; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #f0f040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ | ||||||
|  |   background: linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .triangle-right { | ||||||
|  |   border:2px solid #fff; | ||||||
|  |   background:#000; | ||||||
|  |   color:#fff; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_status_button { | ||||||
|  |   font-size: 12px; | ||||||
|  |   vertical-align: middle; | ||||||
|  |   border:1px solid #4c4c4c; | ||||||
|  |  | ||||||
|  |   background: #4c4c4c; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ | ||||||
|  |   filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */ | ||||||
|  |   background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .noVNC_status_button_selected { | ||||||
|  |   background: #9dd53a; /* Old browsers */ | ||||||
|  |   background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */ | ||||||
|  |   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */ | ||||||
|  |   background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */ | ||||||
|  |   background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */ | ||||||
|  |   background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */ | ||||||
|  |   filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */ | ||||||
|  |   background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */ | ||||||
|  | } | ||||||
							
								
								
									
										64
									
								
								public/novnc/include/blue.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,64 @@ | |||||||
|  | /* | ||||||
|  |  * noVNC blue CSS | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Copyright (C) 2013 Samuel Mannehed for Cendio AB | ||||||
|  |  * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) | ||||||
|  |  * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | .noVNC_status_normal { | ||||||
|  |   background-color:#04073d; | ||||||
|  |   background-image: -webkit-gradient( | ||||||
|  |     linear, | ||||||
|  |     left bottom, | ||||||
|  |     left top, | ||||||
|  |     color-stop(0.54, rgb(10,15,79)), | ||||||
|  |     color-stop(0.5, rgb(4,7,61)) | ||||||
|  |   ); | ||||||
|  |   background-image: -moz-linear-gradient( | ||||||
|  |     center bottom, | ||||||
|  |     rgb(10,15,79) 54%, | ||||||
|  |     rgb(4,7,61) 50% | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | .noVNC_status_error { | ||||||
|  |   background-color:#f04040; | ||||||
|  |   background-image: -webkit-gradient( | ||||||
|  |     linear, | ||||||
|  |     left bottom, | ||||||
|  |     left top, | ||||||
|  |     color-stop(0.54, rgb(240,64,64)), | ||||||
|  |     color-stop(0.5, rgb(4,7,61)) | ||||||
|  |   ); | ||||||
|  |   background-image: -moz-linear-gradient( | ||||||
|  |     center bottom, | ||||||
|  |     rgb(4,7,61) 54%, | ||||||
|  |     rgb(249,64,64) 50% | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | .noVNC_status_warn { | ||||||
|  |   background-color:#f0f040; | ||||||
|  |   background-image: -webkit-gradient( | ||||||
|  |     linear, | ||||||
|  |     left bottom, | ||||||
|  |     left top, | ||||||
|  |     color-stop(0.54, rgb(240,240,64)), | ||||||
|  |     color-stop(0.5, rgb(4,7,61)) | ||||||
|  |   ); | ||||||
|  |   background-image: -moz-linear-gradient( | ||||||
|  |     center bottom, | ||||||
|  |     rgb(4,7,61) 54%, | ||||||
|  |     rgb(240,240,64) 50% | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .triangle-right { | ||||||
|  |   border:2px solid #fff; | ||||||
|  |   background:#04073d; | ||||||
|  |   color:#fff; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #noVNC_keyboardinput { | ||||||
|  |   background-color:#04073d; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										321
									
								
								public/novnc/include/chrome-app/tcp-client.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,321 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2012 Google Inc. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  |  | ||||||
|  | Author: Boris Smus (smus@chromium.org) | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | (function(exports) { | ||||||
|  |  | ||||||
|  |   // Define some local variables here. | ||||||
|  |   var socket = chrome.socket || chrome.experimental.socket; | ||||||
|  |   var dns = chrome.experimental.dns; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Creates an instance of the client | ||||||
|  |    * | ||||||
|  |    * @param {String} host The remote host to connect to | ||||||
|  |    * @param {Number} port The port to connect to at the remote host | ||||||
|  |    */ | ||||||
|  |   function TcpClient(host, port, pollInterval) { | ||||||
|  |     this.host = host; | ||||||
|  |     this.port = port; | ||||||
|  |     this.pollInterval = pollInterval || 15; | ||||||
|  |  | ||||||
|  |     // Callback functions. | ||||||
|  |     this.callbacks = { | ||||||
|  |       connect: null,    // Called when socket is connected. | ||||||
|  |       disconnect: null, // Called when socket is disconnected. | ||||||
|  |       recvBuffer: null, // Called (as ArrayBuffer) when client receives data from server. | ||||||
|  |       recvString: null, // Called (as string) when client receives data from server. | ||||||
|  |       sent: null        // Called when client sends data to server. | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Socket. | ||||||
|  |     this.socketId = null; | ||||||
|  |     this.isConnected = false; | ||||||
|  |  | ||||||
|  |     log('initialized tcp client'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Connects to the TCP socket, and creates an open socket. | ||||||
|  |    * | ||||||
|  |    * @see http://developer.chrome.com/trunk/apps/socket.html#method-create | ||||||
|  |    * @param {Function} callback The function to call on connection | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype.connect = function(callback) { | ||||||
|  |     // First resolve the hostname to an IP. | ||||||
|  |     dns.resolve(this.host, function(result) { | ||||||
|  |       this.addr = result.address; | ||||||
|  |       socket.create('tcp', {}, this._onCreate.bind(this)); | ||||||
|  |  | ||||||
|  |       // Register connect callback. | ||||||
|  |       this.callbacks.connect = callback; | ||||||
|  |     }.bind(this)); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Sends an arraybuffer/view down the wire to the remote side | ||||||
|  |    * | ||||||
|  |    * @see http://developer.chrome.com/trunk/apps/socket.html#method-write | ||||||
|  |    * @param {String} msg The arraybuffer/view to send | ||||||
|  |    * @param {Function} callback The function to call when the message has sent | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype.sendBuffer = function(buf, callback) { | ||||||
|  |     if (buf.buffer) { | ||||||
|  |         buf = buf.buffer; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |     // Debug | ||||||
|  |     var bytes = [], u8 = new Uint8Array(buf); | ||||||
|  |     for (var i = 0; i < u8.length; i++) { | ||||||
|  |         bytes.push(u8[i]); | ||||||
|  |     } | ||||||
|  |     log("sending bytes: " + (bytes.join(','))); | ||||||
|  |     */ | ||||||
|  |      | ||||||
|  |     socket.write(this.socketId, buf, this._onWriteComplete.bind(this)); | ||||||
|  |  | ||||||
|  |     // Register sent callback. | ||||||
|  |     this.callbacks.sent = callback; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Sends a string down the wire to the remote side | ||||||
|  |    * | ||||||
|  |    * @see http://developer.chrome.com/trunk/apps/socket.html#method-write | ||||||
|  |    * @param {String} msg The string to send | ||||||
|  |    * @param {Function} callback The function to call when the message has sent | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype.sendString = function(msg, callback) { | ||||||
|  |     /* | ||||||
|  |     // Debug | ||||||
|  |     log("sending string: " + msg); | ||||||
|  |     */ | ||||||
|  |  | ||||||
|  |     this._stringToArrayBuffer(msg, function(arrayBuffer) { | ||||||
|  |       socket.write(this.socketId, arrayBuffer, this._onWriteComplete.bind(this)); | ||||||
|  |     }.bind(this)); | ||||||
|  |  | ||||||
|  |     // Register sent callback. | ||||||
|  |     this.callbacks.sent = callback; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Sets the callback for when a message is received | ||||||
|  |    * | ||||||
|  |    * @param {Function} callback The function to call when a message has arrived | ||||||
|  |    * @param {String} type The callback argument type: "arraybuffer" or "string" | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype.addResponseListener = function(callback, type) { | ||||||
|  |     if (typeof type === "undefined") { | ||||||
|  |         type = "arraybuffer"; | ||||||
|  |     } | ||||||
|  |     // Register received callback. | ||||||
|  |     if (type === "string") { | ||||||
|  |       this.callbacks.recvString = callback; | ||||||
|  |     } else { | ||||||
|  |       this.callbacks.recvBuffer = callback; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Sets the callback for when the socket disconnects | ||||||
|  |    * | ||||||
|  |    * @param {Function} callback The function to call when the socket disconnects | ||||||
|  |    * @param {String} type The callback argument type: "arraybuffer" or "string" | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype.addDisconnectListener = function(callback) { | ||||||
|  |     // Register disconnect callback. | ||||||
|  |     this.callbacks.disconnect = callback; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Disconnects from the remote side | ||||||
|  |    * | ||||||
|  |    * @see http://developer.chrome.com/trunk/apps/socket.html#method-disconnect | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype.disconnect = function() { | ||||||
|  |     if (this.isConnected) { | ||||||
|  |       this.isConnected = false; | ||||||
|  |       socket.disconnect(this.socketId); | ||||||
|  |       if (this.callbacks.disconnect) { | ||||||
|  |         this.callbacks.disconnect(); | ||||||
|  |       } | ||||||
|  |       log('socket disconnected'); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * The callback function used for when we attempt to have Chrome | ||||||
|  |    * create a socket. If the socket is successfully created | ||||||
|  |    * we go ahead and connect to the remote side. | ||||||
|  |    * | ||||||
|  |    * @private | ||||||
|  |    * @see http://developer.chrome.com/trunk/apps/socket.html#method-connect | ||||||
|  |    * @param {Object} createInfo The socket details | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype._onCreate = function(createInfo) { | ||||||
|  |     this.socketId = createInfo.socketId; | ||||||
|  |     if (this.socketId > 0) { | ||||||
|  |       socket.connect(this.socketId, this.addr, this.port, this._onConnectComplete.bind(this)); | ||||||
|  |     } else { | ||||||
|  |       error('Unable to create socket'); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * The callback function used for when we attempt to have Chrome | ||||||
|  |    * connect to the remote side. If a successful connection is | ||||||
|  |    * made then polling starts to check for data to read | ||||||
|  |    * | ||||||
|  |    * @private | ||||||
|  |    * @param {Number} resultCode Indicates whether the connection was successful | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype._onConnectComplete = function(resultCode) { | ||||||
|  |     // Start polling for reads. | ||||||
|  |     this.isConnected = true; | ||||||
|  |     setTimeout(this._periodicallyRead.bind(this), this.pollInterval); | ||||||
|  |  | ||||||
|  |     if (this.callbacks.connect) { | ||||||
|  |       log('connect complete'); | ||||||
|  |       this.callbacks.connect(); | ||||||
|  |     } | ||||||
|  |     log('onConnectComplete'); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Checks for new data to read from the socket | ||||||
|  |    * | ||||||
|  |    * @see http://developer.chrome.com/trunk/apps/socket.html#method-read | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype._periodicallyRead = function() { | ||||||
|  |     var that = this; | ||||||
|  |     socket.getInfo(this.socketId, function (info) { | ||||||
|  |       if (info.connected) { | ||||||
|  |         setTimeout(that._periodicallyRead.bind(that), that.pollInterval); | ||||||
|  |         socket.read(that.socketId, null, that._onDataRead.bind(that)); | ||||||
|  |       } else if (that.isConnected) { | ||||||
|  |         log('socket disconnect detected'); | ||||||
|  |         that.disconnect(); | ||||||
|  |       } | ||||||
|  |    }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Callback function for when data has been read from the socket. | ||||||
|  |    * Converts the array buffer that is read in to a string | ||||||
|  |    * and sends it on for further processing by passing it to | ||||||
|  |    * the previously assigned callback function. | ||||||
|  |    * | ||||||
|  |    * @private | ||||||
|  |    * @see TcpClient.prototype.addResponseListener | ||||||
|  |    * @param {Object} readInfo The incoming message | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype._onDataRead = function(readInfo) { | ||||||
|  |     // Call received callback if there's data in the response. | ||||||
|  |     if (readInfo.resultCode > 0) { | ||||||
|  |       log('onDataRead'); | ||||||
|  |  | ||||||
|  |       /* | ||||||
|  |       // Debug | ||||||
|  |       var bytes = [], u8 = new Uint8Array(readInfo.data); | ||||||
|  |       for (var i = 0; i < u8.length; i++) { | ||||||
|  |           bytes.push(u8[i]); | ||||||
|  |       } | ||||||
|  |       log("received bytes: " + (bytes.join(','))); | ||||||
|  |       */ | ||||||
|  |        | ||||||
|  |       if (this.callbacks.recvBuffer) { | ||||||
|  |         // Return raw ArrayBuffer directly. | ||||||
|  |         this.callbacks.recvBuffer(readInfo.data); | ||||||
|  |       } | ||||||
|  |       if (this.callbacks.recvString) { | ||||||
|  |         // Convert ArrayBuffer to string. | ||||||
|  |         this._arrayBufferToString(readInfo.data, function(str) { | ||||||
|  |           this.callbacks.recvString(str); | ||||||
|  |         }.bind(this)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Trigger another read right away | ||||||
|  |       setTimeout(this._periodicallyRead.bind(this), 0); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Callback for when data has been successfully | ||||||
|  |    * written to the socket. | ||||||
|  |    * | ||||||
|  |    * @private | ||||||
|  |    * @param {Object} writeInfo The outgoing message | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype._onWriteComplete = function(writeInfo) { | ||||||
|  |     log('onWriteComplete'); | ||||||
|  |     // Call sent callback. | ||||||
|  |     if (this.callbacks.sent) { | ||||||
|  |       this.callbacks.sent(writeInfo); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Converts an array buffer to a string | ||||||
|  |    * | ||||||
|  |    * @private | ||||||
|  |    * @param {ArrayBuffer} buf The buffer to convert | ||||||
|  |    * @param {Function} callback The function to call when conversion is complete | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype._arrayBufferToString = function(buf, callback) { | ||||||
|  |     var bb = new Blob([new Uint8Array(buf)]); | ||||||
|  |     var f = new FileReader(); | ||||||
|  |     f.onload = function(e) { | ||||||
|  |       callback(e.target.result); | ||||||
|  |     }; | ||||||
|  |     f.readAsText(bb); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Converts a string to an array buffer | ||||||
|  |    * | ||||||
|  |    * @private | ||||||
|  |    * @param {String} str The string to convert | ||||||
|  |    * @param {Function} callback The function to call when conversion is complete | ||||||
|  |    */ | ||||||
|  |   TcpClient.prototype._stringToArrayBuffer = function(str, callback) { | ||||||
|  |     var bb = new Blob([str]); | ||||||
|  |     var f = new FileReader(); | ||||||
|  |     f.onload = function(e) { | ||||||
|  |         callback(e.target.result); | ||||||
|  |     }; | ||||||
|  |     f.readAsArrayBuffer(bb); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Wrapper function for logging | ||||||
|  |    */ | ||||||
|  |   function log(msg) { | ||||||
|  |     console.log(msg); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Wrapper function for error logging | ||||||
|  |    */ | ||||||
|  |   function error(msg) { | ||||||
|  |     console.error(msg); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   exports.TcpClient = TcpClient; | ||||||
|  |  | ||||||
|  | })(window); | ||||||
							
								
								
									
										276
									
								
								public/novnc/include/des.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,276 @@ | |||||||
|  | /* | ||||||
|  |  * Ported from Flashlight VNC ActionScript implementation: | ||||||
|  |  *     http://www.wizhelp.com/flashlight-vnc/ | ||||||
|  |  * | ||||||
|  |  * Full attribution follows: | ||||||
|  |  * | ||||||
|  |  * ------------------------------------------------------------------------- | ||||||
|  |  * | ||||||
|  |  * This DES class has been extracted from package Acme.Crypto for use in VNC. | ||||||
|  |  * The unnecessary odd parity code has been removed. | ||||||
|  |  * | ||||||
|  |  * These changes are: | ||||||
|  |  *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * This software is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||||
|  |  * | ||||||
|  |  | ||||||
|  |  * DesCipher - the DES encryption method | ||||||
|  |  * | ||||||
|  |  * The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is: | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. | ||||||
|  |  * | ||||||
|  |  * Permission to use, copy, modify, and distribute this software | ||||||
|  |  * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and | ||||||
|  |  * without fee is hereby granted, provided that this copyright notice is kept  | ||||||
|  |  * intact.  | ||||||
|  |  *  | ||||||
|  |  * WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY | ||||||
|  |  * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED | ||||||
|  |  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | ||||||
|  |  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE | ||||||
|  |  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | ||||||
|  |  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. | ||||||
|  |  *  | ||||||
|  |  * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE | ||||||
|  |  * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE | ||||||
|  |  * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT | ||||||
|  |  * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE | ||||||
|  |  * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE | ||||||
|  |  * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE | ||||||
|  |  * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  WIDGET WORKSHOP | ||||||
|  |  * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR | ||||||
|  |  * HIGH RISK ACTIVITIES. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * The rest is: | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions | ||||||
|  |  * are met: | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright | ||||||
|  |  *    notice, this list of conditions and the following disclaimer. | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *    notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *    documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||||||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
|  |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
|  |  * SUCH DAMAGE. | ||||||
|  |  * | ||||||
|  |  * Visit the ACME Labs Java page for up-to-date versions of this and other | ||||||
|  |  * fine Java utilities: http://www.acme.com/java/ | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* jslint white: false */ | ||||||
|  |  | ||||||
|  | function DES(passwd) { | ||||||
|  |     "use strict"; | ||||||
|  |  | ||||||
|  |     // Tables, permutations, S-boxes, etc. | ||||||
|  |     // jshint -W013 | ||||||
|  |     var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, | ||||||
|  |                25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, | ||||||
|  |                50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], | ||||||
|  |         totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28], | ||||||
|  |         z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8, | ||||||
|  |         keys = []; | ||||||
|  |  | ||||||
|  |     // jshint -W015 | ||||||
|  |     a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; | ||||||
|  |     SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d, | ||||||
|  |            z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z, | ||||||
|  |            a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f, | ||||||
|  |            c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d]; | ||||||
|  |     a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e; | ||||||
|  |     SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d, | ||||||
|  |            a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f, | ||||||
|  |            z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z, | ||||||
|  |            z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e]; | ||||||
|  |     a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e; | ||||||
|  |     SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f, | ||||||
|  |            b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z, | ||||||
|  |            c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d, | ||||||
|  |            b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e]; | ||||||
|  |     a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e; | ||||||
|  |     SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d, | ||||||
|  |            z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f, | ||||||
|  |            b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e, | ||||||
|  |            c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e]; | ||||||
|  |     a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e; | ||||||
|  |     SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z, | ||||||
|  |            a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f, | ||||||
|  |            z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e, | ||||||
|  |            c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d]; | ||||||
|  |     a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e; | ||||||
|  |     SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f, | ||||||
|  |            z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z, | ||||||
|  |            b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z, | ||||||
|  |            a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f]; | ||||||
|  |     a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e; | ||||||
|  |     SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f, | ||||||
|  |            b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e, | ||||||
|  |            b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e, | ||||||
|  |            z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d]; | ||||||
|  |     a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e; | ||||||
|  |     SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d, | ||||||
|  |            c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z, | ||||||
|  |            a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f, | ||||||
|  |            z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e]; | ||||||
|  |     // jshint +W013,+W015 | ||||||
|  |  | ||||||
|  |     // Set the key. | ||||||
|  |     function setKeys(keyBlock) { | ||||||
|  |         var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [], | ||||||
|  |             raw0, raw1, rawi, KnLi; | ||||||
|  |  | ||||||
|  |         for (j = 0, l = 56; j < 56; ++j, l -= 8) { | ||||||
|  |             l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1 | ||||||
|  |             m = l & 0x7; | ||||||
|  |             pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (i = 0; i < 16; ++i) { | ||||||
|  |             m = i << 1; | ||||||
|  |             n = m + 1; | ||||||
|  |             kn[m] = kn[n] = 0; | ||||||
|  |             for (o = 28; o < 59; o += 28) { | ||||||
|  |                 for (j = o - 28; j < o; ++j) { | ||||||
|  |                     l = j + totrot[i]; | ||||||
|  |                     if (l < o) { | ||||||
|  |                         pcr[j] = pc1m[l]; | ||||||
|  |                     } else { | ||||||
|  |                         pcr[j] = pc1m[l - 28]; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             for (j = 0; j < 24; ++j) { | ||||||
|  |                 if (pcr[PC2[j]] !== 0) { | ||||||
|  |                     kn[m] |= 1 << (23 - j); | ||||||
|  |                 } | ||||||
|  |                 if (pcr[PC2[j + 24]] !== 0) { | ||||||
|  |                     kn[n] |= 1 << (23 - j); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // cookey | ||||||
|  |         for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) { | ||||||
|  |             raw0 = kn[rawi++]; | ||||||
|  |             raw1 = kn[rawi++]; | ||||||
|  |             keys[KnLi] = (raw0 & 0x00fc0000) << 6; | ||||||
|  |             keys[KnLi] |= (raw0 & 0x00000fc0) << 10; | ||||||
|  |             keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10; | ||||||
|  |             keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6; | ||||||
|  |             ++KnLi; | ||||||
|  |             keys[KnLi] = (raw0 & 0x0003f000) << 12; | ||||||
|  |             keys[KnLi] |= (raw0 & 0x0000003f) << 16; | ||||||
|  |             keys[KnLi] |= (raw1 & 0x0003f000) >>> 4; | ||||||
|  |             keys[KnLi] |= (raw1 & 0x0000003f); | ||||||
|  |             ++KnLi; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Encrypt 8 bytes of text | ||||||
|  |     function enc8(text) { | ||||||
|  |         var i = 0, b = text.slice(), fval, keysi = 0, | ||||||
|  |             l, r, x; // left, right, accumulator | ||||||
|  |  | ||||||
|  |         // Squash 8 bytes to 2 ints | ||||||
|  |         l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; | ||||||
|  |         r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; | ||||||
|  |  | ||||||
|  |         x = ((l >>> 4) ^ r) & 0x0f0f0f0f; | ||||||
|  |         r ^= x; | ||||||
|  |         l ^= (x << 4); | ||||||
|  |         x = ((l >>> 16) ^ r) & 0x0000ffff; | ||||||
|  |         r ^= x; | ||||||
|  |         l ^= (x << 16); | ||||||
|  |         x = ((r >>> 2) ^ l) & 0x33333333; | ||||||
|  |         l ^= x; | ||||||
|  |         r ^= (x << 2); | ||||||
|  |         x = ((r >>> 8) ^ l) & 0x00ff00ff; | ||||||
|  |         l ^= x; | ||||||
|  |         r ^= (x << 8); | ||||||
|  |         r = (r << 1) | ((r >>> 31) & 1); | ||||||
|  |         x = (l ^ r) & 0xaaaaaaaa; | ||||||
|  |         l ^= x; | ||||||
|  |         r ^= x; | ||||||
|  |         l = (l << 1) | ((l >>> 31) & 1); | ||||||
|  |  | ||||||
|  |         for (i = 0; i < 8; ++i) { | ||||||
|  |             x = (r << 28) | (r >>> 4); | ||||||
|  |             x ^= keys[keysi++]; | ||||||
|  |             fval =  SP7[x & 0x3f]; | ||||||
|  |             fval |= SP5[(x >>> 8) & 0x3f]; | ||||||
|  |             fval |= SP3[(x >>> 16) & 0x3f]; | ||||||
|  |             fval |= SP1[(x >>> 24) & 0x3f]; | ||||||
|  |             x = r ^ keys[keysi++]; | ||||||
|  |             fval |= SP8[x & 0x3f]; | ||||||
|  |             fval |= SP6[(x >>> 8) & 0x3f]; | ||||||
|  |             fval |= SP4[(x >>> 16) & 0x3f]; | ||||||
|  |             fval |= SP2[(x >>> 24) & 0x3f]; | ||||||
|  |             l ^= fval; | ||||||
|  |             x = (l << 28) | (l >>> 4); | ||||||
|  |             x ^= keys[keysi++]; | ||||||
|  |             fval =  SP7[x & 0x3f]; | ||||||
|  |             fval |= SP5[(x >>> 8) & 0x3f]; | ||||||
|  |             fval |= SP3[(x >>> 16) & 0x3f]; | ||||||
|  |             fval |= SP1[(x >>> 24) & 0x3f]; | ||||||
|  |             x = l ^ keys[keysi++]; | ||||||
|  |             fval |= SP8[x & 0x0000003f]; | ||||||
|  |             fval |= SP6[(x >>> 8) & 0x3f]; | ||||||
|  |             fval |= SP4[(x >>> 16) & 0x3f]; | ||||||
|  |             fval |= SP2[(x >>> 24) & 0x3f]; | ||||||
|  |             r ^= fval; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         r = (r << 31) | (r >>> 1); | ||||||
|  |         x = (l ^ r) & 0xaaaaaaaa; | ||||||
|  |         l ^= x; | ||||||
|  |         r ^= x; | ||||||
|  |         l = (l << 31) | (l >>> 1); | ||||||
|  |         x = ((l >>> 8) ^ r) & 0x00ff00ff; | ||||||
|  |         r ^= x; | ||||||
|  |         l ^= (x << 8); | ||||||
|  |         x = ((l >>> 2) ^ r) & 0x33333333; | ||||||
|  |         r ^= x; | ||||||
|  |         l ^= (x << 2); | ||||||
|  |         x = ((r >>> 16) ^ l) & 0x0000ffff; | ||||||
|  |         l ^= x; | ||||||
|  |         r ^= (x << 16); | ||||||
|  |         x = ((r >>> 4) ^ l) & 0x0f0f0f0f; | ||||||
|  |         l ^= x; | ||||||
|  |         r ^= (x << 4); | ||||||
|  |  | ||||||
|  |         // Spread ints to bytes | ||||||
|  |         x = [r, l]; | ||||||
|  |         for (i = 0; i < 8; i++) { | ||||||
|  |             b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256; | ||||||
|  |             if (b[i] < 0) { b[i] += 256; } // unsigned | ||||||
|  |         } | ||||||
|  |         return b; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Encrypt 16 bytes of text using passwd as key | ||||||
|  |     function encrypt(t) { | ||||||
|  |         return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     setKeys(passwd);             // Setup keys | ||||||
|  |     return {'encrypt': encrypt}; // Public interface | ||||||
|  |  | ||||||
|  | } // function DES | ||||||
							
								
								
									
										908
									
								
								public/novnc/include/display.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,908 @@ | |||||||
|  | /* | ||||||
|  |  * noVNC: HTML5 VNC client | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Copyright (C) 2015 Samuel Mannehed for Cendio AB | ||||||
|  |  * Licensed under MPL 2.0 (see LICENSE.txt) | ||||||
|  |  * | ||||||
|  |  * See README.md for usage and integration instructions. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /*jslint browser: true, white: false */ | ||||||
|  | /*global Util, Base64, changeCursor */ | ||||||
|  |  | ||||||
|  | var Display; | ||||||
|  |  | ||||||
|  | (function () { | ||||||
|  |     "use strict"; | ||||||
|  |  | ||||||
|  |     var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false; | ||||||
|  |     try { | ||||||
|  |         new ImageData(new Uint8ClampedArray(1), 1, 1); | ||||||
|  |         SUPPORTS_IMAGEDATA_CONSTRUCTOR = true; | ||||||
|  |     } catch (ex) { | ||||||
|  |         // ignore failure | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Display = function (defaults) { | ||||||
|  |         this._drawCtx = null; | ||||||
|  |         this._c_forceCanvas = false; | ||||||
|  |  | ||||||
|  |         this._renderQ = [];  // queue drawing actions for in-oder rendering | ||||||
|  |  | ||||||
|  |         // the full frame buffer (logical canvas) size | ||||||
|  |         this._fb_width = 0; | ||||||
|  |         this._fb_height = 0; | ||||||
|  |  | ||||||
|  |         // the size limit of the viewport (start disabled) | ||||||
|  |         this._maxWidth = 0; | ||||||
|  |         this._maxHeight = 0; | ||||||
|  |  | ||||||
|  |         // the visible "physical canvas" viewport | ||||||
|  |         this._viewportLoc = { 'x': 0, 'y': 0, 'w': 0, 'h': 0 }; | ||||||
|  |         this._cleanRect = { 'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1 }; | ||||||
|  |  | ||||||
|  |         this._prevDrawStyle = ""; | ||||||
|  |         this._tile = null; | ||||||
|  |         this._tile16x16 = null; | ||||||
|  |         this._tile_x = 0; | ||||||
|  |         this._tile_y = 0; | ||||||
|  |  | ||||||
|  |         Util.set_defaults(this, defaults, { | ||||||
|  |             'true_color': true, | ||||||
|  |             'colourMap': [], | ||||||
|  |             'scale': 1.0, | ||||||
|  |             'viewport': false, | ||||||
|  |             'render_mode': '' | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         Util.Debug(">> Display.constructor"); | ||||||
|  |  | ||||||
|  |         if (!this._target) { | ||||||
|  |             throw new Error("Target must be set"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (typeof this._target === 'string') { | ||||||
|  |             throw new Error('target must be a DOM element'); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this._target.getContext) { | ||||||
|  |             throw new Error("no getContext method"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this._drawCtx) { | ||||||
|  |             this._drawCtx = this._target.getContext('2d'); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Util.Debug("User Agent: " + navigator.userAgent); | ||||||
|  |         if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); } | ||||||
|  |         if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); } | ||||||
|  |         if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); } | ||||||
|  |         if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); } | ||||||
|  |  | ||||||
|  |         this.clear(); | ||||||
|  |  | ||||||
|  |         // Check canvas features | ||||||
|  |         if ('createImageData' in this._drawCtx) { | ||||||
|  |             this._render_mode = 'canvas rendering'; | ||||||
|  |         } else { | ||||||
|  |             throw new Error("Canvas does not support createImageData"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this._prefer_js === null) { | ||||||
|  |             Util.Info("Prefering javascript operations"); | ||||||
|  |             this._prefer_js = true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Determine browser support for setting the cursor via data URI scheme | ||||||
|  |         if (this._cursor_uri || this._cursor_uri === null || | ||||||
|  |                 this._cursor_uri === undefined) { | ||||||
|  |             this._cursor_uri = Util.browserSupportsCursorURIs(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Util.Debug("<< Display.constructor"); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     Display.prototype = { | ||||||
|  |         // Public methods | ||||||
|  |         viewportChangePos: function (deltaX, deltaY) { | ||||||
|  |             var vp = this._viewportLoc; | ||||||
|  |             deltaX = Math.floor(deltaX); | ||||||
|  |             deltaY = Math.floor(deltaY); | ||||||
|  |  | ||||||
|  |             if (!this._viewport) { | ||||||
|  |                 deltaX = -vp.w;  // clamped later of out of bounds | ||||||
|  |                 deltaY = -vp.h; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var vx2 = vp.x + vp.w - 1; | ||||||
|  |             var vy2 = vp.y + vp.h - 1; | ||||||
|  |  | ||||||
|  |             // Position change | ||||||
|  |  | ||||||
|  |             if (deltaX < 0 && vp.x + deltaX < 0) { | ||||||
|  |                 deltaX = -vp.x; | ||||||
|  |             } | ||||||
|  |             if (vx2 + deltaX >= this._fb_width) { | ||||||
|  |                 deltaX -= vx2 + deltaX - this._fb_width + 1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (vp.y + deltaY < 0) { | ||||||
|  |                 deltaY = -vp.y; | ||||||
|  |             } | ||||||
|  |             if (vy2 + deltaY >= this._fb_height) { | ||||||
|  |                 deltaY -= (vy2 + deltaY - this._fb_height + 1); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (deltaX === 0 && deltaY === 0) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); | ||||||
|  |  | ||||||
|  |             vp.x += deltaX; | ||||||
|  |             vx2 += deltaX; | ||||||
|  |             vp.y += deltaY; | ||||||
|  |             vy2 += deltaY; | ||||||
|  |  | ||||||
|  |             // Update the clean rectangle | ||||||
|  |             var cr = this._cleanRect; | ||||||
|  |             if (vp.x > cr.x1) { | ||||||
|  |                 cr.x1 = vp.x; | ||||||
|  |             } | ||||||
|  |             if (vx2 < cr.x2) { | ||||||
|  |                 cr.x2 = vx2; | ||||||
|  |             } | ||||||
|  |             if (vp.y > cr.y1) { | ||||||
|  |                 cr.y1 = vp.y; | ||||||
|  |             } | ||||||
|  |             if (vy2 < cr.y2) { | ||||||
|  |                 cr.y2 = vy2; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var x1, w; | ||||||
|  |             if (deltaX < 0) { | ||||||
|  |                 // Shift viewport left, redraw left section | ||||||
|  |                 x1 = 0; | ||||||
|  |                 w = -deltaX; | ||||||
|  |             } else { | ||||||
|  |                 // Shift viewport right, redraw right section | ||||||
|  |                 x1 = vp.w - deltaX; | ||||||
|  |                 w = deltaX; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var y1, h; | ||||||
|  |             if (deltaY < 0) { | ||||||
|  |                 // Shift viewport up, redraw top section | ||||||
|  |                 y1 = 0; | ||||||
|  |                 h = -deltaY; | ||||||
|  |             } else { | ||||||
|  |                 // Shift viewport down, redraw bottom section | ||||||
|  |                 y1 = vp.h - deltaY; | ||||||
|  |                 h = deltaY; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var saveStyle = this._drawCtx.fillStyle; | ||||||
|  |             var canvas = this._target; | ||||||
|  |             this._drawCtx.fillStyle = "rgb(255,255,255)"; | ||||||
|  |  | ||||||
|  |             // Due to this bug among others [1] we need to disable the image-smoothing to | ||||||
|  |             // avoid getting a blur effect when panning. | ||||||
|  |             // | ||||||
|  |             // 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719 | ||||||
|  |             // | ||||||
|  |             // We need to set these every time since all properties are reset | ||||||
|  |             // when the the size is changed | ||||||
|  |             if (this._drawCtx.mozImageSmoothingEnabled) { | ||||||
|  |                 this._drawCtx.mozImageSmoothingEnabled = false; | ||||||
|  |             } else if (this._drawCtx.webkitImageSmoothingEnabled) { | ||||||
|  |                 this._drawCtx.webkitImageSmoothingEnabled = false; | ||||||
|  |             } else if (this._drawCtx.msImageSmoothingEnabled) { | ||||||
|  |                 this._drawCtx.msImageSmoothingEnabled = false; | ||||||
|  |             } else if (this._drawCtx.imageSmoothingEnabled) { | ||||||
|  |                 this._drawCtx.imageSmoothingEnabled = false; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Copy the valid part of the viewport to the shifted location | ||||||
|  |             this._drawCtx.drawImage(canvas, 0, 0, vp.w, vp.h, -deltaX, -deltaY, vp.w, vp.h); | ||||||
|  |  | ||||||
|  |             if (deltaX !== 0) { | ||||||
|  |                 this._drawCtx.fillRect(x1, 0, w, vp.h); | ||||||
|  |             } | ||||||
|  |             if (deltaY !== 0) { | ||||||
|  |                 this._drawCtx.fillRect(0, y1, vp.w, h); | ||||||
|  |             } | ||||||
|  |             this._drawCtx.fillStyle = saveStyle; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         viewportChangeSize: function(width, height) { | ||||||
|  |  | ||||||
|  |             if (typeof(width) === "undefined" || typeof(height) === "undefined") { | ||||||
|  |  | ||||||
|  |                 Util.Debug("Setting viewport to full display region"); | ||||||
|  |                 width = this._fb_width; | ||||||
|  |                 height = this._fb_height; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var vp = this._viewportLoc; | ||||||
|  |             if (vp.w !== width || vp.h !== height) { | ||||||
|  |  | ||||||
|  |                 if (this._viewport) { | ||||||
|  |                     if (this._maxWidth !== 0 && width > this._maxWidth) { | ||||||
|  |                         width = this._maxWidth; | ||||||
|  |                     } | ||||||
|  |                     if (this._maxHeight !== 0 && height > this._maxHeight) { | ||||||
|  |                         height = this._maxHeight; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 var cr = this._cleanRect; | ||||||
|  |  | ||||||
|  |                 if (width < vp.w &&  cr.x2 > vp.x + width - 1) { | ||||||
|  |                     cr.x2 = vp.x + width - 1; | ||||||
|  |                 } | ||||||
|  |                 if (height < vp.h &&  cr.y2 > vp.y + height - 1) { | ||||||
|  |                     cr.y2 = vp.y + height - 1; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 vp.w = width; | ||||||
|  |                 vp.h = height; | ||||||
|  |  | ||||||
|  |                 var canvas = this._target; | ||||||
|  |                 if (canvas.width !== width || canvas.height !== height) { | ||||||
|  |  | ||||||
|  |                     // We have to save the canvas data since changing the size will clear it | ||||||
|  |                     var saveImg = null; | ||||||
|  |                     if (vp.w > 0 && vp.h > 0 && canvas.width > 0 && canvas.height > 0) { | ||||||
|  |                         var img_width = canvas.width < vp.w ? canvas.width : vp.w; | ||||||
|  |                         var img_height = canvas.height < vp.h ? canvas.height : vp.h; | ||||||
|  |                         saveImg = this._drawCtx.getImageData(0, 0, img_width, img_height); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (canvas.width !== width) { | ||||||
|  |                         canvas.width = width; | ||||||
|  |                         canvas.style.width = width + 'px'; | ||||||
|  |                     } | ||||||
|  |                     if (canvas.height !== height) { | ||||||
|  |                         canvas.height = height; | ||||||
|  |                         canvas.style.height = height + 'px'; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (saveImg) { | ||||||
|  |                         this._drawCtx.putImageData(saveImg, 0, 0); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // Return a map of clean and dirty areas of the viewport and reset the | ||||||
|  |         // tracking of clean and dirty areas | ||||||
|  |         // | ||||||
|  |         // Returns: { 'cleanBox': { 'x': x, 'y': y, 'w': w, 'h': h}, | ||||||
|  |         //            'dirtyBoxes': [{ 'x': x, 'y': y, 'w': w, 'h': h }, ...] } | ||||||
|  |         getCleanDirtyReset: function () { | ||||||
|  |             var vp = this._viewportLoc; | ||||||
|  |             var cr = this._cleanRect; | ||||||
|  |  | ||||||
|  |             var cleanBox = { 'x': cr.x1, 'y': cr.y1, | ||||||
|  |                              'w': cr.x2 - cr.x1 + 1, 'h': cr.y2 - cr.y1 + 1 }; | ||||||
|  |  | ||||||
|  |             var dirtyBoxes = []; | ||||||
|  |             if (cr.x1 >= cr.x2 || cr.y1 >= cr.y2) { | ||||||
|  |                 // Whole viewport is dirty | ||||||
|  |                 dirtyBoxes.push({ 'x': vp.x, 'y': vp.y, 'w': vp.w, 'h': vp.h }); | ||||||
|  |             } else { | ||||||
|  |                 // Redraw dirty regions | ||||||
|  |                 var vx2 = vp.x + vp.w - 1; | ||||||
|  |                 var vy2 = vp.y + vp.h - 1; | ||||||
|  |  | ||||||
|  |                 if (vp.x < cr.x1) { | ||||||
|  |                     // left side dirty region | ||||||
|  |                     dirtyBoxes.push({'x': vp.x, 'y': vp.y, | ||||||
|  |                                      'w': cr.x1 - vp.x + 1, 'h': vp.h}); | ||||||
|  |                 } | ||||||
|  |                 if (vx2 > cr.x2) { | ||||||
|  |                     // right side dirty region | ||||||
|  |                     dirtyBoxes.push({'x': cr.x2 + 1, 'y': vp.y, | ||||||
|  |                                      'w': vx2 - cr.x2, 'h': vp.h}); | ||||||
|  |                 } | ||||||
|  |                 if(vp.y < cr.y1) { | ||||||
|  |                     // top/middle dirty region | ||||||
|  |                     dirtyBoxes.push({'x': cr.x1, 'y': vp.y, | ||||||
|  |                                      'w': cr.x2 - cr.x1 + 1, 'h': cr.y1 - vp.y}); | ||||||
|  |                 } | ||||||
|  |                 if (vy2 > cr.y2) { | ||||||
|  |                     // bottom/middle dirty region | ||||||
|  |                     dirtyBoxes.push({'x': cr.x1, 'y': cr.y2 + 1, | ||||||
|  |                                      'w': cr.x2 - cr.x1 + 1, 'h': vy2 - cr.y2}); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             this._cleanRect = {'x1': vp.x, 'y1': vp.y, | ||||||
|  |                                'x2': vp.x + vp.w - 1, 'y2': vp.y + vp.h - 1}; | ||||||
|  |  | ||||||
|  |             return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes}; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         absX: function (x) { | ||||||
|  |             return x + this._viewportLoc.x; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         absY: function (y) { | ||||||
|  |             return y + this._viewportLoc.y; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         resize: function (width, height) { | ||||||
|  |             this._prevDrawStyle = ""; | ||||||
|  |  | ||||||
|  |             this._fb_width = width; | ||||||
|  |             this._fb_height = height; | ||||||
|  |  | ||||||
|  |             this._rescale(this._scale); | ||||||
|  |  | ||||||
|  |             this.viewportChangeSize(); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         clear: function () { | ||||||
|  |             if (this._logo) { | ||||||
|  |                 this.resize(this._logo.width, this._logo.height); | ||||||
|  |                 this.blitStringImage(this._logo.data, 0, 0); | ||||||
|  |             } else { | ||||||
|  |                 if (Util.Engine.trident === 6) { | ||||||
|  |                     // NB(directxman12): there's a bug in IE10 where we can fail to actually | ||||||
|  |                     //                   clear the canvas here because of the resize. | ||||||
|  |                     //                   Clearing the current viewport first fixes the issue | ||||||
|  |                     this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h); | ||||||
|  |                 } | ||||||
|  |                 this.resize(240, 20); | ||||||
|  |                 this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             this._renderQ = []; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         fillRect: function (x, y, width, height, color, from_queue) { | ||||||
|  |             if (this._renderQ.length !== 0 && !from_queue) { | ||||||
|  |                 this.renderQ_push({ | ||||||
|  |                     'type': 'fill', | ||||||
|  |                     'x': x, | ||||||
|  |                     'y': y, | ||||||
|  |                     'width': width, | ||||||
|  |                     'height': height, | ||||||
|  |                     'color': color | ||||||
|  |                 }); | ||||||
|  |             } else { | ||||||
|  |                 this._setFillColor(color); | ||||||
|  |                 this._drawCtx.fillRect(x - this._viewportLoc.x, y - this._viewportLoc.y, width, height); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) { | ||||||
|  |             if (this._renderQ.length !== 0 && !from_queue) { | ||||||
|  |                 this.renderQ_push({ | ||||||
|  |                     'type': 'copy', | ||||||
|  |                     'old_x': old_x, | ||||||
|  |                     'old_y': old_y, | ||||||
|  |                     'x': new_x, | ||||||
|  |                     'y': new_y, | ||||||
|  |                     'width': w, | ||||||
|  |                     'height': h, | ||||||
|  |                 }); | ||||||
|  |             } else { | ||||||
|  |                 var x1 = old_x - this._viewportLoc.x; | ||||||
|  |                 var y1 = old_y - this._viewportLoc.y; | ||||||
|  |                 var x2 = new_x - this._viewportLoc.x; | ||||||
|  |                 var y2 = new_y - this._viewportLoc.y; | ||||||
|  |  | ||||||
|  |                 this._drawCtx.drawImage(this._target, x1, y1, w, h, x2, y2, w, h); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // start updating a tile | ||||||
|  |         startTile: function (x, y, width, height, color) { | ||||||
|  |             this._tile_x = x; | ||||||
|  |             this._tile_y = y; | ||||||
|  |             if (width === 16 && height === 16) { | ||||||
|  |                 this._tile = this._tile16x16; | ||||||
|  |             } else { | ||||||
|  |                 this._tile = this._drawCtx.createImageData(width, height); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (this._prefer_js) { | ||||||
|  |                 var bgr; | ||||||
|  |                 if (this._true_color) { | ||||||
|  |                     bgr = color; | ||||||
|  |                 } else { | ||||||
|  |                     bgr = this._colourMap[color[0]]; | ||||||
|  |                 } | ||||||
|  |                 var red = bgr[2]; | ||||||
|  |                 var green = bgr[1]; | ||||||
|  |                 var blue = bgr[0]; | ||||||
|  |  | ||||||
|  |                 var data = this._tile.data; | ||||||
|  |                 for (var i = 0; i < width * height * 4; i += 4) { | ||||||
|  |                     data[i] = red; | ||||||
|  |                     data[i + 1] = green; | ||||||
|  |                     data[i + 2] = blue; | ||||||
|  |                     data[i + 3] = 255; | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 this.fillRect(x, y, width, height, color, true); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // update sub-rectangle of the current tile | ||||||
|  |         subTile: function (x, y, w, h, color) { | ||||||
|  |             if (this._prefer_js) { | ||||||
|  |                 var bgr; | ||||||
|  |                 if (this._true_color) { | ||||||
|  |                     bgr = color; | ||||||
|  |                 } else { | ||||||
|  |                     bgr = this._colourMap[color[0]]; | ||||||
|  |                 } | ||||||
|  |                 var red = bgr[2]; | ||||||
|  |                 var green = bgr[1]; | ||||||
|  |                 var blue = bgr[0]; | ||||||
|  |                 var xend = x + w; | ||||||
|  |                 var yend = y + h; | ||||||
|  |  | ||||||
|  |                 var data = this._tile.data; | ||||||
|  |                 var width = this._tile.width; | ||||||
|  |                 for (var j = y; j < yend; j++) { | ||||||
|  |                     for (var i = x; i < xend; i++) { | ||||||
|  |                         var p = (i + (j * width)) * 4; | ||||||
|  |                         data[p] = red; | ||||||
|  |                         data[p + 1] = green; | ||||||
|  |                         data[p + 2] = blue; | ||||||
|  |                         data[p + 3] = 255; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 this.fillRect(this._tile_x + x, this._tile_y + y, w, h, color, true); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // draw the current tile to the screen | ||||||
|  |         finishTile: function () { | ||||||
|  |             if (this._prefer_js) { | ||||||
|  |                 this._drawCtx.putImageData(this._tile, this._tile_x - this._viewportLoc.x, | ||||||
|  |                                            this._tile_y - this._viewportLoc.y); | ||||||
|  |             } | ||||||
|  |             // else: No-op -- already done by setSubTile | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         blitImage: function (x, y, width, height, arr, offset, from_queue) { | ||||||
|  |             if (this._renderQ.length !== 0 && !from_queue) { | ||||||
|  |                 // NB(directxman12): it's technically more performant here to use preallocated arrays, | ||||||
|  |                 // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, | ||||||
|  |                 // this probably isn't getting called *nearly* as much | ||||||
|  |                 var new_arr = new Uint8Array(width * height * 4); | ||||||
|  |                 new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length)); | ||||||
|  |                 this.renderQ_push({ | ||||||
|  |                     'type': 'blit', | ||||||
|  |                     'data': new_arr, | ||||||
|  |                     'x': x, | ||||||
|  |                     'y': y, | ||||||
|  |                     'width': width, | ||||||
|  |                     'height': height, | ||||||
|  |                 }); | ||||||
|  |             } else if (this._true_color) { | ||||||
|  |                 this._bgrxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); | ||||||
|  |             } else { | ||||||
|  |                 this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         blitRgbImage: function (x, y , width, height, arr, offset, from_queue) { | ||||||
|  |             if (this._renderQ.length !== 0 && !from_queue) { | ||||||
|  |                 // NB(directxman12): it's technically more performant here to use preallocated arrays, | ||||||
|  |                 // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, | ||||||
|  |                 // this probably isn't getting called *nearly* as much | ||||||
|  |                 var new_arr = new Uint8Array(width * height * 4); | ||||||
|  |                 new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length)); | ||||||
|  |                 this.renderQ_push({ | ||||||
|  |                     'type': 'blitRgb', | ||||||
|  |                     'data': new_arr, | ||||||
|  |                     'x': x, | ||||||
|  |                     'y': y, | ||||||
|  |                     'width': width, | ||||||
|  |                     'height': height, | ||||||
|  |                 }); | ||||||
|  |             } else if (this._true_color) { | ||||||
|  |                 this._rgbImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); | ||||||
|  |             } else { | ||||||
|  |                 // probably wrong? | ||||||
|  |                 this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) { | ||||||
|  |             if (this._renderQ.length !== 0 && !from_queue) { | ||||||
|  |                 // NB(directxman12): it's technically more performant here to use preallocated arrays, | ||||||
|  |                 // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, | ||||||
|  |                 // this probably isn't getting called *nearly* as much | ||||||
|  |                 var new_arr = new Uint8Array(width * height * 4); | ||||||
|  |                 new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length)); | ||||||
|  |                 this.renderQ_push({ | ||||||
|  |                     'type': 'blitRgbx', | ||||||
|  |                     'data': new_arr, | ||||||
|  |                     'x': x, | ||||||
|  |                     'y': y, | ||||||
|  |                     'width': width, | ||||||
|  |                     'height': height, | ||||||
|  |                 }); | ||||||
|  |             } else { | ||||||
|  |                 this._rgbxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         blitStringImage: function (str, x, y) { | ||||||
|  |             var img = new Image(); | ||||||
|  |             img.onload = function () { | ||||||
|  |                 this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y); | ||||||
|  |             }.bind(this); | ||||||
|  |             img.src = str; | ||||||
|  |             return img; // for debugging purposes | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // wrap ctx.drawImage but relative to viewport | ||||||
|  |         drawImage: function (img, x, y) { | ||||||
|  |             this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         renderQ_push: function (action) { | ||||||
|  |             this._renderQ.push(action); | ||||||
|  |             if (this._renderQ.length === 1) { | ||||||
|  |                 // If this can be rendered immediately it will be, otherwise | ||||||
|  |                 // the scanner will start polling the queue (every | ||||||
|  |                 // requestAnimationFrame interval) | ||||||
|  |                 this._scan_renderQ(); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         changeCursor: function (pixels, mask, hotx, hoty, w, h) { | ||||||
|  |             if (this._cursor_uri === false) { | ||||||
|  |                 Util.Warn("changeCursor called but no cursor data URI support"); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (this._true_color) { | ||||||
|  |                 Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h); | ||||||
|  |             } else { | ||||||
|  |                 Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h, this._colourMap); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         defaultCursor: function () { | ||||||
|  |             this._target.style.cursor = "default"; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         disableLocalCursor: function () { | ||||||
|  |             this._target.style.cursor = "none"; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         clippingDisplay: function () { | ||||||
|  |             var vp = this._viewportLoc; | ||||||
|  |  | ||||||
|  |             var fbClip = this._fb_width > vp.w || this._fb_height > vp.h; | ||||||
|  |             var limitedVp = this._maxWidth !== 0 && this._maxHeight !== 0; | ||||||
|  |             var clipping = false; | ||||||
|  |  | ||||||
|  |             if (limitedVp) { | ||||||
|  |                 clipping = vp.w > this._maxWidth || vp.h > this._maxHeight; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return fbClip || (limitedVp && clipping); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // Overridden getters/setters | ||||||
|  |         get_context: function () { | ||||||
|  |             return this._drawCtx; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         set_scale: function (scale) { | ||||||
|  |             this._rescale(scale); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         set_width: function (w) { | ||||||
|  |             this._fb_width = w; | ||||||
|  |         }, | ||||||
|  |         get_width: function () { | ||||||
|  |             return this._fb_width; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         set_height: function (h) { | ||||||
|  |             this._fb_height =  h; | ||||||
|  |         }, | ||||||
|  |         get_height: function () { | ||||||
|  |             return this._fb_height; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         autoscale: function (containerWidth, containerHeight, downscaleOnly) { | ||||||
|  |             var targetAspectRatio = containerWidth / containerHeight; | ||||||
|  |             var fbAspectRatio = this._fb_width / this._fb_height; | ||||||
|  |  | ||||||
|  |             var scaleRatio; | ||||||
|  |             if (fbAspectRatio >= targetAspectRatio) { | ||||||
|  |                 scaleRatio = containerWidth / this._fb_width; | ||||||
|  |             } else { | ||||||
|  |                 scaleRatio = containerHeight / this._fb_height; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var targetW, targetH; | ||||||
|  |             if (scaleRatio > 1.0 && downscaleOnly) { | ||||||
|  |                 targetW = this._fb_width; | ||||||
|  |                 targetH = this._fb_height; | ||||||
|  |                 scaleRatio = 1.0; | ||||||
|  |             } else if (fbAspectRatio >= targetAspectRatio) { | ||||||
|  |                 targetW = containerWidth; | ||||||
|  |                 targetH = Math.round(containerWidth / fbAspectRatio); | ||||||
|  |             } else { | ||||||
|  |                 targetW = Math.round(containerHeight * fbAspectRatio); | ||||||
|  |                 targetH = containerHeight; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // NB(directxman12): If you set the width directly, or set the | ||||||
|  |             //                   style width to a number, the canvas is cleared. | ||||||
|  |             //                   However, if you set the style width to a string | ||||||
|  |             //                   ('NNNpx'), the canvas is scaled without clearing. | ||||||
|  |             this._target.style.width = targetW + 'px'; | ||||||
|  |             this._target.style.height = targetH + 'px'; | ||||||
|  |  | ||||||
|  |             this._scale = scaleRatio; | ||||||
|  |  | ||||||
|  |             return scaleRatio;  // so that the mouse, etc scale can be set | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // Private Methods | ||||||
|  |         _rescale: function (factor) { | ||||||
|  |             this._scale = factor; | ||||||
|  |  | ||||||
|  |             var w; | ||||||
|  |             var h; | ||||||
|  |  | ||||||
|  |             if (this._viewport && | ||||||
|  |                 this._maxWidth !== 0 && this._maxHeight !== 0) { | ||||||
|  |                 w = Math.min(this._fb_width, this._maxWidth); | ||||||
|  |                 h = Math.min(this._fb_height, this._maxHeight); | ||||||
|  |             } else { | ||||||
|  |                 w = this._fb_width; | ||||||
|  |                 h = this._fb_height; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             this._target.style.width = Math.round(factor * w) + 'px'; | ||||||
|  |             this._target.style.height = Math.round(factor * h) + 'px'; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _setFillColor: function (color) { | ||||||
|  |             var bgr; | ||||||
|  |             if (this._true_color) { | ||||||
|  |                 bgr = color; | ||||||
|  |             } else { | ||||||
|  |                 bgr = this._colourMap[color]; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var newStyle = 'rgb(' + bgr[2] + ',' + bgr[1] + ',' + bgr[0] + ')'; | ||||||
|  |             if (newStyle !== this._prevDrawStyle) { | ||||||
|  |                 this._drawCtx.fillStyle = newStyle; | ||||||
|  |                 this._prevDrawStyle = newStyle; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _rgbImageData: function (x, y, vx, vy, width, height, arr, offset) { | ||||||
|  |             var img = this._drawCtx.createImageData(width, height); | ||||||
|  |             var data = img.data; | ||||||
|  |             for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) { | ||||||
|  |                 data[i]     = arr[j]; | ||||||
|  |                 data[i + 1] = arr[j + 1]; | ||||||
|  |                 data[i + 2] = arr[j + 2]; | ||||||
|  |                 data[i + 3] = 255;  // Alpha | ||||||
|  |             } | ||||||
|  |             this._drawCtx.putImageData(img, x - vx, y - vy); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _bgrxImageData: function (x, y, vx, vy, width, height, arr, offset) { | ||||||
|  |             var img = this._drawCtx.createImageData(width, height); | ||||||
|  |             var data = img.data; | ||||||
|  |             for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) { | ||||||
|  |                 data[i]     = arr[j + 2]; | ||||||
|  |                 data[i + 1] = arr[j + 1]; | ||||||
|  |                 data[i + 2] = arr[j]; | ||||||
|  |                 data[i + 3] = 255;  // Alpha | ||||||
|  |             } | ||||||
|  |             this._drawCtx.putImageData(img, x - vx, y - vy); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _rgbxImageData: function (x, y, vx, vy, width, height, arr, offset) { | ||||||
|  |             // NB(directxman12): arr must be an Type Array view | ||||||
|  |             var img; | ||||||
|  |             if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) { | ||||||
|  |                 img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height); | ||||||
|  |             } else { | ||||||
|  |                 img = this._drawCtx.createImageData(width, height); | ||||||
|  |                 img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4)); | ||||||
|  |             } | ||||||
|  |             this._drawCtx.putImageData(img, x - vx, y - vy); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _cmapImageData: function (x, y, vx, vy, width, height, arr, offset) { | ||||||
|  |             var img = this._drawCtx.createImageData(width, height); | ||||||
|  |             var data = img.data; | ||||||
|  |             var cmap = this._colourMap; | ||||||
|  |             for (var i = 0, j = offset; i < width * height * 4; i += 4, j++) { | ||||||
|  |                 var bgr = cmap[arr[j]]; | ||||||
|  |                 data[i]     = bgr[2]; | ||||||
|  |                 data[i + 1] = bgr[1]; | ||||||
|  |                 data[i + 2] = bgr[0]; | ||||||
|  |                 data[i + 3] = 255;  // Alpha | ||||||
|  |             } | ||||||
|  |             this._drawCtx.putImageData(img, x - vx, y - vy); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _scan_renderQ: function () { | ||||||
|  |             var ready = true; | ||||||
|  |             while (ready && this._renderQ.length > 0) { | ||||||
|  |                 var a = this._renderQ[0]; | ||||||
|  |                 switch (a.type) { | ||||||
|  |                     case 'copy': | ||||||
|  |                         this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true); | ||||||
|  |                         break; | ||||||
|  |                     case 'fill': | ||||||
|  |                         this.fillRect(a.x, a.y, a.width, a.height, a.color, true); | ||||||
|  |                         break; | ||||||
|  |                     case 'blit': | ||||||
|  |                         this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true); | ||||||
|  |                         break; | ||||||
|  |                     case 'blitRgb': | ||||||
|  |                         this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0, true); | ||||||
|  |                         break; | ||||||
|  |                     case 'blitRgbx': | ||||||
|  |                         this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true); | ||||||
|  |                         break; | ||||||
|  |                     case 'img': | ||||||
|  |                         if (a.img.complete) { | ||||||
|  |                             this.drawImage(a.img, a.x, a.y); | ||||||
|  |                         } else { | ||||||
|  |                             // We need to wait for this image to 'load' | ||||||
|  |                             // to keep things in-order | ||||||
|  |                             ready = false; | ||||||
|  |                         } | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (ready) { | ||||||
|  |                     this._renderQ.shift(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (this._renderQ.length > 0) { | ||||||
|  |                 requestAnimFrame(this._scan_renderQ.bind(this)); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     Util.make_properties(Display, [ | ||||||
|  |         ['target', 'wo', 'dom'],       // Canvas element for rendering | ||||||
|  |         ['context', 'ro', 'raw'],      // Canvas 2D context for rendering (read-only) | ||||||
|  |         ['logo', 'rw', 'raw'],         // Logo to display when cleared: {"width": w, "height": h, "data": data} | ||||||
|  |         ['true_color', 'rw', 'bool'],  // Use true-color pixel data | ||||||
|  |         ['colourMap', 'rw', 'arr'],    // Colour map array (when not true-color) | ||||||
|  |         ['scale', 'rw', 'float'],      // Display area scale factor 0.0 - 1.0 | ||||||
|  |         ['viewport', 'rw', 'bool'],    // Use viewport clipping | ||||||
|  |         ['width', 'rw', 'int'],        // Display area width | ||||||
|  |         ['height', 'rw', 'int'],       // Display area height | ||||||
|  |         ['maxWidth', 'rw', 'int'],     // Viewport max width (0 if disabled) | ||||||
|  |         ['maxHeight', 'rw', 'int'],    // Viewport max height (0 if disabled) | ||||||
|  |  | ||||||
|  |         ['render_mode', 'ro', 'str'],  // Canvas rendering mode (read-only) | ||||||
|  |  | ||||||
|  |         ['prefer_js', 'rw', 'str'],    // Prefer Javascript over canvas methods | ||||||
|  |         ['cursor_uri', 'rw', 'raw']    // Can we render cursor using data URI | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     // Class Methods | ||||||
|  |     Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) { | ||||||
|  |         var w = w0; | ||||||
|  |         var h = h0; | ||||||
|  |         if (h < w) { | ||||||
|  |             h = w;  // increase h to make it square | ||||||
|  |         } else { | ||||||
|  |             w = h;  // increase w to make it square | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var cur = []; | ||||||
|  |  | ||||||
|  |         // Push multi-byte little-endian values | ||||||
|  |         cur.push16le = function (num) { | ||||||
|  |             this.push(num & 0xFF, (num >> 8) & 0xFF); | ||||||
|  |         }; | ||||||
|  |         cur.push32le = function (num) { | ||||||
|  |             this.push(num & 0xFF, | ||||||
|  |                       (num >> 8) & 0xFF, | ||||||
|  |                       (num >> 16) & 0xFF, | ||||||
|  |                       (num >> 24) & 0xFF); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         var IHDRsz = 40; | ||||||
|  |         var RGBsz = w * h * 4; | ||||||
|  |         var XORsz = Math.ceil((w * h) / 8.0); | ||||||
|  |         var ANDsz = Math.ceil((w * h) / 8.0); | ||||||
|  |  | ||||||
|  |         cur.push16le(0);        // 0: Reserved | ||||||
|  |         cur.push16le(2);        // 2: .CUR type | ||||||
|  |         cur.push16le(1);        // 4: Number of images, 1 for non-animated ico | ||||||
|  |  | ||||||
|  |         // Cursor #1 header (ICONDIRENTRY) | ||||||
|  |         cur.push(w);            // 6: width | ||||||
|  |         cur.push(h);            // 7: height | ||||||
|  |         cur.push(0);            // 8: colors, 0 -> true-color | ||||||
|  |         cur.push(0);            // 9: reserved | ||||||
|  |         cur.push16le(hotx);     // 10: hotspot x coordinate | ||||||
|  |         cur.push16le(hoty);     // 12: hotspot y coordinate | ||||||
|  |         cur.push32le(IHDRsz + RGBsz + XORsz + ANDsz); | ||||||
|  |                                 // 14: cursor data byte size | ||||||
|  |         cur.push32le(22);       // 18: offset of cursor data in the file | ||||||
|  |  | ||||||
|  |         // Cursor #1 InfoHeader (ICONIMAGE/BITMAPINFO) | ||||||
|  |         cur.push32le(IHDRsz);   // 22: InfoHeader size | ||||||
|  |         cur.push32le(w);        // 26: Cursor width | ||||||
|  |         cur.push32le(h * 2);    // 30: XOR+AND height | ||||||
|  |         cur.push16le(1);        // 34: number of planes | ||||||
|  |         cur.push16le(32);       // 36: bits per pixel | ||||||
|  |         cur.push32le(0);        // 38: Type of compression | ||||||
|  |  | ||||||
|  |         cur.push32le(XORsz + ANDsz); | ||||||
|  |                                 // 42: Size of Image | ||||||
|  |         cur.push32le(0);        // 46: reserved | ||||||
|  |         cur.push32le(0);        // 50: reserved | ||||||
|  |         cur.push32le(0);        // 54: reserved | ||||||
|  |         cur.push32le(0);        // 58: reserved | ||||||
|  |  | ||||||
|  |         // 62: color data (RGBQUAD icColors[]) | ||||||
|  |         var y, x; | ||||||
|  |         for (y = h - 1; y >= 0; y--) { | ||||||
|  |             for (x = 0; x < w; x++) { | ||||||
|  |                 if (x >= w0 || y >= h0) { | ||||||
|  |                     cur.push(0);  // blue | ||||||
|  |                     cur.push(0);  // green | ||||||
|  |                     cur.push(0);  // red | ||||||
|  |                     cur.push(0);  // alpha | ||||||
|  |                 } else { | ||||||
|  |                     var idx = y * Math.ceil(w0 / 8) + Math.floor(x / 8); | ||||||
|  |                     var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0; | ||||||
|  |                     if (cmap) { | ||||||
|  |                         idx = (w0 * y) + x; | ||||||
|  |                         var rgb = cmap[pixels[idx]]; | ||||||
|  |                         cur.push(rgb[2]);  // blue | ||||||
|  |                         cur.push(rgb[1]);  // green | ||||||
|  |                         cur.push(rgb[0]);  // red | ||||||
|  |                         cur.push(alpha);   // alpha | ||||||
|  |                     } else { | ||||||
|  |                         idx = ((w0 * y) + x) * 4; | ||||||
|  |                         cur.push(pixels[idx + 2]); // blue | ||||||
|  |                         cur.push(pixels[idx + 1]); // green | ||||||
|  |                         cur.push(pixels[idx]);     // red | ||||||
|  |                         cur.push(alpha);           // alpha | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // XOR/bitmask data (BYTE icXOR[]) | ||||||
|  |         // (ignored, just needs to be the right size) | ||||||
|  |         for (y = 0; y < h; y++) { | ||||||
|  |             for (x = 0; x < Math.ceil(w / 8); x++) { | ||||||
|  |                 cur.push(0); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // AND/bitmask data (BYTE icAND[]) | ||||||
|  |         // (ignored, just needs to be the right size) | ||||||
|  |         for (y = 0; y < h; y++) { | ||||||
|  |             for (x = 0; x < Math.ceil(w / 8); x++) { | ||||||
|  |                 cur.push(0); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var url = 'data:image/x-icon;base64,' + Base64.encode(cur); | ||||||
|  |         target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default'; | ||||||
|  |     }; | ||||||
|  | })(); | ||||||
							
								
								
									
										2418
									
								
								public/novnc/include/inflator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										389
									
								
								public/novnc/include/input.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,389 @@ | |||||||
|  | /* | ||||||
|  |  * noVNC: HTML5 VNC client | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Copyright (C) 2013 Samuel Mannehed for Cendio AB | ||||||
|  |  * Licensed under MPL 2.0 or any later version (see LICENSE.txt) | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /*jslint browser: true, white: false */ | ||||||
|  | /*global window, Util */ | ||||||
|  |  | ||||||
|  | var Keyboard, Mouse; | ||||||
|  |  | ||||||
|  | (function () { | ||||||
|  |     "use strict"; | ||||||
|  |  | ||||||
|  |     // | ||||||
|  |     // Keyboard event handler | ||||||
|  |     // | ||||||
|  |  | ||||||
|  |     Keyboard = function (defaults) { | ||||||
|  |         this._keyDownList = [];         // List of depressed keys | ||||||
|  |                                         // (even if they are happy) | ||||||
|  |  | ||||||
|  |         Util.set_defaults(this, defaults, { | ||||||
|  |             'target': document, | ||||||
|  |             'focused': true | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // create the keyboard handler | ||||||
|  |         this._handler = new KeyEventDecoder(kbdUtil.ModifierSync(), | ||||||
|  |             VerifyCharModifier( /* jshint newcap: false */ | ||||||
|  |                 TrackKeyState( | ||||||
|  |                     EscapeModifiers(this._handleRfbEvent.bind(this)) | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         ); /* jshint newcap: true */ | ||||||
|  |  | ||||||
|  |         // keep these here so we can refer to them later | ||||||
|  |         this._eventHandlers = { | ||||||
|  |             'keyup': this._handleKeyUp.bind(this), | ||||||
|  |             'keydown': this._handleKeyDown.bind(this), | ||||||
|  |             'keypress': this._handleKeyPress.bind(this), | ||||||
|  |             'blur': this._allKeysUp.bind(this) | ||||||
|  |         }; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     Keyboard.prototype = { | ||||||
|  |         // private methods | ||||||
|  |  | ||||||
|  |         _handleRfbEvent: function (e) { | ||||||
|  |             if (this._onKeyPress) { | ||||||
|  |                 Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") + | ||||||
|  |                            ", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")"); | ||||||
|  |                 this._onKeyPress(e.keysym.keysym, e.type == 'keydown'); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _handleKeyDown: function (e) { | ||||||
|  |             if (!this._focused) { return true; } | ||||||
|  |  | ||||||
|  |             if (this._handler.keydown(e)) { | ||||||
|  |                 // Suppress bubbling/default actions | ||||||
|  |                 Util.stopEvent(e); | ||||||
|  |                 return false; | ||||||
|  |             } else { | ||||||
|  |                 // Allow the event to bubble and become a keyPress event which | ||||||
|  |                 // will have the character code translated | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _handleKeyPress: function (e) { | ||||||
|  |             if (!this._focused) { return true; } | ||||||
|  |  | ||||||
|  |             if (this._handler.keypress(e)) { | ||||||
|  |                 // Suppress bubbling/default actions | ||||||
|  |                 Util.stopEvent(e); | ||||||
|  |                 return false; | ||||||
|  |             } else { | ||||||
|  |                 // Allow the event to bubble and become a keyPress event which | ||||||
|  |                 // will have the character code translated | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _handleKeyUp: function (e) { | ||||||
|  |             if (!this._focused) { return true; } | ||||||
|  |  | ||||||
|  |             if (this._handler.keyup(e)) { | ||||||
|  |                 // Suppress bubbling/default actions | ||||||
|  |                 Util.stopEvent(e); | ||||||
|  |                 return false; | ||||||
|  |             } else { | ||||||
|  |                 // Allow the event to bubble and become a keyPress event which | ||||||
|  |                 // will have the character code translated | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _allKeysUp: function () { | ||||||
|  |             Util.Debug(">> Keyboard.allKeysUp"); | ||||||
|  |             this._handler.releaseAll(); | ||||||
|  |             Util.Debug("<< Keyboard.allKeysUp"); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // Public methods | ||||||
|  |  | ||||||
|  |         grab: function () { | ||||||
|  |             //Util.Debug(">> Keyboard.grab"); | ||||||
|  |             var c = this._target; | ||||||
|  |  | ||||||
|  |             Util.addEvent(c, 'keydown', this._eventHandlers.keydown); | ||||||
|  |             Util.addEvent(c, 'keyup', this._eventHandlers.keyup); | ||||||
|  |             Util.addEvent(c, 'keypress', this._eventHandlers.keypress); | ||||||
|  |  | ||||||
|  |             // Release (key up) if window loses focus | ||||||
|  |             Util.addEvent(window, 'blur', this._eventHandlers.blur); | ||||||
|  |  | ||||||
|  |             //Util.Debug("<< Keyboard.grab"); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         ungrab: function () { | ||||||
|  |             //Util.Debug(">> Keyboard.ungrab"); | ||||||
|  |             var c = this._target; | ||||||
|  |  | ||||||
|  |             Util.removeEvent(c, 'keydown', this._eventHandlers.keydown); | ||||||
|  |             Util.removeEvent(c, 'keyup', this._eventHandlers.keyup); | ||||||
|  |             Util.removeEvent(c, 'keypress', this._eventHandlers.keypress); | ||||||
|  |             Util.removeEvent(window, 'blur', this._eventHandlers.blur); | ||||||
|  |  | ||||||
|  |             // Release (key up) all keys that are in a down state | ||||||
|  |             this._allKeysUp(); | ||||||
|  |  | ||||||
|  |             //Util.Debug(">> Keyboard.ungrab"); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         sync: function (e) { | ||||||
|  |             this._handler.syncModifiers(e); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     Util.make_properties(Keyboard, [ | ||||||
|  |         ['target',     'wo', 'dom'],  // DOM element that captures keyboard input | ||||||
|  |         ['focused',    'rw', 'bool'], // Capture and send key events | ||||||
|  |  | ||||||
|  |         ['onKeyPress', 'rw', 'func'] // Handler for key press/release | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|  |     // | ||||||
|  |     // Mouse event handler | ||||||
|  |     // | ||||||
|  |  | ||||||
|  |     Mouse = function (defaults) { | ||||||
|  |         this._mouseCaptured  = false; | ||||||
|  |  | ||||||
|  |         this._doubleClickTimer = null; | ||||||
|  |         this._lastTouchPos = null; | ||||||
|  |  | ||||||
|  |         // Configuration attributes | ||||||
|  |         Util.set_defaults(this, defaults, { | ||||||
|  |             'target': document, | ||||||
|  |             'focused': true, | ||||||
|  |             'scale': 1.0, | ||||||
|  |             'touchButton': 1 | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         this._eventHandlers = { | ||||||
|  |             'mousedown': this._handleMouseDown.bind(this), | ||||||
|  |             'mouseup': this._handleMouseUp.bind(this), | ||||||
|  |             'mousemove': this._handleMouseMove.bind(this), | ||||||
|  |             'mousewheel': this._handleMouseWheel.bind(this), | ||||||
|  |             'mousedisable': this._handleMouseDisable.bind(this) | ||||||
|  |         }; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     Mouse.prototype = { | ||||||
|  |         // private methods | ||||||
|  |         _captureMouse: function () { | ||||||
|  |             // capturing the mouse ensures we get the mouseup event | ||||||
|  |             if (this._target.setCapture) { | ||||||
|  |                 this._target.setCapture(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // some browsers give us mouseup events regardless, | ||||||
|  |             // so if we never captured the mouse, we can disregard the event | ||||||
|  |             this._mouseCaptured = true; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _releaseMouse: function () { | ||||||
|  |             if (this._target.releaseCapture) { | ||||||
|  |                 this._target.releaseCapture(); | ||||||
|  |             } | ||||||
|  |             this._mouseCaptured = false; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _resetDoubleClickTimer: function () { | ||||||
|  |             this._doubleClickTimer = null; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _handleMouseButton: function (e, down) { | ||||||
|  |             if (!this._focused) { return true; } | ||||||
|  |  | ||||||
|  |             if (this._notify) { | ||||||
|  |                 this._notify(e); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var evt = (e ? e : window.event); | ||||||
|  |             var pos = Util.getEventPosition(e, this._target, this._scale); | ||||||
|  |  | ||||||
|  |             var bmask; | ||||||
|  |             if (e.touches || e.changedTouches) { | ||||||
|  |                 // Touch device | ||||||
|  |  | ||||||
|  |                 // When two touches occur within 500 ms of each other and are | ||||||
|  |                 // close enough together a double click is triggered. | ||||||
|  |                 if (down == 1) { | ||||||
|  |                     if (this._doubleClickTimer === null) { | ||||||
|  |                         this._lastTouchPos = pos; | ||||||
|  |                     } else { | ||||||
|  |                         clearTimeout(this._doubleClickTimer); | ||||||
|  |  | ||||||
|  |                         // When the distance between the two touches is small enough | ||||||
|  |                         // force the position of the latter touch to the position of | ||||||
|  |                         // the first. | ||||||
|  |  | ||||||
|  |                         var xs = this._lastTouchPos.x - pos.x; | ||||||
|  |                         var ys = this._lastTouchPos.y - pos.y; | ||||||
|  |                         var d = Math.sqrt((xs * xs) + (ys * ys)); | ||||||
|  |  | ||||||
|  |                         // The goal is to trigger on a certain physical width, the | ||||||
|  |                         // devicePixelRatio brings us a bit closer but is not optimal. | ||||||
|  |                         var threshold = 20 * (window.devicePixelRatio || 1); | ||||||
|  |                         if (d < threshold) { | ||||||
|  |                             pos = this._lastTouchPos; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     this._doubleClickTimer = setTimeout(this._resetDoubleClickTimer.bind(this), 500); | ||||||
|  |                 } | ||||||
|  |                 bmask = this._touchButton; | ||||||
|  |                 // If bmask is set | ||||||
|  |             } else if (evt.which) { | ||||||
|  |                 /* everything except IE */ | ||||||
|  |                 bmask = 1 << evt.button; | ||||||
|  |             } else { | ||||||
|  |                 /* IE including 9 */ | ||||||
|  |                 bmask = (evt.button & 0x1) +      // Left | ||||||
|  |                         (evt.button & 0x2) * 2 +  // Right | ||||||
|  |                         (evt.button & 0x4) / 2;   // Middle | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (this._onMouseButton) { | ||||||
|  |                 Util.Debug("onMouseButton " + (down ? "down" : "up") + | ||||||
|  |                            ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask); | ||||||
|  |                 this._onMouseButton(pos.x, pos.y, down, bmask); | ||||||
|  |             } | ||||||
|  |             Util.stopEvent(e); | ||||||
|  |             return false; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _handleMouseDown: function (e) { | ||||||
|  |             this._captureMouse(); | ||||||
|  |             this._handleMouseButton(e, 1); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _handleMouseUp: function (e) { | ||||||
|  |             if (!this._mouseCaptured) { return; } | ||||||
|  |  | ||||||
|  |             this._handleMouseButton(e, 0); | ||||||
|  |             this._releaseMouse(); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _handleMouseWheel: function (e) { | ||||||
|  |             if (!this._focused) { return true; } | ||||||
|  |  | ||||||
|  |             if (this._notify) { | ||||||
|  |                 this._notify(e); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var evt = (e ? e : window.event); | ||||||
|  |             var pos = Util.getEventPosition(e, this._target, this._scale); | ||||||
|  |             var wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40; | ||||||
|  |             var bmask; | ||||||
|  |             if (wheelData > 0) { | ||||||
|  |                 bmask = 1 << 3; | ||||||
|  |             } else { | ||||||
|  |                 bmask = 1 << 4; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (this._onMouseButton) { | ||||||
|  |                 this._onMouseButton(pos.x, pos.y, 1, bmask); | ||||||
|  |                 this._onMouseButton(pos.x, pos.y, 0, bmask); | ||||||
|  |             } | ||||||
|  |             Util.stopEvent(e); | ||||||
|  |             return false; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _handleMouseMove: function (e) { | ||||||
|  |             if (! this._focused) { return true; } | ||||||
|  |  | ||||||
|  |             if (this._notify) { | ||||||
|  |                 this._notify(e); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var evt = (e ? e : window.event); | ||||||
|  |             var pos = Util.getEventPosition(e, this._target, this._scale); | ||||||
|  |             if (this._onMouseMove) { | ||||||
|  |                 this._onMouseMove(pos.x, pos.y); | ||||||
|  |             } | ||||||
|  |             Util.stopEvent(e); | ||||||
|  |             return false; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _handleMouseDisable: function (e) { | ||||||
|  |             if (!this._focused) { return true; } | ||||||
|  |  | ||||||
|  |             var evt = (e ? e : window.event); | ||||||
|  |             var pos = Util.getEventPosition(e, this._target, this._scale); | ||||||
|  |  | ||||||
|  |             /* Stop propagation if inside canvas area */ | ||||||
|  |             if ((pos.realx >= 0) && (pos.realy >= 0) && | ||||||
|  |                 (pos.realx < this._target.offsetWidth) && | ||||||
|  |                 (pos.realy < this._target.offsetHeight)) { | ||||||
|  |                 //Util.Debug("mouse event disabled"); | ||||||
|  |                 Util.stopEvent(e); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return true; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         // Public methods | ||||||
|  |         grab: function () { | ||||||
|  |             var c = this._target; | ||||||
|  |  | ||||||
|  |             if ('ontouchstart' in document.documentElement) { | ||||||
|  |                 Util.addEvent(c, 'touchstart', this._eventHandlers.mousedown); | ||||||
|  |                 Util.addEvent(window, 'touchend', this._eventHandlers.mouseup); | ||||||
|  |                 Util.addEvent(c, 'touchend', this._eventHandlers.mouseup); | ||||||
|  |                 Util.addEvent(c, 'touchmove', this._eventHandlers.mousemove); | ||||||
|  |             } else { | ||||||
|  |                 Util.addEvent(c, 'mousedown', this._eventHandlers.mousedown); | ||||||
|  |                 Util.addEvent(window, 'mouseup', this._eventHandlers.mouseup); | ||||||
|  |                 Util.addEvent(c, 'mouseup', this._eventHandlers.mouseup); | ||||||
|  |                 Util.addEvent(c, 'mousemove', this._eventHandlers.mousemove); | ||||||
|  |                 Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', | ||||||
|  |                               this._eventHandlers.mousewheel); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             /* Work around right and middle click browser behaviors */ | ||||||
|  |             Util.addEvent(document, 'click', this._eventHandlers.mousedisable); | ||||||
|  |             Util.addEvent(document.body, 'contextmenu', this._eventHandlers.mousedisable); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         ungrab: function () { | ||||||
|  |             var c = this._target; | ||||||
|  |  | ||||||
|  |             if ('ontouchstart' in document.documentElement) { | ||||||
|  |                 Util.removeEvent(c, 'touchstart', this._eventHandlers.mousedown); | ||||||
|  |                 Util.removeEvent(window, 'touchend', this._eventHandlers.mouseup); | ||||||
|  |                 Util.removeEvent(c, 'touchend', this._eventHandlers.mouseup); | ||||||
|  |                 Util.removeEvent(c, 'touchmove', this._eventHandlers.mousemove); | ||||||
|  |             } else { | ||||||
|  |                 Util.removeEvent(c, 'mousedown', this._eventHandlers.mousedown); | ||||||
|  |                 Util.removeEvent(window, 'mouseup', this._eventHandlers.mouseup); | ||||||
|  |                 Util.removeEvent(c, 'mouseup', this._eventHandlers.mouseup); | ||||||
|  |                 Util.removeEvent(c, 'mousemove', this._eventHandlers.mousemove); | ||||||
|  |                 Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', | ||||||
|  |                                  this._eventHandlers.mousewheel); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             /* Work around right and middle click browser behaviors */ | ||||||
|  |             Util.removeEvent(document, 'click', this._eventHandlers.mousedisable); | ||||||
|  |             Util.removeEvent(document.body, 'contextmenu', this._eventHandlers.mousedisable); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     Util.make_properties(Mouse, [ | ||||||
|  |         ['target',         'ro', 'dom'],   // DOM element that captures mouse input | ||||||
|  |         ['notify',         'ro', 'func'],  // Function to call to notify whenever a mouse event is received | ||||||
|  |         ['focused',        'rw', 'bool'],  // Capture and send mouse clicks/movement | ||||||
|  |         ['scale',          'rw', 'float'], // Viewport scale factor 0.0 - 1.0 | ||||||
|  |  | ||||||
|  |         ['onMouseButton',  'rw', 'func'],  // Handler for mouse button click/release | ||||||
|  |         ['onMouseMove',    'rw', 'func'],  // Handler for mouse movement | ||||||
|  |         ['touchButton',    'rw', 'int']    // Button mask (1, 2, 4) for touch devices (0 means ignore clicks) | ||||||
|  |     ]); | ||||||
|  | })(); | ||||||
							
								
								
									
										543
									
								
								public/novnc/include/keyboard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,543 @@ | |||||||
|  | var kbdUtil = (function() { | ||||||
|  |     "use strict"; | ||||||
|  |  | ||||||
|  |     function substituteCodepoint(cp) { | ||||||
|  |         // Any Unicode code points which do not have corresponding keysym entries | ||||||
|  |         // can be swapped out for another code point by adding them to this table | ||||||
|  |         var substitutions = { | ||||||
|  |             // {S,s} with comma below -> {S,s} with cedilla | ||||||
|  |             0x218 : 0x15e, | ||||||
|  |             0x219 : 0x15f, | ||||||
|  |             // {T,t} with comma below -> {T,t} with cedilla | ||||||
|  |             0x21a : 0x162, | ||||||
|  |             0x21b : 0x163 | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         var sub = substitutions[cp]; | ||||||
|  |         return sub ? sub : cp; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function isMac() { | ||||||
|  |         return navigator && !!(/mac/i).exec(navigator.platform); | ||||||
|  |     } | ||||||
|  |     function isWindows() { | ||||||
|  |         return navigator && !!(/win/i).exec(navigator.platform); | ||||||
|  |     } | ||||||
|  |     function isLinux() { | ||||||
|  |         return navigator && !!(/linux/i).exec(navigator.platform); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Return true if a modifier which is not the specified char modifier (and is not shift) is down | ||||||
|  |     function hasShortcutModifier(charModifier, currentModifiers) { | ||||||
|  |         var mods = {}; | ||||||
|  |         for (var key in currentModifiers) { | ||||||
|  |             if (parseInt(key) !== XK_Shift_L) { | ||||||
|  |                 mods[key] = currentModifiers[key]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var sum = 0; | ||||||
|  |         for (var k in currentModifiers) { | ||||||
|  |             if (mods[k]) { | ||||||
|  |                 ++sum; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (hasCharModifier(charModifier, mods)) { | ||||||
|  |             return sum > charModifier.length; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             return sum > 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Return true if the specified char modifier is currently down | ||||||
|  |     function hasCharModifier(charModifier, currentModifiers) { | ||||||
|  |         if (charModifier.length === 0) { return false; } | ||||||
|  |  | ||||||
|  |         for (var i = 0; i < charModifier.length; ++i) { | ||||||
|  |             if (!currentModifiers[charModifier[i]]) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Helper object tracking modifier key state | ||||||
|  |     // and generates fake key events to compensate if it gets out of sync | ||||||
|  |     function ModifierSync(charModifier) { | ||||||
|  |         if (!charModifier) { | ||||||
|  |             if (isMac()) { | ||||||
|  |                 // on Mac, Option (AKA Alt) is used as a char modifier | ||||||
|  |                 charModifier = [XK_Alt_L]; | ||||||
|  |             } | ||||||
|  |             else if (isWindows()) { | ||||||
|  |                 // on Windows, Ctrl+Alt is used as a char modifier | ||||||
|  |                 charModifier = [XK_Alt_L, XK_Control_L]; | ||||||
|  |             } | ||||||
|  |             else if (isLinux()) { | ||||||
|  |                 // on Linux, ISO Level 3 Shift (AltGr) is used as a char modifier | ||||||
|  |                 charModifier = [XK_ISO_Level3_Shift]; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 charModifier = []; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var state = {}; | ||||||
|  |         state[XK_Control_L] = false; | ||||||
|  |         state[XK_Alt_L] = false; | ||||||
|  |         state[XK_ISO_Level3_Shift] = false; | ||||||
|  |         state[XK_Shift_L] = false; | ||||||
|  |         state[XK_Meta_L] = false; | ||||||
|  |  | ||||||
|  |         function sync(evt, keysym) { | ||||||
|  |             var result = []; | ||||||
|  |             function syncKey(keysym) { | ||||||
|  |                 return {keysym: keysyms.lookup(keysym), type: state[keysym] ? 'keydown' : 'keyup'}; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (evt.ctrlKey !== undefined && | ||||||
|  |                 evt.ctrlKey !== state[XK_Control_L] && keysym !== XK_Control_L) { | ||||||
|  |                 state[XK_Control_L] = evt.ctrlKey; | ||||||
|  |                 result.push(syncKey(XK_Control_L)); | ||||||
|  |             } | ||||||
|  |             if (evt.altKey !== undefined && | ||||||
|  |                 evt.altKey !== state[XK_Alt_L] && keysym !== XK_Alt_L) { | ||||||
|  |                 state[XK_Alt_L] = evt.altKey; | ||||||
|  |                 result.push(syncKey(XK_Alt_L)); | ||||||
|  |             } | ||||||
|  |             if (evt.altGraphKey !== undefined && | ||||||
|  |                 evt.altGraphKey !== state[XK_ISO_Level3_Shift] && keysym !== XK_ISO_Level3_Shift) { | ||||||
|  |                 state[XK_ISO_Level3_Shift] = evt.altGraphKey; | ||||||
|  |                 result.push(syncKey(XK_ISO_Level3_Shift)); | ||||||
|  |             } | ||||||
|  |             if (evt.shiftKey !== undefined && | ||||||
|  |                 evt.shiftKey !== state[XK_Shift_L] && keysym !== XK_Shift_L) { | ||||||
|  |                 state[XK_Shift_L] = evt.shiftKey; | ||||||
|  |                 result.push(syncKey(XK_Shift_L)); | ||||||
|  |             } | ||||||
|  |             if (evt.metaKey !== undefined && | ||||||
|  |                 evt.metaKey !== state[XK_Meta_L] && keysym !== XK_Meta_L) { | ||||||
|  |                 state[XK_Meta_L] = evt.metaKey; | ||||||
|  |                 result.push(syncKey(XK_Meta_L)); | ||||||
|  |             } | ||||||
|  |             return result; | ||||||
|  |         } | ||||||
|  |         function syncKeyEvent(evt, down) { | ||||||
|  |             var obj = getKeysym(evt); | ||||||
|  |             var keysym = obj ? obj.keysym : null; | ||||||
|  |  | ||||||
|  |             // first, apply the event itself, if relevant | ||||||
|  |             if (keysym !== null && state[keysym] !== undefined) { | ||||||
|  |                 state[keysym] = down; | ||||||
|  |             } | ||||||
|  |             return sync(evt, keysym); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return { | ||||||
|  |             // sync on the appropriate keyboard event | ||||||
|  |             keydown: function(evt) { return syncKeyEvent(evt, true);}, | ||||||
|  |             keyup: function(evt) { return syncKeyEvent(evt, false);}, | ||||||
|  |             // Call this with a non-keyboard event (such as mouse events) to use its modifier state to synchronize anyway | ||||||
|  |             syncAny: function(evt) { return sync(evt);}, | ||||||
|  |  | ||||||
|  |             // is a shortcut modifier down? | ||||||
|  |             hasShortcutModifier: function() { return hasShortcutModifier(charModifier, state); }, | ||||||
|  |             // if a char modifier is down, return the keys it consists of, otherwise return null | ||||||
|  |             activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; } | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Get a key ID from a keyboard event | ||||||
|  |     // May be a string or an integer depending on the available properties | ||||||
|  |     function getKey(evt){ | ||||||
|  |         if ('keyCode' in evt && 'key' in evt) { | ||||||
|  |             return evt.key + ':' + evt.keyCode; | ||||||
|  |         } | ||||||
|  |         else if ('keyCode' in evt) { | ||||||
|  |             return evt.keyCode; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             return evt.key; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Get the most reliable keysym value we can get from a key event | ||||||
|  |     // if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which | ||||||
|  |     function getKeysym(evt){ | ||||||
|  |         var codepoint; | ||||||
|  |         if (evt.char && evt.char.length === 1) { | ||||||
|  |             codepoint = evt.char.charCodeAt(); | ||||||
|  |         } | ||||||
|  |         else if (evt.charCode) { | ||||||
|  |             codepoint = evt.charCode; | ||||||
|  |         } | ||||||
|  |         else if (evt.keyCode && evt.type === 'keypress') { | ||||||
|  |             // IE10 stores the char code as keyCode, and has no other useful properties | ||||||
|  |             codepoint = evt.keyCode; | ||||||
|  |         } | ||||||
|  |         if (codepoint) { | ||||||
|  |             var res = keysyms.fromUnicode(substituteCodepoint(codepoint)); | ||||||
|  |             if (res) { | ||||||
|  |                 return res; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         // we could check evt.key here. | ||||||
|  |         // Legal values are defined in http://www.w3.org/TR/DOM-Level-3-Events/#key-values-list, | ||||||
|  |         // so we "just" need to map them to keysym, but AFAIK this is only available in IE10, which also provides evt.key | ||||||
|  |         // so we don't *need* it yet | ||||||
|  |         if (evt.keyCode) { | ||||||
|  |             return keysyms.lookup(keysymFromKeyCode(evt.keyCode, evt.shiftKey)); | ||||||
|  |         } | ||||||
|  |         if (evt.which) { | ||||||
|  |             return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey)); | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Given a keycode, try to predict which keysym it might be. | ||||||
|  |     // If the keycode is unknown, null is returned. | ||||||
|  |     function keysymFromKeyCode(keycode, shiftPressed) { | ||||||
|  |         if (typeof(keycode) !== 'number') { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         // won't be accurate for azerty | ||||||
|  |         if (keycode >= 0x30 && keycode <= 0x39) { | ||||||
|  |             return keycode; // digit | ||||||
|  |         } | ||||||
|  |         if (keycode >= 0x41 && keycode <= 0x5a) { | ||||||
|  |             // remap to lowercase unless shift is down | ||||||
|  |             return shiftPressed ? keycode : keycode + 32; // A-Z | ||||||
|  |         } | ||||||
|  |         if (keycode >= 0x60 && keycode <= 0x69) { | ||||||
|  |             return XK_KP_0 + (keycode - 0x60); // numpad 0-9 | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         switch(keycode) { | ||||||
|  |             case 0x20: return XK_space; | ||||||
|  |             case 0x6a: return XK_KP_Multiply; | ||||||
|  |             case 0x6b: return XK_KP_Add; | ||||||
|  |             case 0x6c: return XK_KP_Separator; | ||||||
|  |             case 0x6d: return XK_KP_Subtract; | ||||||
|  |             case 0x6e: return XK_KP_Decimal; | ||||||
|  |             case 0x6f: return XK_KP_Divide; | ||||||
|  |             case 0xbb: return XK_plus; | ||||||
|  |             case 0xbc: return XK_comma; | ||||||
|  |             case 0xbd: return XK_minus; | ||||||
|  |             case 0xbe: return XK_period; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return nonCharacterKey({keyCode: keycode}); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // if the key is a known non-character key (any key which doesn't generate character data) | ||||||
|  |     // return its keysym value. Otherwise return null | ||||||
|  |     function nonCharacterKey(evt) { | ||||||
|  |         // evt.key not implemented yet | ||||||
|  |         if (!evt.keyCode) { return null; } | ||||||
|  |         var keycode = evt.keyCode; | ||||||
|  |  | ||||||
|  |         if (keycode >= 0x70 && keycode <= 0x87) { | ||||||
|  |             return XK_F1 + keycode - 0x70; // F1-F24 | ||||||
|  |         } | ||||||
|  |         switch (keycode) { | ||||||
|  |  | ||||||
|  |             case 8 : return XK_BackSpace; | ||||||
|  |             case 13 : return XK_Return; | ||||||
|  |  | ||||||
|  |             case 9 : return XK_Tab; | ||||||
|  |  | ||||||
|  |             case 27 : return XK_Escape; | ||||||
|  |             case 46 : return XK_Delete; | ||||||
|  |  | ||||||
|  |             case 36 : return XK_Home; | ||||||
|  |             case 35 : return XK_End; | ||||||
|  |             case 33 : return XK_Page_Up; | ||||||
|  |             case 34 : return XK_Page_Down; | ||||||
|  |             case 45 : return XK_Insert; | ||||||
|  |  | ||||||
|  |             case 37 : return XK_Left; | ||||||
|  |             case 38 : return XK_Up; | ||||||
|  |             case 39 : return XK_Right; | ||||||
|  |             case 40 : return XK_Down; | ||||||
|  |  | ||||||
|  |             case 16 : return XK_Shift_L; | ||||||
|  |             case 17 : return XK_Control_L; | ||||||
|  |             case 18 : return XK_Alt_L; // also: Option-key on Mac | ||||||
|  |  | ||||||
|  |             case 224 : return XK_Meta_L; | ||||||
|  |             case 225 : return XK_ISO_Level3_Shift; // AltGr | ||||||
|  |             case 91 : return XK_Super_L; // also: Windows-key | ||||||
|  |             case 92 : return XK_Super_R; // also: Windows-key | ||||||
|  |             case 93 : return XK_Menu; // also: Windows-Menu, Command on Mac | ||||||
|  |             default: return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return { | ||||||
|  |         hasShortcutModifier : hasShortcutModifier, | ||||||
|  |         hasCharModifier : hasCharModifier, | ||||||
|  |         ModifierSync : ModifierSync, | ||||||
|  |         getKey : getKey, | ||||||
|  |         getKeysym : getKeysym, | ||||||
|  |         keysymFromKeyCode : keysymFromKeyCode, | ||||||
|  |         nonCharacterKey : nonCharacterKey, | ||||||
|  |         substituteCodepoint : substituteCodepoint | ||||||
|  |     }; | ||||||
|  | })(); | ||||||
|  |  | ||||||
|  | // Takes a DOM keyboard event and: | ||||||
|  | // - determines which keysym it represents | ||||||
|  | // - determines a keyId  identifying the key that was pressed (corresponding to the key/keyCode properties on the DOM event) | ||||||
|  | // - synthesizes events to synchronize modifier key state between which modifiers are actually down, and which we thought were down | ||||||
|  | // - marks each event with an 'escape' property if a modifier was down which should be "escaped" | ||||||
|  | // - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown | ||||||
|  | // This information is collected into an object which is passed to the next() function. (one call per event) | ||||||
|  | function KeyEventDecoder(modifierState, next) { | ||||||
|  |     "use strict"; | ||||||
|  |     function sendAll(evts) { | ||||||
|  |         for (var i = 0; i < evts.length; ++i) { | ||||||
|  |             next(evts[i]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     function process(evt, type) { | ||||||
|  |         var result = {type: type}; | ||||||
|  |         var keyId = kbdUtil.getKey(evt); | ||||||
|  |         if (keyId) { | ||||||
|  |             result.keyId = keyId; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var keysym = kbdUtil.getKeysym(evt); | ||||||
|  |  | ||||||
|  |         var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier(); | ||||||
|  |         // Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress? | ||||||
|  |         // "special" keys like enter, tab or backspace don't send keypress events, | ||||||
|  |         // and some browsers don't send keypresses at all if a modifier is down | ||||||
|  |         if (keysym && (type !== 'keydown' || kbdUtil.nonCharacterKey(evt) || hasModifier)) { | ||||||
|  |             result.keysym = keysym; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var isShift = evt.keyCode === 0x10 || evt.key === 'Shift'; | ||||||
|  |  | ||||||
|  |         // Should we prevent the browser from handling the event? | ||||||
|  |         // Doing so on a keydown (in most browsers) prevents keypress from being generated | ||||||
|  |         // so only do that if we have to. | ||||||
|  |         var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!kbdUtil.nonCharacterKey(evt)); | ||||||
|  |  | ||||||
|  |         // If a char modifier is down on a keydown, we need to insert a stall, | ||||||
|  |         // so VerifyCharModifier knows to wait and see if a keypress is comnig | ||||||
|  |         var stall = type === 'keydown' && modifierState.activeCharModifier() && !kbdUtil.nonCharacterKey(evt); | ||||||
|  |  | ||||||
|  |         // if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt) | ||||||
|  |         var active = modifierState.activeCharModifier(); | ||||||
|  |  | ||||||
|  |         // If we have a char modifier down, and we're able to determine a keysym reliably | ||||||
|  |         // then (a) we know to treat the modifier as a char modifier, | ||||||
|  |         // and (b) we'll have to "escape" the modifier to undo the modifier when sending the char. | ||||||
|  |         if (active && keysym) { | ||||||
|  |             var isCharModifier = false; | ||||||
|  |             for (var i  = 0; i < active.length; ++i) { | ||||||
|  |                 if (active[i] === keysym.keysym) { | ||||||
|  |                     isCharModifier = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (type === 'keypress' && !isCharModifier) { | ||||||
|  |                 result.escape = modifierState.activeCharModifier(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (stall) { | ||||||
|  |             // insert a fake "stall" event | ||||||
|  |             next({type: 'stall'}); | ||||||
|  |         } | ||||||
|  |         next(result); | ||||||
|  |  | ||||||
|  |         return suppress; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         keydown: function(evt) { | ||||||
|  |             sendAll(modifierState.keydown(evt)); | ||||||
|  |             return process(evt, 'keydown'); | ||||||
|  |         }, | ||||||
|  |         keypress: function(evt) { | ||||||
|  |             return process(evt, 'keypress'); | ||||||
|  |         }, | ||||||
|  |         keyup: function(evt) { | ||||||
|  |             sendAll(modifierState.keyup(evt)); | ||||||
|  |             return process(evt, 'keyup'); | ||||||
|  |         }, | ||||||
|  |         syncModifiers: function(evt) { | ||||||
|  |             sendAll(modifierState.syncAny(evt)); | ||||||
|  |         }, | ||||||
|  |         releaseAll: function() { next({type: 'releaseall'}); } | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Combines keydown and keypress events where necessary to handle char modifiers. | ||||||
|  | // On some OS'es, a char modifier is sometimes used as a shortcut modifier. | ||||||
|  | // For example, on Windows, AltGr is synonymous with Ctrl-Alt. On a Danish keyboard layout, AltGr-2 yields a @, but Ctrl-Alt-D does nothing | ||||||
|  | // so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not. | ||||||
|  | // The only way we can distinguish these cases is to wait and see if a keypress event arrives | ||||||
|  | // When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two | ||||||
|  | function VerifyCharModifier(next) { | ||||||
|  |     "use strict"; | ||||||
|  |     var queue = []; | ||||||
|  |     var timer = null; | ||||||
|  |     function process() { | ||||||
|  |         if (timer) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var delayProcess = function () { | ||||||
|  |             clearTimeout(timer); | ||||||
|  |             timer = null; | ||||||
|  |             process(); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         while (queue.length !== 0) { | ||||||
|  |             var cur = queue[0]; | ||||||
|  |             queue = queue.splice(1); | ||||||
|  |             switch (cur.type) { | ||||||
|  |             case 'stall': | ||||||
|  |                 // insert a delay before processing available events. | ||||||
|  |                 /* jshint loopfunc: true */ | ||||||
|  |                 timer = setTimeout(delayProcess, 5); | ||||||
|  |                 /* jshint loopfunc: false */ | ||||||
|  |                 return; | ||||||
|  |             case 'keydown': | ||||||
|  |                 // is the next element a keypress? Then we should merge the two | ||||||
|  |                 if (queue.length !== 0 && queue[0].type === 'keypress') { | ||||||
|  |                     // Firefox sends keypress even when no char is generated. | ||||||
|  |                     // so, if keypress keysym is the same as we'd have guessed from keydown, | ||||||
|  |                     // the modifier didn't have any effect, and should not be escaped | ||||||
|  |                     if (queue[0].escape && (!cur.keysym || cur.keysym.keysym !== queue[0].keysym.keysym)) { | ||||||
|  |                         cur.escape = queue[0].escape; | ||||||
|  |                     } | ||||||
|  |                     cur.keysym = queue[0].keysym; | ||||||
|  |                     queue = queue.splice(1); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // swallow stall events, and pass all others to the next stage | ||||||
|  |             if (cur.type !== 'stall') { | ||||||
|  |                 next(cur); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return function(evt) { | ||||||
|  |         queue.push(evt); | ||||||
|  |         process(); | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Keeps track of which keys we (and the server) believe are down | ||||||
|  | // When a keyup is received, match it against this list, to determine the corresponding keysym(s) | ||||||
|  | // in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars | ||||||
|  | // key repeat events should be merged into a single entry. | ||||||
|  | // Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess | ||||||
|  | function TrackKeyState(next) { | ||||||
|  |     "use strict"; | ||||||
|  |     var state = []; | ||||||
|  |  | ||||||
|  |     return function (evt) { | ||||||
|  |         var last = state.length !== 0 ? state[state.length-1] : null; | ||||||
|  |  | ||||||
|  |         switch (evt.type) { | ||||||
|  |         case 'keydown': | ||||||
|  |             // insert a new entry if last seen key was different. | ||||||
|  |             if (!last || !evt.keyId || last.keyId !== evt.keyId) { | ||||||
|  |                 last = {keyId: evt.keyId, keysyms: {}}; | ||||||
|  |                 state.push(last); | ||||||
|  |             } | ||||||
|  |             if (evt.keysym) { | ||||||
|  |                 // make sure last event contains this keysym (a single "logical" keyevent | ||||||
|  |                 // can cause multiple key events to be sent to the VNC server) | ||||||
|  |                 last.keysyms[evt.keysym.keysym] = evt.keysym; | ||||||
|  |                 last.ignoreKeyPress = true; | ||||||
|  |                 next(evt); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 'keypress': | ||||||
|  |             if (!last) { | ||||||
|  |                 last = {keyId: evt.keyId, keysyms: {}}; | ||||||
|  |                 state.push(last); | ||||||
|  |             } | ||||||
|  |             if (!evt.keysym) { | ||||||
|  |                 console.log('keypress with no keysym:', evt); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // If we didn't expect a keypress, and already sent a keydown to the VNC server | ||||||
|  |             // based on the keydown, make sure to skip this event. | ||||||
|  |             if (evt.keysym && !last.ignoreKeyPress) { | ||||||
|  |                 last.keysyms[evt.keysym.keysym] = evt.keysym; | ||||||
|  |                 evt.type = 'keydown'; | ||||||
|  |                 next(evt); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 'keyup': | ||||||
|  |             if (state.length === 0) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             var idx = null; | ||||||
|  |             // do we have a matching key tracked as being down? | ||||||
|  |             for (var i = 0; i !== state.length; ++i) { | ||||||
|  |                 if (state[i].keyId === evt.keyId) { | ||||||
|  |                     idx = i; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             // if we couldn't find a match (it happens), assume it was the last key pressed | ||||||
|  |             if (idx === null) { | ||||||
|  |                 idx = state.length - 1; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var item = state.splice(idx, 1)[0]; | ||||||
|  |             // for each keysym tracked by this key entry, clone the current event and override the keysym | ||||||
|  |             var clone = (function(){ | ||||||
|  |                 function Clone(){} | ||||||
|  |                 return function (obj) { Clone.prototype=obj; return new Clone(); }; | ||||||
|  |             }()); | ||||||
|  |             for (var key in item.keysyms) { | ||||||
|  |                 var out = clone(evt); | ||||||
|  |                 out.keysym = item.keysyms[key]; | ||||||
|  |                 next(out); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 'releaseall': | ||||||
|  |             /* jshint shadow: true */ | ||||||
|  |             for (var i = 0; i < state.length; ++i) { | ||||||
|  |                 for (var key in state[i].keysyms) { | ||||||
|  |                     var keysym = state[i].keysyms[key]; | ||||||
|  |                     next({keyId: 0, keysym: keysym, type: 'keyup'}); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             /* jshint shadow: false */ | ||||||
|  |             state = []; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @), | ||||||
|  | // then the modifier must be "undone" before sending the @, and "redone" afterwards. | ||||||
|  | function EscapeModifiers(next) { | ||||||
|  |     "use strict"; | ||||||
|  |     return function(evt) { | ||||||
|  |         if (evt.type !== 'keydown' || evt.escape === undefined) { | ||||||
|  |             next(evt); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         // undo modifiers | ||||||
|  |         for (var i = 0; i < evt.escape.length; ++i) { | ||||||
|  |             next({type: 'keyup', keyId: 0, keysym: keysyms.lookup(evt.escape[i])}); | ||||||
|  |         } | ||||||
|  |         // send the character event | ||||||
|  |         next(evt); | ||||||
|  |         // redo modifiers | ||||||
|  |         /* jshint shadow: true */ | ||||||
|  |         for (var i = 0; i < evt.escape.length; ++i) { | ||||||
|  |             next({type: 'keydown', keyId: 0, keysym: keysyms.lookup(evt.escape[i])}); | ||||||
|  |         } | ||||||
|  |         /* jshint shadow: false */ | ||||||
|  |     }; | ||||||
|  | } | ||||||
							
								
								
									
										378
									
								
								public/novnc/include/keysym.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,378 @@ | |||||||
|  | var XK_VoidSymbol =                0xffffff, /* Void symbol */ | ||||||
|  |  | ||||||
|  | XK_BackSpace =                   0xff08, /* Back space, back char */ | ||||||
|  | XK_Tab =                         0xff09, | ||||||
|  | XK_Linefeed =                    0xff0a, /* Linefeed, LF */ | ||||||
|  | XK_Clear =                       0xff0b, | ||||||
|  | XK_Return =                      0xff0d, /* Return, enter */ | ||||||
|  | XK_Pause =                       0xff13, /* Pause, hold */ | ||||||
|  | XK_Scroll_Lock =                 0xff14, | ||||||
|  | XK_Sys_Req =                     0xff15, | ||||||
|  | XK_Escape =                      0xff1b, | ||||||
|  | XK_Delete =                      0xffff, /* Delete, rubout */ | ||||||
|  |  | ||||||
|  | /* Cursor control & motion */ | ||||||
|  |  | ||||||
|  | XK_Home =                        0xff50, | ||||||
|  | XK_Left =                        0xff51, /* Move left, left arrow */ | ||||||
|  | XK_Up =                          0xff52, /* Move up, up arrow */ | ||||||
|  | XK_Right =                       0xff53, /* Move right, right arrow */ | ||||||
|  | XK_Down =                        0xff54, /* Move down, down arrow */ | ||||||
|  | XK_Prior =                       0xff55, /* Prior, previous */ | ||||||
|  | XK_Page_Up =                     0xff55, | ||||||
|  | XK_Next =                        0xff56, /* Next */ | ||||||
|  | XK_Page_Down =                   0xff56, | ||||||
|  | XK_End =                         0xff57, /* EOL */ | ||||||
|  | XK_Begin =                       0xff58, /* BOL */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Misc functions */ | ||||||
|  |  | ||||||
|  | XK_Select =                      0xff60, /* Select, mark */ | ||||||
|  | XK_Print =                       0xff61, | ||||||
|  | XK_Execute =                     0xff62, /* Execute, run, do */ | ||||||
|  | XK_Insert =                      0xff63, /* Insert, insert here */ | ||||||
|  | XK_Undo =                        0xff65, | ||||||
|  | XK_Redo =                        0xff66, /* Redo, again */ | ||||||
|  | XK_Menu =                        0xff67, | ||||||
|  | XK_Find =                        0xff68, /* Find, search */ | ||||||
|  | XK_Cancel =                      0xff69, /* Cancel, stop, abort, exit */ | ||||||
|  | XK_Help =                        0xff6a, /* Help */ | ||||||
|  | XK_Break =                       0xff6b, | ||||||
|  | XK_Mode_switch =                 0xff7e, /* Character set switch */ | ||||||
|  | XK_script_switch =               0xff7e, /* Alias for mode_switch */ | ||||||
|  | XK_Num_Lock =                    0xff7f, | ||||||
|  |  | ||||||
|  | /* Keypad functions, keypad numbers cleverly chosen to map to ASCII */ | ||||||
|  |  | ||||||
|  | XK_KP_Space =                    0xff80, /* Space */ | ||||||
|  | XK_KP_Tab =                      0xff89, | ||||||
|  | XK_KP_Enter =                    0xff8d, /* Enter */ | ||||||
|  | XK_KP_F1 =                       0xff91, /* PF1, KP_A, ... */ | ||||||
|  | XK_KP_F2 =                       0xff92, | ||||||
|  | XK_KP_F3 =                       0xff93, | ||||||
|  | XK_KP_F4 =                       0xff94, | ||||||
|  | XK_KP_Home =                     0xff95, | ||||||
|  | XK_KP_Left =                     0xff96, | ||||||
|  | XK_KP_Up =                       0xff97, | ||||||
|  | XK_KP_Right =                    0xff98, | ||||||
|  | XK_KP_Down =                     0xff99, | ||||||
|  | XK_KP_Prior =                    0xff9a, | ||||||
|  | XK_KP_Page_Up =                  0xff9a, | ||||||
|  | XK_KP_Next =                     0xff9b, | ||||||
|  | XK_KP_Page_Down =                0xff9b, | ||||||
|  | XK_KP_End =                      0xff9c, | ||||||
|  | XK_KP_Begin =                    0xff9d, | ||||||
|  | XK_KP_Insert =                   0xff9e, | ||||||
|  | XK_KP_Delete =                   0xff9f, | ||||||
|  | XK_KP_Equal =                    0xffbd, /* Equals */ | ||||||
|  | XK_KP_Multiply =                 0xffaa, | ||||||
|  | XK_KP_Add =                      0xffab, | ||||||
|  | XK_KP_Separator =                0xffac, /* Separator, often comma */ | ||||||
|  | XK_KP_Subtract =                 0xffad, | ||||||
|  | XK_KP_Decimal =                  0xffae, | ||||||
|  | XK_KP_Divide =                   0xffaf, | ||||||
|  |  | ||||||
|  | XK_KP_0 =                        0xffb0, | ||||||
|  | XK_KP_1 =                        0xffb1, | ||||||
|  | XK_KP_2 =                        0xffb2, | ||||||
|  | XK_KP_3 =                        0xffb3, | ||||||
|  | XK_KP_4 =                        0xffb4, | ||||||
|  | XK_KP_5 =                        0xffb5, | ||||||
|  | XK_KP_6 =                        0xffb6, | ||||||
|  | XK_KP_7 =                        0xffb7, | ||||||
|  | XK_KP_8 =                        0xffb8, | ||||||
|  | XK_KP_9 =                        0xffb9, | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Auxiliary functions; note the duplicate definitions for left and right | ||||||
|  |  * function keys;  Sun keyboards and a few other manufacturers have such | ||||||
|  |  * function key groups on the left and/or right sides of the keyboard. | ||||||
|  |  * We've not found a keyboard with more than 35 function keys total. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | XK_F1 =                          0xffbe, | ||||||
|  | XK_F2 =                          0xffbf, | ||||||
|  | XK_F3 =                          0xffc0, | ||||||
|  | XK_F4 =                          0xffc1, | ||||||
|  | XK_F5 =                          0xffc2, | ||||||
|  | XK_F6 =                          0xffc3, | ||||||
|  | XK_F7 =                          0xffc4, | ||||||
|  | XK_F8 =                          0xffc5, | ||||||
|  | XK_F9 =                          0xffc6, | ||||||
|  | XK_F10 =                         0xffc7, | ||||||
|  | XK_F11 =                         0xffc8, | ||||||
|  | XK_L1 =                          0xffc8, | ||||||
|  | XK_F12 =                         0xffc9, | ||||||
|  | XK_L2 =                          0xffc9, | ||||||
|  | XK_F13 =                         0xffca, | ||||||
|  | XK_L3 =                          0xffca, | ||||||
|  | XK_F14 =                         0xffcb, | ||||||
|  | XK_L4 =                          0xffcb, | ||||||
|  | XK_F15 =                         0xffcc, | ||||||
|  | XK_L5 =                          0xffcc, | ||||||
|  | XK_F16 =                         0xffcd, | ||||||
|  | XK_L6 =                          0xffcd, | ||||||
|  | XK_F17 =                         0xffce, | ||||||
|  | XK_L7 =                          0xffce, | ||||||
|  | XK_F18 =                         0xffcf, | ||||||
|  | XK_L8 =                          0xffcf, | ||||||
|  | XK_F19 =                         0xffd0, | ||||||
|  | XK_L9 =                          0xffd0, | ||||||
|  | XK_F20 =                         0xffd1, | ||||||
|  | XK_L10 =                         0xffd1, | ||||||
|  | XK_F21 =                         0xffd2, | ||||||
|  | XK_R1 =                          0xffd2, | ||||||
|  | XK_F22 =                         0xffd3, | ||||||
|  | XK_R2 =                          0xffd3, | ||||||
|  | XK_F23 =                         0xffd4, | ||||||
|  | XK_R3 =                          0xffd4, | ||||||
|  | XK_F24 =                         0xffd5, | ||||||
|  | XK_R4 =                          0xffd5, | ||||||
|  | XK_F25 =                         0xffd6, | ||||||
|  | XK_R5 =                          0xffd6, | ||||||
|  | XK_F26 =                         0xffd7, | ||||||
|  | XK_R6 =                          0xffd7, | ||||||
|  | XK_F27 =                         0xffd8, | ||||||
|  | XK_R7 =                          0xffd8, | ||||||
|  | XK_F28 =                         0xffd9, | ||||||
|  | XK_R8 =                          0xffd9, | ||||||
|  | XK_F29 =                         0xffda, | ||||||
|  | XK_R9 =                          0xffda, | ||||||
|  | XK_F30 =                         0xffdb, | ||||||
|  | XK_R10 =                         0xffdb, | ||||||
|  | XK_F31 =                         0xffdc, | ||||||
|  | XK_R11 =                         0xffdc, | ||||||
|  | XK_F32 =                         0xffdd, | ||||||
|  | XK_R12 =                         0xffdd, | ||||||
|  | XK_F33 =                         0xffde, | ||||||
|  | XK_R13 =                         0xffde, | ||||||
|  | XK_F34 =                         0xffdf, | ||||||
|  | XK_R14 =                         0xffdf, | ||||||
|  | XK_F35 =                         0xffe0, | ||||||
|  | XK_R15 =                         0xffe0, | ||||||
|  |  | ||||||
|  | /* Modifiers */ | ||||||
|  |  | ||||||
|  | XK_Shift_L =                     0xffe1, /* Left shift */ | ||||||
|  | XK_Shift_R =                     0xffe2, /* Right shift */ | ||||||
|  | XK_Control_L =                   0xffe3, /* Left control */ | ||||||
|  | XK_Control_R =                   0xffe4, /* Right control */ | ||||||
|  | XK_Caps_Lock =                   0xffe5, /* Caps lock */ | ||||||
|  | XK_Shift_Lock =                  0xffe6, /* Shift lock */ | ||||||
|  |  | ||||||
|  | XK_Meta_L =                      0xffe7, /* Left meta */ | ||||||
|  | XK_Meta_R =                      0xffe8, /* Right meta */ | ||||||
|  | XK_Alt_L =                       0xffe9, /* Left alt */ | ||||||
|  | XK_Alt_R =                       0xffea, /* Right alt */ | ||||||
|  | XK_Super_L =                     0xffeb, /* Left super */ | ||||||
|  | XK_Super_R =                     0xffec, /* Right super */ | ||||||
|  | XK_Hyper_L =                     0xffed, /* Left hyper */ | ||||||
|  | XK_Hyper_R =                     0xffee, /* Right hyper */ | ||||||
|  |  | ||||||
|  | XK_ISO_Level3_Shift =            0xfe03, /* AltGr */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Latin 1 | ||||||
|  |  * (ISO/IEC 8859-1 = Unicode U+0020..U+00FF) | ||||||
|  |  * Byte 3 = 0 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | XK_space =                       0x0020, /* U+0020 SPACE */ | ||||||
|  | XK_exclam =                      0x0021, /* U+0021 EXCLAMATION MARK */ | ||||||
|  | XK_quotedbl =                    0x0022, /* U+0022 QUOTATION MARK */ | ||||||
|  | XK_numbersign =                  0x0023, /* U+0023 NUMBER SIGN */ | ||||||
|  | XK_dollar =                      0x0024, /* U+0024 DOLLAR SIGN */ | ||||||
|  | XK_percent =                     0x0025, /* U+0025 PERCENT SIGN */ | ||||||
|  | XK_ampersand =                   0x0026, /* U+0026 AMPERSAND */ | ||||||
|  | XK_apostrophe =                  0x0027, /* U+0027 APOSTROPHE */ | ||||||
|  | XK_quoteright =                  0x0027, /* deprecated */ | ||||||
|  | XK_parenleft =                   0x0028, /* U+0028 LEFT PARENTHESIS */ | ||||||
|  | XK_parenright =                  0x0029, /* U+0029 RIGHT PARENTHESIS */ | ||||||
|  | XK_asterisk =                    0x002a, /* U+002A ASTERISK */ | ||||||
|  | XK_plus =                        0x002b, /* U+002B PLUS SIGN */ | ||||||
|  | XK_comma =                       0x002c, /* U+002C COMMA */ | ||||||
|  | XK_minus =                       0x002d, /* U+002D HYPHEN-MINUS */ | ||||||
|  | XK_period =                      0x002e, /* U+002E FULL STOP */ | ||||||
|  | XK_slash =                       0x002f, /* U+002F SOLIDUS */ | ||||||
|  | XK_0 =                           0x0030, /* U+0030 DIGIT ZERO */ | ||||||
|  | XK_1 =                           0x0031, /* U+0031 DIGIT ONE */ | ||||||
|  | XK_2 =                           0x0032, /* U+0032 DIGIT TWO */ | ||||||
|  | XK_3 =                           0x0033, /* U+0033 DIGIT THREE */ | ||||||
|  | XK_4 =                           0x0034, /* U+0034 DIGIT FOUR */ | ||||||
|  | XK_5 =                           0x0035, /* U+0035 DIGIT FIVE */ | ||||||
|  | XK_6 =                           0x0036, /* U+0036 DIGIT SIX */ | ||||||
|  | XK_7 =                           0x0037, /* U+0037 DIGIT SEVEN */ | ||||||
|  | XK_8 =                           0x0038, /* U+0038 DIGIT EIGHT */ | ||||||
|  | XK_9 =                           0x0039, /* U+0039 DIGIT NINE */ | ||||||
|  | XK_colon =                       0x003a, /* U+003A COLON */ | ||||||
|  | XK_semicolon =                   0x003b, /* U+003B SEMICOLON */ | ||||||
|  | XK_less =                        0x003c, /* U+003C LESS-THAN SIGN */ | ||||||
|  | XK_equal =                       0x003d, /* U+003D EQUALS SIGN */ | ||||||
|  | XK_greater =                     0x003e, /* U+003E GREATER-THAN SIGN */ | ||||||
|  | XK_question =                    0x003f, /* U+003F QUESTION MARK */ | ||||||
|  | XK_at =                          0x0040, /* U+0040 COMMERCIAL AT */ | ||||||
|  | XK_A =                           0x0041, /* U+0041 LATIN CAPITAL LETTER A */ | ||||||
|  | XK_B =                           0x0042, /* U+0042 LATIN CAPITAL LETTER B */ | ||||||
|  | XK_C =                           0x0043, /* U+0043 LATIN CAPITAL LETTER C */ | ||||||
|  | XK_D =                           0x0044, /* U+0044 LATIN CAPITAL LETTER D */ | ||||||
|  | XK_E =                           0x0045, /* U+0045 LATIN CAPITAL LETTER E */ | ||||||
|  | XK_F =                           0x0046, /* U+0046 LATIN CAPITAL LETTER F */ | ||||||
|  | XK_G =                           0x0047, /* U+0047 LATIN CAPITAL LETTER G */ | ||||||
|  | XK_H =                           0x0048, /* U+0048 LATIN CAPITAL LETTER H */ | ||||||
|  | XK_I =                           0x0049, /* U+0049 LATIN CAPITAL LETTER I */ | ||||||
|  | XK_J =                           0x004a, /* U+004A LATIN CAPITAL LETTER J */ | ||||||
|  | XK_K =                           0x004b, /* U+004B LATIN CAPITAL LETTER K */ | ||||||
|  | XK_L =                           0x004c, /* U+004C LATIN CAPITAL LETTER L */ | ||||||
|  | XK_M =                           0x004d, /* U+004D LATIN CAPITAL LETTER M */ | ||||||
|  | XK_N =                           0x004e, /* U+004E LATIN CAPITAL LETTER N */ | ||||||
|  | XK_O =                           0x004f, /* U+004F LATIN CAPITAL LETTER O */ | ||||||
|  | XK_P =                           0x0050, /* U+0050 LATIN CAPITAL LETTER P */ | ||||||
|  | XK_Q =                           0x0051, /* U+0051 LATIN CAPITAL LETTER Q */ | ||||||
|  | XK_R =                           0x0052, /* U+0052 LATIN CAPITAL LETTER R */ | ||||||
|  | XK_S =                           0x0053, /* U+0053 LATIN CAPITAL LETTER S */ | ||||||
|  | XK_T =                           0x0054, /* U+0054 LATIN CAPITAL LETTER T */ | ||||||
|  | XK_U =                           0x0055, /* U+0055 LATIN CAPITAL LETTER U */ | ||||||
|  | XK_V =                           0x0056, /* U+0056 LATIN CAPITAL LETTER V */ | ||||||
|  | XK_W =                           0x0057, /* U+0057 LATIN CAPITAL LETTER W */ | ||||||
|  | XK_X =                           0x0058, /* U+0058 LATIN CAPITAL LETTER X */ | ||||||
|  | XK_Y =                           0x0059, /* U+0059 LATIN CAPITAL LETTER Y */ | ||||||
|  | XK_Z =                           0x005a, /* U+005A LATIN CAPITAL LETTER Z */ | ||||||
|  | XK_bracketleft =                 0x005b, /* U+005B LEFT SQUARE BRACKET */ | ||||||
|  | XK_backslash =                   0x005c, /* U+005C REVERSE SOLIDUS */ | ||||||
|  | XK_bracketright =                0x005d, /* U+005D RIGHT SQUARE BRACKET */ | ||||||
|  | XK_asciicircum =                 0x005e, /* U+005E CIRCUMFLEX ACCENT */ | ||||||
|  | XK_underscore =                  0x005f, /* U+005F LOW LINE */ | ||||||
|  | XK_grave =                       0x0060, /* U+0060 GRAVE ACCENT */ | ||||||
|  | XK_quoteleft =                   0x0060, /* deprecated */ | ||||||
|  | XK_a =                           0x0061, /* U+0061 LATIN SMALL LETTER A */ | ||||||
|  | XK_b =                           0x0062, /* U+0062 LATIN SMALL LETTER B */ | ||||||
|  | XK_c =                           0x0063, /* U+0063 LATIN SMALL LETTER C */ | ||||||
|  | XK_d =                           0x0064, /* U+0064 LATIN SMALL LETTER D */ | ||||||
|  | XK_e =                           0x0065, /* U+0065 LATIN SMALL LETTER E */ | ||||||
|  | XK_f =                           0x0066, /* U+0066 LATIN SMALL LETTER F */ | ||||||
|  | XK_g =                           0x0067, /* U+0067 LATIN SMALL LETTER G */ | ||||||
|  | XK_h =                           0x0068, /* U+0068 LATIN SMALL LETTER H */ | ||||||
|  | XK_i =                           0x0069, /* U+0069 LATIN SMALL LETTER I */ | ||||||
|  | XK_j =                           0x006a, /* U+006A LATIN SMALL LETTER J */ | ||||||
|  | XK_k =                           0x006b, /* U+006B LATIN SMALL LETTER K */ | ||||||
|  | XK_l =                           0x006c, /* U+006C LATIN SMALL LETTER L */ | ||||||
|  | XK_m =                           0x006d, /* U+006D LATIN SMALL LETTER M */ | ||||||
|  | XK_n =                           0x006e, /* U+006E LATIN SMALL LETTER N */ | ||||||
|  | XK_o =                           0x006f, /* U+006F LATIN SMALL LETTER O */ | ||||||
|  | XK_p =                           0x0070, /* U+0070 LATIN SMALL LETTER P */ | ||||||
|  | XK_q =                           0x0071, /* U+0071 LATIN SMALL LETTER Q */ | ||||||
|  | XK_r =                           0x0072, /* U+0072 LATIN SMALL LETTER R */ | ||||||
|  | XK_s =                           0x0073, /* U+0073 LATIN SMALL LETTER S */ | ||||||
|  | XK_t =                           0x0074, /* U+0074 LATIN SMALL LETTER T */ | ||||||
|  | XK_u =                           0x0075, /* U+0075 LATIN SMALL LETTER U */ | ||||||
|  | XK_v =                           0x0076, /* U+0076 LATIN SMALL LETTER V */ | ||||||
|  | XK_w =                           0x0077, /* U+0077 LATIN SMALL LETTER W */ | ||||||
|  | XK_x =                           0x0078, /* U+0078 LATIN SMALL LETTER X */ | ||||||
|  | XK_y =                           0x0079, /* U+0079 LATIN SMALL LETTER Y */ | ||||||
|  | XK_z =                           0x007a, /* U+007A LATIN SMALL LETTER Z */ | ||||||
|  | XK_braceleft =                   0x007b, /* U+007B LEFT CURLY BRACKET */ | ||||||
|  | XK_bar =                         0x007c, /* U+007C VERTICAL LINE */ | ||||||
|  | XK_braceright =                  0x007d, /* U+007D RIGHT CURLY BRACKET */ | ||||||
|  | XK_asciitilde =                  0x007e, /* U+007E TILDE */ | ||||||
|  |  | ||||||
|  | XK_nobreakspace =                0x00a0, /* U+00A0 NO-BREAK SPACE */ | ||||||
|  | XK_exclamdown =                  0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */ | ||||||
|  | XK_cent =                        0x00a2, /* U+00A2 CENT SIGN */ | ||||||
|  | XK_sterling =                    0x00a3, /* U+00A3 POUND SIGN */ | ||||||
|  | XK_currency =                    0x00a4, /* U+00A4 CURRENCY SIGN */ | ||||||
|  | XK_yen =                         0x00a5, /* U+00A5 YEN SIGN */ | ||||||
|  | XK_brokenbar =                   0x00a6, /* U+00A6 BROKEN BAR */ | ||||||
|  | XK_section =                     0x00a7, /* U+00A7 SECTION SIGN */ | ||||||
|  | XK_diaeresis =                   0x00a8, /* U+00A8 DIAERESIS */ | ||||||
|  | XK_copyright =                   0x00a9, /* U+00A9 COPYRIGHT SIGN */ | ||||||
|  | XK_ordfeminine =                 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */ | ||||||
|  | XK_guillemotleft =               0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ | ||||||
|  | XK_notsign =                     0x00ac, /* U+00AC NOT SIGN */ | ||||||
|  | XK_hyphen =                      0x00ad, /* U+00AD SOFT HYPHEN */ | ||||||
|  | XK_registered =                  0x00ae, /* U+00AE REGISTERED SIGN */ | ||||||
|  | XK_macron =                      0x00af, /* U+00AF MACRON */ | ||||||
|  | XK_degree =                      0x00b0, /* U+00B0 DEGREE SIGN */ | ||||||
|  | XK_plusminus =                   0x00b1, /* U+00B1 PLUS-MINUS SIGN */ | ||||||
|  | XK_twosuperior =                 0x00b2, /* U+00B2 SUPERSCRIPT TWO */ | ||||||
|  | XK_threesuperior =               0x00b3, /* U+00B3 SUPERSCRIPT THREE */ | ||||||
|  | XK_acute =                       0x00b4, /* U+00B4 ACUTE ACCENT */ | ||||||
|  | XK_mu =                          0x00b5, /* U+00B5 MICRO SIGN */ | ||||||
|  | XK_paragraph =                   0x00b6, /* U+00B6 PILCROW SIGN */ | ||||||
|  | XK_periodcentered =              0x00b7, /* U+00B7 MIDDLE DOT */ | ||||||
|  | XK_cedilla =                     0x00b8, /* U+00B8 CEDILLA */ | ||||||
|  | XK_onesuperior =                 0x00b9, /* U+00B9 SUPERSCRIPT ONE */ | ||||||
|  | XK_masculine =                   0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */ | ||||||
|  | XK_guillemotright =              0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ | ||||||
|  | XK_onequarter =                  0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */ | ||||||
|  | XK_onehalf =                     0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */ | ||||||
|  | XK_threequarters =               0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */ | ||||||
|  | XK_questiondown =                0x00bf, /* U+00BF INVERTED QUESTION MARK */ | ||||||
|  | XK_Agrave =                      0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */ | ||||||
|  | XK_Aacute =                      0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */ | ||||||
|  | XK_Acircumflex =                 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ | ||||||
|  | XK_Atilde =                      0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */ | ||||||
|  | XK_Adiaeresis =                  0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */ | ||||||
|  | XK_Aring =                       0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */ | ||||||
|  | XK_AE =                          0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */ | ||||||
|  | XK_Ccedilla =                    0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */ | ||||||
|  | XK_Egrave =                      0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */ | ||||||
|  | XK_Eacute =                      0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */ | ||||||
|  | XK_Ecircumflex =                 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ | ||||||
|  | XK_Ediaeresis =                  0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */ | ||||||
|  | XK_Igrave =                      0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */ | ||||||
|  | XK_Iacute =                      0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */ | ||||||
|  | XK_Icircumflex =                 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ | ||||||
|  | XK_Idiaeresis =                  0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */ | ||||||
|  | XK_ETH =                         0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */ | ||||||
|  | XK_Eth =                         0x00d0, /* deprecated */ | ||||||
|  | XK_Ntilde =                      0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */ | ||||||
|  | XK_Ograve =                      0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */ | ||||||
|  | XK_Oacute =                      0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */ | ||||||
|  | XK_Ocircumflex =                 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ | ||||||
|  | XK_Otilde =                      0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */ | ||||||
|  | XK_Odiaeresis =                  0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */ | ||||||
|  | XK_multiply =                    0x00d7, /* U+00D7 MULTIPLICATION SIGN */ | ||||||
|  | XK_Oslash =                      0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */ | ||||||
|  | XK_Ooblique =                    0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */ | ||||||
|  | XK_Ugrave =                      0x00d9, /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */ | ||||||
|  | XK_Uacute =                      0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */ | ||||||
|  | XK_Ucircumflex =                 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ | ||||||
|  | XK_Udiaeresis =                  0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */ | ||||||
|  | XK_Yacute =                      0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */ | ||||||
|  | XK_THORN =                       0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */ | ||||||
|  | XK_Thorn =                       0x00de, /* deprecated */ | ||||||
|  | XK_ssharp =                      0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */ | ||||||
|  | XK_agrave =                      0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */ | ||||||
|  | XK_aacute =                      0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */ | ||||||
|  | XK_acircumflex =                 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */ | ||||||
|  | XK_atilde =                      0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */ | ||||||
|  | XK_adiaeresis =                  0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */ | ||||||
|  | XK_aring =                       0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */ | ||||||
|  | XK_ae =                          0x00e6, /* U+00E6 LATIN SMALL LETTER AE */ | ||||||
|  | XK_ccedilla =                    0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */ | ||||||
|  | XK_egrave =                      0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */ | ||||||
|  | XK_eacute =                      0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */ | ||||||
|  | XK_ecircumflex =                 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */ | ||||||
|  | XK_ediaeresis =                  0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */ | ||||||
|  | XK_igrave =                      0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */ | ||||||
|  | XK_iacute =                      0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */ | ||||||
|  | XK_icircumflex =                 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */ | ||||||
|  | XK_idiaeresis =                  0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */ | ||||||
|  | XK_eth =                         0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */ | ||||||
|  | XK_ntilde =                      0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */ | ||||||
|  | XK_ograve =                      0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */ | ||||||
|  | XK_oacute =                      0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */ | ||||||
|  | XK_ocircumflex =                 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */ | ||||||
|  | XK_otilde =                      0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */ | ||||||
|  | XK_odiaeresis =                  0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */ | ||||||
|  | XK_division =                    0x00f7, /* U+00F7 DIVISION SIGN */ | ||||||
|  | XK_oslash =                      0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */ | ||||||
|  | XK_ooblique =                    0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */ | ||||||
|  | XK_ugrave =                      0x00f9, /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */ | ||||||
|  | XK_uacute =                      0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */ | ||||||
|  | XK_ucircumflex =                 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */ | ||||||
|  | XK_udiaeresis =                  0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */ | ||||||
|  | XK_yacute =                      0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */ | ||||||
|  | XK_thorn =                       0x00fe, /* U+00FE LATIN SMALL LETTER THORN */ | ||||||
|  | XK_ydiaeresis =                  0x00ff; /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */ | ||||||
							
								
								
									
										15
									
								
								public/novnc/include/keysymdef.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								public/novnc/include/logo.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										120
									
								
								public/novnc/include/playback.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,120 @@ | |||||||
|  | /* | ||||||
|  |  * noVNC: HTML5 VNC client | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Licensed under MPL 2.0 (see LICENSE.txt) | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | "use strict"; | ||||||
|  | /*jslint browser: true, white: false */ | ||||||
|  | /*global Util, VNC_frame_data, finish */ | ||||||
|  |  | ||||||
|  | var rfb, mode, test_state, frame_idx, frame_length, | ||||||
|  |     iteration, iterations, istart_time, | ||||||
|  |  | ||||||
|  |     // Pre-declarations for jslint | ||||||
|  |     send_array, next_iteration, queue_next_packet, do_packet, enable_test_mode; | ||||||
|  |  | ||||||
|  | // Override send_array | ||||||
|  | send_array = function (arr) { | ||||||
|  |     // Stub out send_array | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enable_test_mode = function () { | ||||||
|  |     rfb._sock._mode = VNC_frame_encoding; | ||||||
|  |     rfb._sock.send = send_array; | ||||||
|  |     rfb._sock.close = function () {}; | ||||||
|  |     rfb._sock.flush = function () {}; | ||||||
|  |     rfb._checkEvents = function () {}; | ||||||
|  |     rfb.connect = function (host, port, password, path) { | ||||||
|  |         this._rfb_host = host; | ||||||
|  |         this._rfb_port = port; | ||||||
|  |         this._rfb_password = (password !== undefined) ? password : ""; | ||||||
|  |         this._rfb_path = (path !== undefined) ? path : ""; | ||||||
|  |         this._sock.init('binary', 'ws'); | ||||||
|  |         this._updateState('ProtocolVersion', "Starting VNC handshake"); | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | next_iteration = function () { | ||||||
|  |     rfb = new RFB({'target': $D('VNC_canvas'), | ||||||
|  |                    'onUpdateState': updateState}); | ||||||
|  |     enable_test_mode(); | ||||||
|  |  | ||||||
|  |     if (iteration === 0) { | ||||||
|  |         frame_length = VNC_frame_data.length; | ||||||
|  |         test_state = 'running'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (test_state !== 'running') { return; } | ||||||
|  |  | ||||||
|  |     iteration += 1; | ||||||
|  |     if (iteration > iterations) { | ||||||
|  |         finish(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     frame_idx = 0; | ||||||
|  |     istart_time = (new Date()).getTime(); | ||||||
|  |     rfb.connect('test', 0, "bogus"); | ||||||
|  |  | ||||||
|  |     queue_next_packet(); | ||||||
|  |  | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | queue_next_packet = function () { | ||||||
|  |     var frame, foffset, toffset, delay; | ||||||
|  |     if (test_state !== 'running') { return; } | ||||||
|  |  | ||||||
|  |     frame = VNC_frame_data[frame_idx]; | ||||||
|  |     while ((frame_idx < frame_length) && (frame.charAt(0) === "}")) { | ||||||
|  |         //Util.Debug("Send frame " + frame_idx); | ||||||
|  |         frame_idx += 1; | ||||||
|  |         frame = VNC_frame_data[frame_idx]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (frame === 'EOF') { | ||||||
|  |         Util.Debug("Finished, found EOF"); | ||||||
|  |         next_iteration(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     if (frame_idx >= frame_length) { | ||||||
|  |         Util.Debug("Finished, no more frames"); | ||||||
|  |         next_iteration(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (mode === 'realtime') { | ||||||
|  |         foffset = frame.slice(1, frame.indexOf('{', 1)); | ||||||
|  |         toffset = (new Date()).getTime() - istart_time; | ||||||
|  |         delay = foffset - toffset; | ||||||
|  |         if (delay < 1) { | ||||||
|  |             delay = 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         setTimeout(do_packet, delay); | ||||||
|  |     } else { | ||||||
|  |         setTimeout(do_packet, 1); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | var bytes_processed = 0; | ||||||
|  |  | ||||||
|  | do_packet = function () { | ||||||
|  |     //Util.Debug("Processing frame: " + frame_idx); | ||||||
|  |     var frame = VNC_frame_data[frame_idx], | ||||||
|  |         start = frame.indexOf('{', 1) + 1; | ||||||
|  |     bytes_processed += frame.length - start; | ||||||
|  |     if (VNC_frame_encoding === 'binary') { | ||||||
|  |         var u8 = new Uint8Array(frame.length - start); | ||||||
|  |         for (var i = 0; i < frame.length - start; i++) { | ||||||
|  |             u8[i] = frame.charCodeAt(start + i); | ||||||
|  |         } | ||||||
|  |         rfb._sock._recv_message({'data' : u8}); | ||||||
|  |     } else { | ||||||
|  |         rfb._sock._recv_message({'data' : frame.slice(start)}); | ||||||
|  |     } | ||||||
|  |     frame_idx += 1; | ||||||
|  |  | ||||||
|  |     queue_next_packet(); | ||||||
|  | }; | ||||||
|  |  | ||||||
							
								
								
									
										2148
									
								
								public/novnc/include/rfb.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1287
									
								
								public/novnc/include/ui.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										622
									
								
								public/novnc/include/util.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,622 @@ | |||||||
|  | /* | ||||||
|  |  * noVNC: HTML5 VNC client | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Licensed under MPL 2.0 (see LICENSE.txt) | ||||||
|  |  * | ||||||
|  |  * See README.md for usage and integration instructions. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* jshint white: false, nonstandard: true */ | ||||||
|  | /*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */ | ||||||
|  |  | ||||||
|  | // Globals defined here | ||||||
|  | var Util = {}; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Make arrays quack | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | var addFunc = function (cl, name, func) { | ||||||
|  |     if (!cl.prototype[name]) { | ||||||
|  |         Object.defineProperty(cl.prototype, name, { enumerable: false, value: func }); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | addFunc(Array, 'push8', function (num) { | ||||||
|  |     "use strict"; | ||||||
|  |     this.push(num & 0xFF); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | addFunc(Array, 'push16', function (num) { | ||||||
|  |     "use strict"; | ||||||
|  |     this.push((num >> 8) & 0xFF, | ||||||
|  |               num & 0xFF); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | addFunc(Array, 'push32', function (num) { | ||||||
|  |     "use strict"; | ||||||
|  |     this.push((num >> 24) & 0xFF, | ||||||
|  |               (num >> 16) & 0xFF, | ||||||
|  |               (num >>  8) & 0xFF, | ||||||
|  |               num & 0xFF); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | // IE does not support map (even in IE9) | ||||||
|  | //This prototype is provided by the Mozilla foundation and | ||||||
|  | //is distributed under the MIT license. | ||||||
|  | //http://www.ibiblio.org/pub/Linux/LICENSES/mit.license | ||||||
|  | addFunc(Array, 'map', function (fun /*, thisp*/) { | ||||||
|  |     "use strict"; | ||||||
|  |     var len = this.length; | ||||||
|  |     if (typeof fun != "function") { | ||||||
|  |         throw new TypeError(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var res = new Array(len); | ||||||
|  |     var thisp = arguments[1]; | ||||||
|  |     for (var i = 0; i < len; i++) { | ||||||
|  |         if (i in this) { | ||||||
|  |             res[i] = fun.call(thisp, this[i], i, this); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return res; | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | // IE <9 does not support indexOf | ||||||
|  | //This prototype is provided by the Mozilla foundation and | ||||||
|  | //is distributed under the MIT license. | ||||||
|  | //http://www.ibiblio.org/pub/Linux/LICENSES/mit.license | ||||||
|  | addFunc(Array, 'indexOf', function (elt /*, from*/) { | ||||||
|  |     "use strict"; | ||||||
|  |     var len = this.length >>> 0; | ||||||
|  |  | ||||||
|  |     var from = Number(arguments[1]) || 0; | ||||||
|  |     from = (from < 0) ? Math.ceil(from) : Math.floor(from); | ||||||
|  |     if (from < 0) { | ||||||
|  |         from += len; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (; from < len; from++) { | ||||||
|  |         if (from in this && | ||||||
|  |                 this[from] === elt) { | ||||||
|  |             return from; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return -1; | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys | ||||||
|  | if (!Object.keys) { | ||||||
|  |     Object.keys = (function () { | ||||||
|  |         'use strict'; | ||||||
|  |         var hasOwnProperty = Object.prototype.hasOwnProperty, | ||||||
|  |             hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), | ||||||
|  |             dontEnums = [ | ||||||
|  |                 'toString', | ||||||
|  |                 'toLocaleString', | ||||||
|  |                 'valueOf', | ||||||
|  |                 'hasOwnProperty', | ||||||
|  |                 'isPrototypeOf', | ||||||
|  |                 'propertyIsEnumerable', | ||||||
|  |                 'constructor' | ||||||
|  |             ], | ||||||
|  |             dontEnumsLength = dontEnums.length; | ||||||
|  |  | ||||||
|  |         return function (obj) { | ||||||
|  |             if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { | ||||||
|  |                 throw new TypeError('Object.keys called on non-object'); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var result = [], prop, i; | ||||||
|  |  | ||||||
|  |             for (prop in obj) { | ||||||
|  |                 if (hasOwnProperty.call(obj, prop)) { | ||||||
|  |                     result.push(prop); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (hasDontEnumBug) { | ||||||
|  |                 for (i = 0; i < dontEnumsLength; i++) { | ||||||
|  |                     if (hasOwnProperty.call(obj, dontEnums[i])) { | ||||||
|  |                         result.push(dontEnums[i]); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return result; | ||||||
|  |         }; | ||||||
|  |     })(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // PhantomJS 1.x doesn't support bind, | ||||||
|  | // so leave this in until PhantomJS 2.0 is released | ||||||
|  | //This prototype is provided by the Mozilla foundation and | ||||||
|  | //is distributed under the MIT license. | ||||||
|  | //http://www.ibiblio.org/pub/Linux/LICENSES/mit.license | ||||||
|  | addFunc(Function, 'bind', function (oThis) { | ||||||
|  |     if (typeof this !== "function") { | ||||||
|  |         // closest thing possible to the ECMAScript 5 | ||||||
|  |         // internal IsCallable function | ||||||
|  |         throw new TypeError("Function.prototype.bind - " + | ||||||
|  |                             "what is trying to be bound is not callable"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var aArgs = Array.prototype.slice.call(arguments, 1), | ||||||
|  |             fToBind = this, | ||||||
|  |             fNOP = function () {}, | ||||||
|  |             fBound = function () { | ||||||
|  |                 return fToBind.apply(this instanceof fNOP && oThis ? this | ||||||
|  |                                                                    : oThis, | ||||||
|  |                                      aArgs.concat(Array.prototype.slice.call(arguments))); | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |     fNOP.prototype = this.prototype; | ||||||
|  |     fBound.prototype = new fNOP(); | ||||||
|  |  | ||||||
|  |     return fBound; | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // requestAnimationFrame shim with setTimeout fallback | ||||||
|  | // | ||||||
|  |  | ||||||
|  | window.requestAnimFrame = (function () { | ||||||
|  |     "use strict"; | ||||||
|  |     return  window.requestAnimationFrame       || | ||||||
|  |             window.webkitRequestAnimationFrame || | ||||||
|  |             window.mozRequestAnimationFrame    || | ||||||
|  |             window.oRequestAnimationFrame      || | ||||||
|  |             window.msRequestAnimationFrame     || | ||||||
|  |             function (callback) { | ||||||
|  |                 window.setTimeout(callback, 1000 / 60); | ||||||
|  |             }; | ||||||
|  | })(); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * ------------------------------------------------------ | ||||||
|  |  * Namespaced in Util | ||||||
|  |  * ------------------------------------------------------ | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Logging/debug routines | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | Util._log_level = 'warn'; | ||||||
|  | Util.init_logging = function (level) { | ||||||
|  |     "use strict"; | ||||||
|  |     if (typeof level === 'undefined') { | ||||||
|  |         level = Util._log_level; | ||||||
|  |     } else { | ||||||
|  |         Util._log_level = level; | ||||||
|  |     } | ||||||
|  |     if (typeof window.console === "undefined") { | ||||||
|  |         if (typeof window.opera !== "undefined") { | ||||||
|  |             window.console = { | ||||||
|  |                 'log'  : window.opera.postError, | ||||||
|  |                 'warn' : window.opera.postError, | ||||||
|  |                 'error': window.opera.postError | ||||||
|  |             }; | ||||||
|  |         } else { | ||||||
|  |             window.console = { | ||||||
|  |                 'log'  : function (m) {}, | ||||||
|  |                 'warn' : function (m) {}, | ||||||
|  |                 'error': function (m) {} | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {}; | ||||||
|  |     /* jshint -W086 */ | ||||||
|  |     switch (level) { | ||||||
|  |         case 'debug': | ||||||
|  |             Util.Debug = function (msg) { console.log(msg); }; | ||||||
|  |         case 'info': | ||||||
|  |             Util.Info  = function (msg) { console.log(msg); }; | ||||||
|  |         case 'warn': | ||||||
|  |             Util.Warn  = function (msg) { console.warn(msg); }; | ||||||
|  |         case 'error': | ||||||
|  |             Util.Error = function (msg) { console.error(msg); }; | ||||||
|  |         case 'none': | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             throw new Error("invalid logging type '" + level + "'"); | ||||||
|  |     } | ||||||
|  |     /* jshint +W086 */ | ||||||
|  | }; | ||||||
|  | Util.get_logging = function () { | ||||||
|  |     return Util._log_level; | ||||||
|  | }; | ||||||
|  | // Initialize logging level | ||||||
|  | Util.init_logging(); | ||||||
|  |  | ||||||
|  | Util.make_property = function (proto, name, mode, type) { | ||||||
|  |     "use strict"; | ||||||
|  |  | ||||||
|  |     var getter; | ||||||
|  |     if (type === 'arr') { | ||||||
|  |         getter = function (idx) { | ||||||
|  |             if (typeof idx !== 'undefined') { | ||||||
|  |                 return this['_' + name][idx]; | ||||||
|  |             } else { | ||||||
|  |                 return this['_' + name]; | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |     } else { | ||||||
|  |         getter = function () { | ||||||
|  |             return this['_' + name]; | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var make_setter = function (process_val) { | ||||||
|  |         if (process_val) { | ||||||
|  |             return function (val, idx) { | ||||||
|  |                 if (typeof idx !== 'undefined') { | ||||||
|  |                     this['_' + name][idx] = process_val(val); | ||||||
|  |                 } else { | ||||||
|  |                     this['_' + name] = process_val(val); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |         } else { | ||||||
|  |             return function (val, idx) { | ||||||
|  |                 if (typeof idx !== 'undefined') { | ||||||
|  |                     this['_' + name][idx] = val; | ||||||
|  |                 } else { | ||||||
|  |                     this['_' + name] = val; | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     var setter; | ||||||
|  |     if (type === 'bool') { | ||||||
|  |         setter = make_setter(function (val) { | ||||||
|  |             if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) { | ||||||
|  |                 return false; | ||||||
|  |             } else { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } else if (type === 'int') { | ||||||
|  |         setter = make_setter(function (val) { return parseInt(val, 10); }); | ||||||
|  |     } else if (type === 'float') { | ||||||
|  |         setter = make_setter(parseFloat); | ||||||
|  |     } else if (type === 'str') { | ||||||
|  |         setter = make_setter(String); | ||||||
|  |     } else if (type === 'func') { | ||||||
|  |         setter = make_setter(function (val) { | ||||||
|  |             if (!val) { | ||||||
|  |                 return function () {}; | ||||||
|  |             } else { | ||||||
|  |                 return val; | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } else if (type === 'arr' || type === 'dom' || type == 'raw') { | ||||||
|  |         setter = make_setter(); | ||||||
|  |     } else { | ||||||
|  |         throw new Error('Unknown property type ' + type);  // some sanity checking | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // set the getter | ||||||
|  |     if (typeof proto['get_' + name] === 'undefined') { | ||||||
|  |         proto['get_' + name] = getter; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // set the setter if needed | ||||||
|  |     if (typeof proto['set_' + name] === 'undefined') { | ||||||
|  |         if (mode === 'rw') { | ||||||
|  |             proto['set_' + name] = setter; | ||||||
|  |         } else if (mode === 'wo') { | ||||||
|  |             proto['set_' + name] = function (val, idx) { | ||||||
|  |                 if (typeof this['_' + name] !== 'undefined') { | ||||||
|  |                     throw new Error(name + " can only be set once"); | ||||||
|  |                 } | ||||||
|  |                 setter.call(this, val, idx); | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // make a special setter that we can use in set defaults | ||||||
|  |     proto['_raw_set_' + name] = function (val, idx) { | ||||||
|  |         setter.call(this, val, idx); | ||||||
|  |         //delete this['_init_set_' + name];  // remove it after use | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Util.make_properties = function (constructor, arr) { | ||||||
|  |     "use strict"; | ||||||
|  |     for (var i = 0; i < arr.length; i++) { | ||||||
|  |         Util.make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Util.set_defaults = function (obj, conf, defaults) { | ||||||
|  |     var defaults_keys = Object.keys(defaults); | ||||||
|  |     var conf_keys = Object.keys(conf); | ||||||
|  |     var keys_obj = {}; | ||||||
|  |     var i; | ||||||
|  |     for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; } | ||||||
|  |     for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; } | ||||||
|  |     var keys = Object.keys(keys_obj); | ||||||
|  |  | ||||||
|  |     for (i = 0; i < keys.length; i++) { | ||||||
|  |         var setter = obj['_raw_set_' + keys[i]]; | ||||||
|  |         if (!setter) { | ||||||
|  |           Util.Warn('Invalid property ' + keys[i]); | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (keys[i] in conf) { | ||||||
|  |             setter.call(obj, conf[keys[i]]); | ||||||
|  |         } else { | ||||||
|  |             setter.call(obj, defaults[keys[i]]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Decode from UTF-8 | ||||||
|  |  */ | ||||||
|  | Util.decodeUTF8 = function (utf8string) { | ||||||
|  |     "use strict"; | ||||||
|  |     return decodeURIComponent(escape(utf8string)); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Cross-browser routines | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Dynamically load scripts without using document.write() | ||||||
|  | // Reference: http://unixpapa.com/js/dyna.html | ||||||
|  | // | ||||||
|  | // Handles the case where load_scripts is invoked from a script that | ||||||
|  | // itself is loaded via load_scripts. Once all scripts are loaded the | ||||||
|  | // window.onscriptsloaded handler is called (if set). | ||||||
|  | Util.get_include_uri = function () { | ||||||
|  |     return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/"; | ||||||
|  | }; | ||||||
|  | Util._loading_scripts = []; | ||||||
|  | Util._pending_scripts = []; | ||||||
|  | Util.load_scripts = function (files) { | ||||||
|  |     "use strict"; | ||||||
|  |     var head = document.getElementsByTagName('head')[0], script, | ||||||
|  |         ls = Util._loading_scripts, ps = Util._pending_scripts; | ||||||
|  |  | ||||||
|  |     var loadFunc = function (e) { | ||||||
|  |         while (ls.length > 0 && (ls[0].readyState === 'loaded' || | ||||||
|  |                                  ls[0].readyState === 'complete')) { | ||||||
|  |             // For IE, append the script to trigger execution | ||||||
|  |             var s = ls.shift(); | ||||||
|  |             //console.log("loaded script: " + s.src); | ||||||
|  |             head.appendChild(s); | ||||||
|  |         } | ||||||
|  |         if (!this.readyState || | ||||||
|  |             (Util.Engine.presto && this.readyState === 'loaded') || | ||||||
|  |             this.readyState === 'complete') { | ||||||
|  |             if (ps.indexOf(this) >= 0) { | ||||||
|  |                 this.onload = this.onreadystatechange = null; | ||||||
|  |                 //console.log("completed script: " + this.src); | ||||||
|  |                 ps.splice(ps.indexOf(this), 1); | ||||||
|  |  | ||||||
|  |                 // Call window.onscriptsload after last script loads | ||||||
|  |                 if (ps.length === 0 && window.onscriptsload) { | ||||||
|  |                     window.onscriptsload(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     for (var f = 0; f < files.length; f++) { | ||||||
|  |         script = document.createElement('script'); | ||||||
|  |         script.type = 'text/javascript'; | ||||||
|  |         script.src = Util.get_include_uri() + files[f]; | ||||||
|  |         //console.log("loading script: " + script.src); | ||||||
|  |         script.onload = script.onreadystatechange = loadFunc; | ||||||
|  |         // In-order script execution tricks | ||||||
|  |         if (Util.Engine.trident) { | ||||||
|  |             // For IE wait until readyState is 'loaded' before | ||||||
|  |             // appending it which will trigger execution | ||||||
|  |             // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order | ||||||
|  |             ls.push(script); | ||||||
|  |         } else { | ||||||
|  |             // For webkit and firefox set async=false and append now | ||||||
|  |             // https://developer.mozilla.org/en-US/docs/HTML/Element/script | ||||||
|  |             script.async = false; | ||||||
|  |             head.appendChild(script); | ||||||
|  |         } | ||||||
|  |         ps.push(script); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Util.getPosition = function(obj) { | ||||||
|  |     "use strict"; | ||||||
|  |     // NB(sross): the Mozilla developer reference seems to indicate that | ||||||
|  |     // getBoundingClientRect includes border and padding, so the canvas | ||||||
|  |     // style should NOT include either. | ||||||
|  |     var objPosition = obj.getBoundingClientRect(); | ||||||
|  |     return {'x': objPosition.left + window.pageXOffset, 'y': objPosition.top + window.pageYOffset, | ||||||
|  |             'width': objPosition.width, 'height': objPosition.height}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Get mouse event position in DOM element | ||||||
|  | Util.getEventPosition = function (e, obj, scale) { | ||||||
|  |     "use strict"; | ||||||
|  |     var evt, docX, docY, pos; | ||||||
|  |     //if (!e) evt = window.event; | ||||||
|  |     evt = (e ? e : window.event); | ||||||
|  |     evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt); | ||||||
|  |     if (evt.pageX || evt.pageY) { | ||||||
|  |         docX = evt.pageX; | ||||||
|  |         docY = evt.pageY; | ||||||
|  |     } else if (evt.clientX || evt.clientY) { | ||||||
|  |         docX = evt.clientX + document.body.scrollLeft + | ||||||
|  |             document.documentElement.scrollLeft; | ||||||
|  |         docY = evt.clientY + document.body.scrollTop + | ||||||
|  |             document.documentElement.scrollTop; | ||||||
|  |     } | ||||||
|  |     pos = Util.getPosition(obj); | ||||||
|  |     if (typeof scale === "undefined") { | ||||||
|  |         scale = 1; | ||||||
|  |     } | ||||||
|  |     var realx = docX - pos.x; | ||||||
|  |     var realy = docY - pos.y; | ||||||
|  |     var x = Math.max(Math.min(realx, pos.width - 1), 0); | ||||||
|  |     var y = Math.max(Math.min(realy, pos.height - 1), 0); | ||||||
|  |     return {'x': x / scale, 'y': y / scale, 'realx': realx / scale, 'realy': realy / scale}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events | ||||||
|  | Util.addEvent = function (obj, evType, fn) { | ||||||
|  |     "use strict"; | ||||||
|  |     if (obj.attachEvent) { | ||||||
|  |         var r = obj.attachEvent("on" + evType, fn); | ||||||
|  |         return r; | ||||||
|  |     } else if (obj.addEventListener) { | ||||||
|  |         obj.addEventListener(evType, fn, false); | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         throw new Error("Handler could not be attached"); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Util.removeEvent = function (obj, evType, fn) { | ||||||
|  |     "use strict"; | ||||||
|  |     if (obj.detachEvent) { | ||||||
|  |         var r = obj.detachEvent("on" + evType, fn); | ||||||
|  |         return r; | ||||||
|  |     } else if (obj.removeEventListener) { | ||||||
|  |         obj.removeEventListener(evType, fn, false); | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         throw new Error("Handler could not be removed"); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Util.stopEvent = function (e) { | ||||||
|  |     "use strict"; | ||||||
|  |     if (e.stopPropagation) { e.stopPropagation(); } | ||||||
|  |     else                   { e.cancelBubble = true; } | ||||||
|  |  | ||||||
|  |     if (e.preventDefault)  { e.preventDefault(); } | ||||||
|  |     else                   { e.returnValue = false; } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Util._cursor_uris_supported = null; | ||||||
|  |  | ||||||
|  | Util.browserSupportsCursorURIs = function () { | ||||||
|  |     if (Util._cursor_uris_supported === null) { | ||||||
|  |         try { | ||||||
|  |             var target = document.createElement('canvas'); | ||||||
|  |             target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default'; | ||||||
|  |  | ||||||
|  |             if (target.style.cursor) { | ||||||
|  |                 Util.Info("Data URI scheme cursor supported"); | ||||||
|  |                 Util._cursor_uris_supported = true; | ||||||
|  |             } else { | ||||||
|  |                 Util.Warn("Data URI scheme cursor not supported"); | ||||||
|  |                 Util._cursor_uris_supported = false; | ||||||
|  |             } | ||||||
|  |         } catch (exc) { | ||||||
|  |             Util.Error("Data URI scheme cursor test exception: " + exc); | ||||||
|  |             Util._cursor_uris_supported = false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Util._cursor_uris_supported; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Set browser engine versions. Based on mootools. | ||||||
|  | Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}; | ||||||
|  |  | ||||||
|  | (function () { | ||||||
|  |     "use strict"; | ||||||
|  |     // 'presto': (function () { return (!window.opera) ? false : true; }()), | ||||||
|  |     var detectPresto = function () { | ||||||
|  |         return !!window.opera; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); | ||||||
|  |     var detectTrident = function () { | ||||||
|  |         if (!window.ActiveXObject) { | ||||||
|  |             return false; | ||||||
|  |         } else { | ||||||
|  |             if (window.XMLHttpRequest) { | ||||||
|  |                 return (document.querySelectorAll) ? 6 : 5; | ||||||
|  |             } else { | ||||||
|  |                 return 4; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()), | ||||||
|  |     var detectInitialWebkit = function () { | ||||||
|  |         try { | ||||||
|  |             if (navigator.taintEnabled) { | ||||||
|  |                 return false; | ||||||
|  |             } else { | ||||||
|  |                 if (Util.Features.xpath) { | ||||||
|  |                     return (Util.Features.query) ? 525 : 420; | ||||||
|  |                 } else { | ||||||
|  |                     return 419; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } catch (e) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     var detectActualWebkit = function (initial_ver) { | ||||||
|  |         var re = /WebKit\/([0-9\.]*) /; | ||||||
|  |         var str_ver = (navigator.userAgent.match(re) || ['', initial_ver])[1]; | ||||||
|  |         return parseFloat(str_ver, 10); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }()) | ||||||
|  |     var detectGecko = function () { | ||||||
|  |         /* jshint -W041 */ | ||||||
|  |         if (!document.getBoxObjectFor && window.mozInnerScreenX == null) { | ||||||
|  |             return false; | ||||||
|  |         } else { | ||||||
|  |             return (document.getElementsByClassName) ? 19 : 18; | ||||||
|  |         } | ||||||
|  |         /* jshint +W041 */ | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     Util.Engine = { | ||||||
|  |         // Version detection break in Opera 11.60 (errors on arguments.callee.caller reference) | ||||||
|  |         //'presto': (function() { | ||||||
|  |         //         return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()), | ||||||
|  |         'presto': detectPresto(), | ||||||
|  |         'trident': detectTrident(), | ||||||
|  |         'webkit': detectInitialWebkit(), | ||||||
|  |         'gecko': detectGecko(), | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (Util.Engine.webkit) { | ||||||
|  |         // Extract actual webkit version if available | ||||||
|  |         Util.Engine.webkit = detectActualWebkit(Util.Engine.webkit); | ||||||
|  |     } | ||||||
|  | })(); | ||||||
|  |  | ||||||
|  | Util.Flash = (function () { | ||||||
|  |     "use strict"; | ||||||
|  |     var v, version; | ||||||
|  |     try { | ||||||
|  |         v = navigator.plugins['Shockwave Flash'].description; | ||||||
|  |     } catch (err1) { | ||||||
|  |         try { | ||||||
|  |             v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); | ||||||
|  |         } catch (err2) { | ||||||
|  |             v = '0 r0'; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     version = v.match(/\d+/g); | ||||||
|  |     return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; | ||||||
|  | }()); | ||||||
							
								
								
									
										440
									
								
								public/novnc/include/websock.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,440 @@ | |||||||
|  | /* | ||||||
|  |  * Websock: high-performance binary WebSockets | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Licensed under MPL 2.0 (see LICENSE.txt) | ||||||
|  |  * | ||||||
|  |  * Websock is similar to the standard WebSocket object but Websock | ||||||
|  |  * enables communication with raw TCP sockets (i.e. the binary stream) | ||||||
|  |  * via websockify. This is accomplished by base64 encoding the data | ||||||
|  |  * stream between Websock and websockify. | ||||||
|  |  * | ||||||
|  |  * Websock has built-in receive queue buffering; the message event | ||||||
|  |  * does not contain actual data but is simply a notification that | ||||||
|  |  * there is new data available. Several rQ* methods are available to | ||||||
|  |  * read binary data off of the receive queue. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /*jslint browser: true, bitwise: true */ | ||||||
|  | /*global Util*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Load Flash WebSocket emulator if needed | ||||||
|  |  | ||||||
|  | // To force WebSocket emulator even when native WebSocket available | ||||||
|  | //window.WEB_SOCKET_FORCE_FLASH = true; | ||||||
|  | // To enable WebSocket emulator debug: | ||||||
|  | //window.WEB_SOCKET_DEBUG=1; | ||||||
|  |  | ||||||
|  | if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) { | ||||||
|  |     Websock_native = true; | ||||||
|  | } else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) { | ||||||
|  |     Websock_native = true; | ||||||
|  |     window.WebSocket = window.MozWebSocket; | ||||||
|  | } else { | ||||||
|  |     /* no builtin WebSocket so load web_socket.js */ | ||||||
|  |  | ||||||
|  |     Websock_native = false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function Websock() { | ||||||
|  |     "use strict"; | ||||||
|  |  | ||||||
|  |     this._websocket = null;  // WebSocket object | ||||||
|  |  | ||||||
|  |     this._rQi = 0;           // Receive queue index | ||||||
|  |     this._rQlen = 0;         // Next write position in the receive queue | ||||||
|  |     this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB) | ||||||
|  |     this._rQmax = this._rQbufferSize / 8; | ||||||
|  |     // called in init: this._rQ = new Uint8Array(this._rQbufferSize); | ||||||
|  |     this._rQ = null; // Receive queue | ||||||
|  |  | ||||||
|  |     this._sQbufferSize = 1024 * 10;  // 10 KiB | ||||||
|  |     // called in init: this._sQ = new Uint8Array(this._sQbufferSize); | ||||||
|  |     this._sQlen = 0; | ||||||
|  |     this._sQ = null;  // Send queue | ||||||
|  |  | ||||||
|  |     this._mode = 'binary';    // Current WebSocket mode: 'binary', 'base64' | ||||||
|  |     this.maxBufferedAmount = 200; | ||||||
|  |  | ||||||
|  |     this._eventHandlers = { | ||||||
|  |         'message': function () {}, | ||||||
|  |         'open': function () {}, | ||||||
|  |         'close': function () {}, | ||||||
|  |         'error': function () {} | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | (function () { | ||||||
|  |     "use strict"; | ||||||
|  |     // this has performance issues in some versions Chromium, and | ||||||
|  |     // doesn't gain a tremendous amount of performance increase in Firefox | ||||||
|  |     // at the moment.  It may be valuable to turn it on in the future. | ||||||
|  |     var ENABLE_COPYWITHIN = false; | ||||||
|  |  | ||||||
|  |     var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024;  // 40 MiB | ||||||
|  |  | ||||||
|  |     var typedArrayToString = (function () { | ||||||
|  |         // This is only for PhantomJS, which doesn't like apply-ing | ||||||
|  |         // with Typed Arrays | ||||||
|  |         try { | ||||||
|  |             var arr = new Uint8Array([1, 2, 3]); | ||||||
|  |             String.fromCharCode.apply(null, arr); | ||||||
|  |             return function (a) { return String.fromCharCode.apply(null, a); }; | ||||||
|  |         } catch (ex) { | ||||||
|  |             return function (a) { | ||||||
|  |                 return String.fromCharCode.apply( | ||||||
|  |                     null, Array.prototype.slice.call(a)); | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     })(); | ||||||
|  |  | ||||||
|  |     Websock.prototype = { | ||||||
|  |         // Getters and Setters | ||||||
|  |         get_sQ: function () { | ||||||
|  |             return this._sQ; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         get_rQ: function () { | ||||||
|  |             return this._rQ; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         get_rQi: function () { | ||||||
|  |             return this._rQi; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         set_rQi: function (val) { | ||||||
|  |             this._rQi = val; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // Receive Queue | ||||||
|  |         rQlen: function () { | ||||||
|  |             return this._rQlen - this._rQi; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQpeek8: function () { | ||||||
|  |             return this._rQ[this._rQi]; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQshift8: function () { | ||||||
|  |             return this._rQ[this._rQi++]; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQskip8: function () { | ||||||
|  |             this._rQi++; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQskipBytes: function (num) { | ||||||
|  |             this._rQi += num; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // TODO(directxman12): test performance with these vs a DataView | ||||||
|  |         rQshift16: function () { | ||||||
|  |             return (this._rQ[this._rQi++] << 8) + | ||||||
|  |                    this._rQ[this._rQi++]; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQshift32: function () { | ||||||
|  |             return (this._rQ[this._rQi++] << 24) + | ||||||
|  |                    (this._rQ[this._rQi++] << 16) + | ||||||
|  |                    (this._rQ[this._rQi++] << 8) + | ||||||
|  |                    this._rQ[this._rQi++]; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQshiftStr: function (len) { | ||||||
|  |             if (typeof(len) === 'undefined') { len = this.rQlen(); } | ||||||
|  |             var arr = new Uint8Array(this._rQ.buffer, this._rQi, len); | ||||||
|  |             this._rQi += len; | ||||||
|  |             return typedArrayToString(arr); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQshiftBytes: function (len) { | ||||||
|  |             if (typeof(len) === 'undefined') { len = this.rQlen(); } | ||||||
|  |             this._rQi += len; | ||||||
|  |             return new Uint8Array(this._rQ.buffer, this._rQi - len, len); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQshiftTo: function (target, len) { | ||||||
|  |             if (len === undefined) { len = this.rQlen(); } | ||||||
|  |             // TODO: make this just use set with views when using a ArrayBuffer to store the rQ | ||||||
|  |             target.set(new Uint8Array(this._rQ.buffer, this._rQi, len)); | ||||||
|  |             this._rQi += len; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQwhole: function () { | ||||||
|  |             return new Uint8Array(this._rQ.buffer, 0, this._rQlen); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         rQslice: function (start, end) { | ||||||
|  |             if (end) { | ||||||
|  |                 return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start); | ||||||
|  |             } else { | ||||||
|  |                 return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // Check to see if we must wait for 'num' bytes (default to FBU.bytes) | ||||||
|  |         // to be available in the receive queue. Return true if we need to | ||||||
|  |         // wait (and possibly print a debug message), otherwise false. | ||||||
|  |         rQwait: function (msg, num, goback) { | ||||||
|  |             var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call | ||||||
|  |             if (rQlen < num) { | ||||||
|  |                 if (goback) { | ||||||
|  |                     if (this._rQi < goback) { | ||||||
|  |                         throw new Error("rQwait cannot backup " + goback + " bytes"); | ||||||
|  |                     } | ||||||
|  |                     this._rQi -= goback; | ||||||
|  |                 } | ||||||
|  |                 return true; // true means need more data | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // Send Queue | ||||||
|  |  | ||||||
|  |         flush: function () { | ||||||
|  |             if (this._websocket.bufferedAmount !== 0) { | ||||||
|  |                 Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (this._websocket.bufferedAmount < this.maxBufferedAmount) { | ||||||
|  |                 if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) { | ||||||
|  |                     this._websocket.send(this._encode_message()); | ||||||
|  |                     this._sQlen = 0; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 return true; | ||||||
|  |             } else { | ||||||
|  |                 Util.Info("Delaying send, bufferedAmount: " + | ||||||
|  |                         this._websocket.bufferedAmount); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         send: function (arr) { | ||||||
|  |             this._sQ.set(arr, this._sQlen); | ||||||
|  |             this._sQlen += arr.length; | ||||||
|  |             return this.flush(); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         send_string: function (str) { | ||||||
|  |             this.send(str.split('').map(function (chr) { | ||||||
|  |                 return chr.charCodeAt(0); | ||||||
|  |             })); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // Event Handlers | ||||||
|  |         off: function (evt) { | ||||||
|  |             this._eventHandlers[evt] = function () {}; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         on: function (evt, handler) { | ||||||
|  |             this._eventHandlers[evt] = handler; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _allocate_buffers: function () { | ||||||
|  |             this._rQ = new Uint8Array(this._rQbufferSize); | ||||||
|  |             this._sQ = new Uint8Array(this._sQbufferSize); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         init: function (protocols, ws_schema) { | ||||||
|  |             this._allocate_buffers(); | ||||||
|  |             this._rQi = 0; | ||||||
|  |             this._websocket = null; | ||||||
|  |  | ||||||
|  |             // Check for full typed array support | ||||||
|  |             var bt = false; | ||||||
|  |             if (('Uint8Array' in window) && | ||||||
|  |                     ('set' in Uint8Array.prototype)) { | ||||||
|  |                 bt = true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Check for full binary type support in WebSockets | ||||||
|  |             // Inspired by: | ||||||
|  |             // https://github.com/Modernizr/Modernizr/issues/370 | ||||||
|  |             // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/websockets/binary.js | ||||||
|  |             var wsbt = false; | ||||||
|  |             try { | ||||||
|  |                 if (bt && ('binaryType' in WebSocket.prototype || | ||||||
|  |                            !!(new WebSocket(ws_schema + '://.').binaryType))) { | ||||||
|  |                     Util.Info("Detected binaryType support in WebSockets"); | ||||||
|  |                     wsbt = true; | ||||||
|  |                 } | ||||||
|  |             } catch (exc) { | ||||||
|  |                 // Just ignore failed test localhost connection | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Default protocols if not specified | ||||||
|  |             if (typeof(protocols) === "undefined") { | ||||||
|  |                 protocols = 'binary'; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (Array.isArray(protocols) && protocols.indexOf('binary') > -1) { | ||||||
|  |                 protocols = 'binary'; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!wsbt) { | ||||||
|  |                 throw new Error("noVNC no longer supports base64 WebSockets.  " + | ||||||
|  |                                 "Please use a browser which supports binary WebSockets."); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (protocols != 'binary') { | ||||||
|  |                 throw new Error("noVNC no longer supports base64 WebSockets.  Please " + | ||||||
|  |                                 "use the binary subprotocol instead."); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return protocols; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         open: function (uri, protocols) { | ||||||
|  |             var ws_schema = uri.match(/^([a-z]+):\/\//)[1]; | ||||||
|  |             protocols = this.init(protocols, ws_schema); | ||||||
|  |  | ||||||
|  |             this._websocket = new WebSocket(uri, protocols); | ||||||
|  |  | ||||||
|  |             if (protocols.indexOf('binary') >= 0) { | ||||||
|  |                 this._websocket.binaryType = 'arraybuffer'; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             this._websocket.onmessage = this._recv_message.bind(this); | ||||||
|  |             this._websocket.onopen = (function () { | ||||||
|  |                 Util.Debug('>> WebSock.onopen'); | ||||||
|  |                 if (this._websocket.protocol) { | ||||||
|  |                     this._mode = this._websocket.protocol; | ||||||
|  |                     Util.Info("Server choose sub-protocol: " + this._websocket.protocol); | ||||||
|  |                 } else { | ||||||
|  |                     this._mode = 'binary'; | ||||||
|  |                     Util.Error('Server select no sub-protocol!: ' + this._websocket.protocol); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (this._mode != 'binary') { | ||||||
|  |                     throw new Error("noVNC no longer supports base64 WebSockets.  Please " + | ||||||
|  |                                     "use the binary subprotocol instead."); | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 this._eventHandlers.open(); | ||||||
|  |                 Util.Debug("<< WebSock.onopen"); | ||||||
|  |             }).bind(this); | ||||||
|  |             this._websocket.onclose = (function (e) { | ||||||
|  |                 Util.Debug(">> WebSock.onclose"); | ||||||
|  |                 this._eventHandlers.close(e); | ||||||
|  |                 Util.Debug("<< WebSock.onclose"); | ||||||
|  |             }).bind(this); | ||||||
|  |             this._websocket.onerror = (function (e) { | ||||||
|  |                 Util.Debug(">> WebSock.onerror: " + e); | ||||||
|  |                 this._eventHandlers.error(e); | ||||||
|  |                 Util.Debug("<< WebSock.onerror: " + e); | ||||||
|  |             }).bind(this); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         close: function () { | ||||||
|  |             if (this._websocket) { | ||||||
|  |                 if ((this._websocket.readyState === WebSocket.OPEN) || | ||||||
|  |                         (this._websocket.readyState === WebSocket.CONNECTING)) { | ||||||
|  |                     Util.Info("Closing WebSocket connection"); | ||||||
|  |                     this._websocket.close(); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 this._websocket.onmessage = function (e) { return; }; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         // private methods | ||||||
|  |         _encode_message: function () { | ||||||
|  |             // Put in a binary arraybuffer | ||||||
|  |             // according to the spec, you can send ArrayBufferViews with the send method | ||||||
|  |             return new Uint8Array(this._sQ.buffer, 0, this._sQlen); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _expand_compact_rQ: function (min_fit) { | ||||||
|  |             var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2; | ||||||
|  |             if (resizeNeeded) { | ||||||
|  |                 if (!min_fit) { | ||||||
|  |                     // just double the size if we need to do compaction | ||||||
|  |                     this._rQbufferSize *= 2; | ||||||
|  |                 } else { | ||||||
|  |                     // otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8 | ||||||
|  |                     this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // we don't want to grow unboundedly | ||||||
|  |             if (this._rQbufferSize > MAX_RQ_GROW_SIZE) { | ||||||
|  |                 this._rQbufferSize = MAX_RQ_GROW_SIZE; | ||||||
|  |                 if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) { | ||||||
|  |                     throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (resizeNeeded) { | ||||||
|  |                 var old_rQbuffer = this._rQ.buffer; | ||||||
|  |                 this._rQmax = this._rQbufferSize / 8; | ||||||
|  |                 this._rQ = new Uint8Array(this._rQbufferSize); | ||||||
|  |                 this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi)); | ||||||
|  |             } else { | ||||||
|  |                 if (ENABLE_COPYWITHIN) { | ||||||
|  |                     this._rQ.copyWithin(0, this._rQi); | ||||||
|  |                 } else { | ||||||
|  |                     this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             this._rQlen = this._rQlen - this._rQi; | ||||||
|  |             this._rQi = 0; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _decode_message: function (data) { | ||||||
|  |             // push arraybuffer values onto the end | ||||||
|  |             var u8 = new Uint8Array(data); | ||||||
|  |             if (u8.length > this._rQbufferSize - this._rQlen) { | ||||||
|  |                 this._expand_compact_rQ(u8.length); | ||||||
|  |             } | ||||||
|  |             this._rQ.set(u8, this._rQlen); | ||||||
|  |             this._rQlen += u8.length; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _recv_message: function (e) { | ||||||
|  |             try { | ||||||
|  |                 this._decode_message(e.data); | ||||||
|  |                 if (this.rQlen() > 0) { | ||||||
|  |                     this._eventHandlers.message(); | ||||||
|  |                     // Compact the receive queue | ||||||
|  |                     if (this._rQlen == this._rQi) { | ||||||
|  |                         this._rQlen = 0; | ||||||
|  |                         this._rQi = 0; | ||||||
|  |                     } else if (this._rQlen > this._rQmax) { | ||||||
|  |                         this._expand_compact_rQ(); | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     Util.Debug("Ignoring empty message"); | ||||||
|  |                 } | ||||||
|  |             } catch (exc) { | ||||||
|  |                 var exception_str = ""; | ||||||
|  |                 if (exc.name) { | ||||||
|  |                     exception_str += "\n    name: " + exc.name + "\n"; | ||||||
|  |                     exception_str += "    message: " + exc.message + "\n"; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (typeof exc.description !== 'undefined') { | ||||||
|  |                     exception_str += "    description: " + exc.description + "\n"; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (typeof exc.stack !== 'undefined') { | ||||||
|  |                     exception_str += exc.stack; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (exception_str.length > 0) { | ||||||
|  |                     Util.Error("recv_message, caught exception: " + exception_str); | ||||||
|  |                 } else { | ||||||
|  |                     Util.Error("recv_message, caught exception: " + exc); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (typeof exc.name !== 'undefined') { | ||||||
|  |                     this._eventHandlers.error(exc.name + ": " + exc.message); | ||||||
|  |                 } else { | ||||||
|  |                     this._eventHandlers.error(exc); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | })(); | ||||||
							
								
								
									
										292
									
								
								public/novnc/include/webutil.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,292 @@ | |||||||
|  | /* | ||||||
|  |  * noVNC: HTML5 VNC client | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Copyright (C) 2013 NTT corp. | ||||||
|  |  * Licensed under MPL 2.0 (see LICENSE.txt) | ||||||
|  |  * | ||||||
|  |  * See README.md for usage and integration instructions. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /*jslint bitwise: false, white: false, browser: true, devel: true */ | ||||||
|  | /*global Util, window, document */ | ||||||
|  |  | ||||||
|  | // Globals defined here | ||||||
|  | var WebUtil = {}, $D; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Simple DOM selector by ID | ||||||
|  |  */ | ||||||
|  | if (!window.$D) { | ||||||
|  |     window.$D = function (id) { | ||||||
|  |         if (document.getElementById) { | ||||||
|  |             return document.getElementById(id); | ||||||
|  |         } else if (document.all) { | ||||||
|  |             return document.all[id]; | ||||||
|  |         } else if (document.layers) { | ||||||
|  |             return document.layers[id]; | ||||||
|  |         } | ||||||
|  |         return undefined; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * ------------------------------------------------------ | ||||||
|  |  * Namespaced in WebUtil | ||||||
|  |  * ------------------------------------------------------ | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | // init log level reading the logging HTTP param | ||||||
|  | WebUtil.init_logging = function (level) { | ||||||
|  |     "use strict"; | ||||||
|  |     if (typeof level !== "undefined") { | ||||||
|  |         Util._log_level = level; | ||||||
|  |     } else { | ||||||
|  |         var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/); | ||||||
|  |         Util._log_level = (param || ['', Util._log_level])[1]; | ||||||
|  |     } | ||||||
|  |     Util.init_logging(); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | WebUtil.dirObj = function (obj, depth, parent) { | ||||||
|  |     "use strict"; | ||||||
|  |     if (! depth) { depth = 2; } | ||||||
|  |     if (! parent) { parent = ""; } | ||||||
|  |  | ||||||
|  |     // Print the properties of the passed-in object | ||||||
|  |     var msg = ""; | ||||||
|  |     for (var i in obj) { | ||||||
|  |         if ((depth > 1) && (typeof obj[i] === "object")) { | ||||||
|  |             // Recurse attributes that are objects | ||||||
|  |             msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i); | ||||||
|  |         } else { | ||||||
|  |             //val = new String(obj[i]).replace("\n", " "); | ||||||
|  |             var val = ""; | ||||||
|  |             if (typeof(obj[i]) === "undefined") { | ||||||
|  |                 val = "undefined"; | ||||||
|  |             } else { | ||||||
|  |                 val = obj[i].toString().replace("\n", " "); | ||||||
|  |             } | ||||||
|  |             if (val.length > 30) { | ||||||
|  |                 val = val.substr(0, 30) + "..."; | ||||||
|  |             } | ||||||
|  |             msg += parent + "." + i + ": " + val + "\n"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return msg; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Read a query string variable | ||||||
|  | WebUtil.getQueryVar = function (name, defVal) { | ||||||
|  |     "use strict"; | ||||||
|  |     var re = new RegExp('.*[?&]' + name + '=([^&#]*)'), | ||||||
|  |         match = document.location.href.match(re); | ||||||
|  |     if (typeof defVal === 'undefined') { defVal = null; } | ||||||
|  |     if (match) { | ||||||
|  |         return decodeURIComponent(match[1]); | ||||||
|  |     } else { | ||||||
|  |         return defVal; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Read a hash fragment variable | ||||||
|  | WebUtil.getHashVar = function (name, defVal) { | ||||||
|  |     "use strict"; | ||||||
|  |     var re = new RegExp('.*[&#]' + name + '=([^&]*)'), | ||||||
|  |         match = document.location.hash.match(re); | ||||||
|  |     if (typeof defVal === 'undefined') { defVal = null; } | ||||||
|  |     if (match) { | ||||||
|  |         return decodeURIComponent(match[1]); | ||||||
|  |     } else { | ||||||
|  |         return defVal; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Read a variable from the fragment or the query string | ||||||
|  | // Fragment takes precedence | ||||||
|  | WebUtil.getConfigVar = function (name, defVal) { | ||||||
|  |     "use strict"; | ||||||
|  |     var val = WebUtil.getHashVar(name); | ||||||
|  |     if (val === null) { | ||||||
|  |         val = WebUtil.getQueryVar(name, defVal); | ||||||
|  |     } | ||||||
|  |     return val; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | // No days means only for this browser session | ||||||
|  | WebUtil.createCookie = function (name, value, days) { | ||||||
|  |     "use strict"; | ||||||
|  |     var date, expires; | ||||||
|  |     if (days) { | ||||||
|  |         date = new Date(); | ||||||
|  |         date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); | ||||||
|  |         expires = "; expires=" + date.toGMTString(); | ||||||
|  |     } else { | ||||||
|  |         expires = ""; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var secure; | ||||||
|  |     if (document.location.protocol === "https:") { | ||||||
|  |         secure = "; secure"; | ||||||
|  |     } else { | ||||||
|  |         secure = ""; | ||||||
|  |     } | ||||||
|  |     document.cookie = name + "=" + value + expires + "; path=/" + secure; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | WebUtil.readCookie = function (name, defaultValue) { | ||||||
|  |     "use strict"; | ||||||
|  |     var nameEQ = name + "=", | ||||||
|  |         ca = document.cookie.split(';'); | ||||||
|  |  | ||||||
|  |     for (var i = 0; i < ca.length; i += 1) { | ||||||
|  |         var c = ca[i]; | ||||||
|  |         while (c.charAt(0) === ' ') { c = c.substring(1, c.length); } | ||||||
|  |         if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); } | ||||||
|  |     } | ||||||
|  |     return (typeof defaultValue !== 'undefined') ? defaultValue : null; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | WebUtil.eraseCookie = function (name) { | ||||||
|  |     "use strict"; | ||||||
|  |     WebUtil.createCookie(name, "", -1); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Setting handling. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | WebUtil.initSettings = function (callback /*, ...callbackArgs */) { | ||||||
|  |     "use strict"; | ||||||
|  |     var callbackArgs = Array.prototype.slice.call(arguments, 1); | ||||||
|  |     if (window.chrome && window.chrome.storage) { | ||||||
|  |         window.chrome.storage.sync.get(function (cfg) { | ||||||
|  |             WebUtil.settings = cfg; | ||||||
|  |             console.log(WebUtil.settings); | ||||||
|  |             if (callback) { | ||||||
|  |                 callback.apply(this, callbackArgs); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } else { | ||||||
|  |         // No-op | ||||||
|  |         if (callback) { | ||||||
|  |             callback.apply(this, callbackArgs); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // No days means only for this browser session | ||||||
|  | WebUtil.writeSetting = function (name, value) { | ||||||
|  |     "use strict"; | ||||||
|  |     if (window.chrome && window.chrome.storage) { | ||||||
|  |         //console.log("writeSetting:", name, value); | ||||||
|  |         if (WebUtil.settings[name] !== value) { | ||||||
|  |             WebUtil.settings[name] = value; | ||||||
|  |             window.chrome.storage.sync.set(WebUtil.settings); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         localStorage.setItem(name, value); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | WebUtil.readSetting = function (name, defaultValue) { | ||||||
|  |     "use strict"; | ||||||
|  |     var value; | ||||||
|  |     if (window.chrome && window.chrome.storage) { | ||||||
|  |         value = WebUtil.settings[name]; | ||||||
|  |     } else { | ||||||
|  |         value = localStorage.getItem(name); | ||||||
|  |     } | ||||||
|  |     if (typeof value === "undefined") { | ||||||
|  |         value = null; | ||||||
|  |     } | ||||||
|  |     if (value === null && typeof defaultValue !== undefined) { | ||||||
|  |         return defaultValue; | ||||||
|  |     } else { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | WebUtil.eraseSetting = function (name) { | ||||||
|  |     "use strict"; | ||||||
|  |     if (window.chrome && window.chrome.storage) { | ||||||
|  |         window.chrome.storage.sync.remove(name); | ||||||
|  |         delete WebUtil.settings[name]; | ||||||
|  |     } else { | ||||||
|  |         localStorage.removeItem(name); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Alternate stylesheet selection | ||||||
|  |  */ | ||||||
|  | WebUtil.getStylesheets = function () { | ||||||
|  |     "use strict"; | ||||||
|  |     var links = document.getElementsByTagName("link"); | ||||||
|  |     var sheets = []; | ||||||
|  |  | ||||||
|  |     for (var i = 0; i < links.length; i += 1) { | ||||||
|  |         if (links[i].title && | ||||||
|  |             links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) { | ||||||
|  |             sheets.push(links[i]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return sheets; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // No sheet means try and use value from cookie, null sheet used to | ||||||
|  | // clear all alternates. | ||||||
|  | WebUtil.selectStylesheet = function (sheet) { | ||||||
|  |     "use strict"; | ||||||
|  |     if (typeof sheet === 'undefined') { | ||||||
|  |         sheet = 'default'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var sheets = WebUtil.getStylesheets(); | ||||||
|  |     for (var i = 0; i < sheets.length; i += 1) { | ||||||
|  |         var link = sheets[i]; | ||||||
|  |         if (link.title === sheet) { | ||||||
|  |             Util.Debug("Using stylesheet " + sheet); | ||||||
|  |             link.disabled = false; | ||||||
|  |         } else { | ||||||
|  |             //Util.Debug("Skipping stylesheet " + link.title); | ||||||
|  |             link.disabled = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return sheet; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | WebUtil.injectParamIfMissing = function (path, param, value) { | ||||||
|  |     // force pretend that we're dealing with a relative path | ||||||
|  |     // (assume that we wanted an extra if we pass one in) | ||||||
|  |     path = "/" + path; | ||||||
|  |  | ||||||
|  |     var elem = document.createElement('a'); | ||||||
|  |     elem.href = path; | ||||||
|  |  | ||||||
|  |     var param_eq = encodeURIComponent(param) + "="; | ||||||
|  |     var query; | ||||||
|  |     if (elem.search) { | ||||||
|  |         query = elem.search.slice(1).split('&'); | ||||||
|  |     } else { | ||||||
|  |         query = []; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!query.some(function (v) { return v.startsWith(param_eq); })) { | ||||||
|  |         query.push(param_eq + encodeURIComponent(value)); | ||||||
|  |         elem.search = "?" + query.join("&"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // some browsers (e.g. IE11) may occasionally omit the leading slash | ||||||
|  |     // in the elem.pathname string. Handle that case gracefully. | ||||||
|  |     if (elem.pathname.charAt(0) == "/") { | ||||||
|  |         return elem.pathname.slice(1) + elem.search + elem.hash; | ||||||
|  |     } else { | ||||||
|  |         return elem.pathname + elem.search + elem.hash; | ||||||
|  |     } | ||||||
|  | }; | ||||||
							
								
								
									
										197
									
								
								public/novnc/karma.conf.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,197 @@ | |||||||
|  | // Karma configuration | ||||||
|  |  | ||||||
|  | module.exports = function(config) { | ||||||
|  |   /*var customLaunchers = { | ||||||
|  |     sl_chrome_win7: { | ||||||
|  |       base: 'SauceLabs', | ||||||
|  |       browserName: 'chrome', | ||||||
|  |       platform: 'Windows 7' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     sl_firefox30_linux: { | ||||||
|  |       base: 'SauceLabs', | ||||||
|  |       browserName: 'firefox', | ||||||
|  |       version: '30', | ||||||
|  |       platform: 'Linux' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     sl_firefox26_linux: { | ||||||
|  |       base: 'SauceLabs', | ||||||
|  |       browserName: 'firefox', | ||||||
|  |       version: 26, | ||||||
|  |       platform: 'Linux' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     sl_windows7_ie10: { | ||||||
|  |       base: 'SauceLabs', | ||||||
|  |       browserName: 'internet explorer', | ||||||
|  |       platform: 'Windows 7', | ||||||
|  |       version: '10' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     sl_windows81_ie11: { | ||||||
|  |       base: 'SauceLabs', | ||||||
|  |       browserName: 'internet explorer', | ||||||
|  |       platform: 'Windows 8.1', | ||||||
|  |       version: '11' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     sl_osxmavericks_safari7: { | ||||||
|  |       base: 'SauceLabs', | ||||||
|  |       browserName: 'safari', | ||||||
|  |       platform: 'OS X 10.9', | ||||||
|  |       version: '7' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     sl_osxmtnlion_safari6: { | ||||||
|  |       base: 'SauceLabs', | ||||||
|  |       browserName: 'safari', | ||||||
|  |       platform: 'OS X 10.8', | ||||||
|  |       version: '6' | ||||||
|  |     } | ||||||
|  |   };*/ | ||||||
|  |  | ||||||
|  |   var customLaunchers = {}; | ||||||
|  |   var browsers = []; | ||||||
|  |   var useSauce = false; | ||||||
|  |  | ||||||
|  |   if (process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY) { | ||||||
|  |     useSauce = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (useSauce && process.env.TEST_BROWSER_NAME && process.env.TEST_BROWSER_NAME != 'PhantomJS') { | ||||||
|  |     var names = process.env.TEST_BROWSER_NAME.split(','); | ||||||
|  |     var platforms = process.env.TEST_BROWSER_OS.split(','); | ||||||
|  |     var versions = []; | ||||||
|  |     if (process.env.TEST_BROWSER_VERSION) { | ||||||
|  |       versions = process.env.TEST_BROWSER_VERSION.split(','); | ||||||
|  |     } else { | ||||||
|  |       versions = [null]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (var i = 0; i < names.length; i++) { | ||||||
|  |       for (var j = 0; j < platforms.length; j++) { | ||||||
|  |         for (var k = 0; k < versions.length; k++) { | ||||||
|  |           var launcher_name = 'sl_' + platforms[j].replace(/[^a-zA-Z0-9]/g, '') + '_' + names[i]; | ||||||
|  |           if (versions[k]) { | ||||||
|  |             launcher_name += '_' + versions[k]; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           customLaunchers[launcher_name] = { | ||||||
|  |             base: 'SauceLabs', | ||||||
|  |             browserName: names[i], | ||||||
|  |             platform: platforms[j], | ||||||
|  |           }; | ||||||
|  |  | ||||||
|  |           if (versions[i]) { | ||||||
|  |             customLaunchers[launcher_name].version = versions[k]; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     browsers = Object.keys(customLaunchers); | ||||||
|  |   } else { | ||||||
|  |     useSauce = false; | ||||||
|  |     browsers = ['PhantomJS']; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   var my_conf = { | ||||||
|  |  | ||||||
|  |     // base path that will be used to resolve all patterns (eg. files, exclude) | ||||||
|  |     basePath: '', | ||||||
|  |  | ||||||
|  |     // frameworks to use | ||||||
|  |     // available frameworks: https://npmjs.org/browse/keyword/karma-adapter | ||||||
|  |     frameworks: ['mocha', 'sinon', 'chai', 'sinon-chai'], | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // list of files / patterns to load in the browser (loaded in order) | ||||||
|  |     files: [ | ||||||
|  |       'tests/fake.*.js', | ||||||
|  |       'tests/assertions.js', | ||||||
|  |       'include/util.js',  // load first to avoid issues, since methods are called immediately | ||||||
|  |       //'../include/*.js', | ||||||
|  |       'include/base64.js', | ||||||
|  |       'include/keysym.js', | ||||||
|  |       'include/keysymdef.js', | ||||||
|  |       'include/keyboard.js', | ||||||
|  |       'include/input.js', | ||||||
|  |       'include/websock.js', | ||||||
|  |       'include/rfb.js', | ||||||
|  |       'include/des.js', | ||||||
|  |       'include/display.js', | ||||||
|  |       'include/inflator.js', | ||||||
|  |       'tests/test.*.js' | ||||||
|  |     ], | ||||||
|  |  | ||||||
|  |     client: { | ||||||
|  |       mocha: { | ||||||
|  |         'ui': 'bdd' | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     // list of files to exclude | ||||||
|  |     exclude: [ | ||||||
|  |       '../include/playback.js', | ||||||
|  |       '../include/ui.js' | ||||||
|  |     ], | ||||||
|  |  | ||||||
|  |     customLaunchers: customLaunchers, | ||||||
|  |  | ||||||
|  |     // start these browsers | ||||||
|  |     // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher | ||||||
|  |     browsers: browsers, | ||||||
|  |  | ||||||
|  |     // preprocess matching files before serving them to the browser | ||||||
|  |     // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor | ||||||
|  |     preprocessors: { | ||||||
|  |  | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // test results reporter to use | ||||||
|  |     // possible values: 'dots', 'progress' | ||||||
|  |     // available reporters: https://npmjs.org/browse/keyword/karma-reporter | ||||||
|  |     reporters: ['mocha', 'saucelabs'], | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // web server port | ||||||
|  |     port: 9876, | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // enable / disable colors in the output (reporters and logs) | ||||||
|  |     colors: true, | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // level of logging | ||||||
|  |     // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG | ||||||
|  |     logLevel: config.LOG_INFO, | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // enable / disable watching file and executing tests whenever any file changes | ||||||
|  |     autoWatch: false, | ||||||
|  |  | ||||||
|  |     // Continuous Integration mode | ||||||
|  |     // if true, Karma captures browsers, runs the tests and exits | ||||||
|  |     singleRun: true, | ||||||
|  |  | ||||||
|  |     // Increase timeout in case connection is slow/we run more browsers than possible | ||||||
|  |     // (we currently get 3 for free, and we try to run 7, so it can take a while) | ||||||
|  |     captureTimeout: 240000, | ||||||
|  |  | ||||||
|  |     // similarly to above | ||||||
|  |     browserNoActivityTimeout: 100000, | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   if (useSauce) { | ||||||
|  |     my_conf.captureTimeout = 0; // use SL timeout | ||||||
|  |     my_conf.sauceLabs = { | ||||||
|  |       testName: 'noVNC Tests (all)', | ||||||
|  |       startConnect: false, | ||||||
|  |       tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   config.set(my_conf); | ||||||
|  | }; | ||||||
							
								
								
									
										50
									
								
								public/novnc/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,50 @@ | |||||||
|  | { | ||||||
|  |   "name": "noVNC", | ||||||
|  |   "version": "0.6.2", | ||||||
|  |   "description": "An HTML5 VNC client", | ||||||
|  |   "main": "karma.conf.js", | ||||||
|  |   "directories": { | ||||||
|  |     "doc": "docs", | ||||||
|  |     "test": "tests" | ||||||
|  |   }, | ||||||
|  |   "scripts": { | ||||||
|  |     "test": "PATH=$PATH:node_modules/karma/bin karma start karma.conf.js" | ||||||
|  |   }, | ||||||
|  |   "repository": { | ||||||
|  |     "type": "git", | ||||||
|  |     "url": "https://github.com/kanaka/noVNC.git" | ||||||
|  |   }, | ||||||
|  |   "author": "Joel Martin <github@martintribe.org> (https://github.com/kanaka)", | ||||||
|  |   "contributors": [ | ||||||
|  |     "Solly Ross <sross@redhat.com> (https://github.com/directxman12)", | ||||||
|  |     "Peter Åstrand <astrand@cendio.se> (https://github.com/astrand)", | ||||||
|  |     "Samuel Mannehed <samuel@cendio.se> (https://github.com/samhed)" | ||||||
|  |   ], | ||||||
|  |   "license": "MPL 2.0", | ||||||
|  |   "bugs": { | ||||||
|  |     "url": "https://github.com/kanaka/noVNC/issues" | ||||||
|  |   }, | ||||||
|  |   "homepage": "https://github.com/kanaka/noVNC", | ||||||
|  |   "devDependencies": { | ||||||
|  |     "ansi": "^0.3.0", | ||||||
|  |     "casperjs": "^1.1.0-beta3", | ||||||
|  |     "chai": "^2.1.0", | ||||||
|  |     "commander": "^2.6.0", | ||||||
|  |     "karma": "^0.12.31", | ||||||
|  |     "karma-chai": "^0.1.0", | ||||||
|  |     "karma-mocha": "^0.1.10", | ||||||
|  |     "karma-mocha-reporter": "^1.0.0", | ||||||
|  |     "karma-phantomjs-launcher": "^0.1.4", | ||||||
|  |     "karma-sauce-launcher": "^0.2.10", | ||||||
|  |     "karma-sinon": "^1.0.4", | ||||||
|  |     "karma-sinon-chai-latest": "^0.1.0", | ||||||
|  |     "mocha": "^2.1.0", | ||||||
|  |     "open": "^0.0.5", | ||||||
|  |     "phantom": "^0.7.2", | ||||||
|  |     "phantomjs": "^1.9.15", | ||||||
|  |     "sinon": "^1.12.2", | ||||||
|  |     "sinon-chai": "^2.7.0", | ||||||
|  |     "spooky": "^0.2.5", | ||||||
|  |     "temp": "^0.8.1" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								public/novnc/tests/arrays.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,39 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |     <head> | ||||||
|  |         <title>Javascript Arrays Performance Test</title> | ||||||
|  |         <!-- | ||||||
|  |         <script type='text/javascript'  | ||||||
|  |             src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> | ||||||
|  |         --> | ||||||
|  |         <script src="../include/util.js"></script> | ||||||
|  |         <script src="../include/webutil.js"></script> | ||||||
|  |         <script src="browser.js"></script> | ||||||
|  |         <script src="stats.js"></script> | ||||||
|  |         <script src="arrays.js"></script> | ||||||
|  |     </head> | ||||||
|  |     <body> | ||||||
|  |         <h3>Javascript Arrays Performance Test</h3> | ||||||
|  |         Iterations: <input id='iterations' style='width:50'>  | ||||||
|  |         Array Size: <input id='arraySize' style='width:40'>*1024  | ||||||
|  |  | ||||||
|  |         <input id='startButton' type='button' value='Run Tests' | ||||||
|  |             onclick="begin();">  | ||||||
|  |  | ||||||
|  |         <br><br> | ||||||
|  |         Results:<br> | ||||||
|  |         <textarea id="messages" style="font-size: 9;" cols=80 rows=50></textarea> | ||||||
|  |         </br> | ||||||
|  |         <canvas id="canvas" style="display: none;"></canvas> | ||||||
|  |  | ||||||
|  |     </body> | ||||||
|  |  | ||||||
|  |     <script> | ||||||
|  |         var verbose = true; | ||||||
|  |         window.onload = function() { | ||||||
|  |             vmessage("in onload"); | ||||||
|  |             init(); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
|  | </html> | ||||||
							
								
								
									
										375
									
								
								public/novnc/tests/arrays.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,375 @@ | |||||||
|  | /* | ||||||
|  |  * Javascript binary array performance tests | ||||||
|  |  * Copyright (C) 2012 Joel Martin | ||||||
|  |  * Licensed under MPL 2.0 (see LICENSE.txt) | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | var ctx, i, j, randlist, | ||||||
|  |     new_normal, new_imageData, new_arrayBuffer, | ||||||
|  |     browser = Browser.browser + " " + | ||||||
|  |               Browser.version + " on " + | ||||||
|  |               Browser.OS, | ||||||
|  |     do_imageData   = false, | ||||||
|  |     do_arrayBuffer = false, | ||||||
|  |     conf = { | ||||||
|  |         'create_cnt'     : 2000, | ||||||
|  |         'read_cnt'       : 5000000, | ||||||
|  |         'write_cnt'      : 5000000, | ||||||
|  |         'iterations'     : 0, | ||||||
|  |         'order_l1'       : [browser], | ||||||
|  |         'order_l2'       : ['normal', | ||||||
|  |                             'imageData', | ||||||
|  |                             'arrayBuffer'], | ||||||
|  |         'order_l3'       : ['create', | ||||||
|  |                             'sequentialRead', | ||||||
|  |                             'randomRead', | ||||||
|  |                             'sequentialWrite'] | ||||||
|  |     }, | ||||||
|  |     stats = {}, | ||||||
|  |     testFunc = {}, | ||||||
|  |     iteration, arraySize; | ||||||
|  |  | ||||||
|  | var newline = "\n"; | ||||||
|  | if (Util.Engine.trident) { | ||||||
|  |     var newline = "<br>\n"; | ||||||
|  | } | ||||||
|  | function message(str) { | ||||||
|  |     //console.log(str); | ||||||
|  |     cell = $D('messages'); | ||||||
|  |     cell.textContent += str + newline; | ||||||
|  |     cell.scrollTop = cell.scrollHeight; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function vmessage(str) { | ||||||
|  |     if (verbose) { | ||||||
|  |         message(str); | ||||||
|  |     } else { | ||||||
|  |         console.log(str); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | new_normal = function() { | ||||||
|  |     var arr = [], i; | ||||||
|  |     for (i = 0; i < arraySize; i++) { | ||||||
|  |         arr[i] = 0; | ||||||
|  |     } | ||||||
|  |     return arr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Will be overridden with real function */ | ||||||
|  | new_imageData = function() { | ||||||
|  |     throw("imageData not supported"); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | new_imageData_createImageData = function() { | ||||||
|  |     var imageData = ctx.createImageData(1024/4, arraySize / 1024); | ||||||
|  |     return imageData.data; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | new_imageData_getImageData = function() { | ||||||
|  |     var imageData = ctx.getImageData(0, 0, 1024/4, arraySize / 1024), | ||||||
|  |         arr = imageData.data; | ||||||
|  |     for (i = 0; i < arraySize; i++) { | ||||||
|  |         arr[i] = 0; | ||||||
|  |     } | ||||||
|  |     return arr; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | new_arrayBuffer = function() { | ||||||
|  |     var arr = new ArrayBuffer(arraySize); | ||||||
|  |     return new Uint8Array(arr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function init_randlist() { | ||||||
|  |     randlist = []; | ||||||
|  |     for (var i=0; i < arraySize; i++) { | ||||||
|  |         randlist[i] = parseInt(Math.random() * 256, 10); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | function copy_randlist(arr) { | ||||||
|  |     for (var i=0; i < arraySize; i++) { | ||||||
|  |         arr[i] = randlist[i]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function begin() { | ||||||
|  |     var i, j; | ||||||
|  |     conf.iterations = parseInt($D('iterations').value, 10); | ||||||
|  |     arraySize = parseInt($D('arraySize').value, 10) * 1024; | ||||||
|  |  | ||||||
|  |     init_randlist(); | ||||||
|  |  | ||||||
|  |     // TODO: randomize test_list | ||||||
|  |      | ||||||
|  |     stats = {}; | ||||||
|  |     for (i = 0; i < conf.order_l2.length; i++) { | ||||||
|  |         stats[conf.order_l2[i]] = {}; | ||||||
|  |         for (j = 0; j < conf.order_l3.length; j++) { | ||||||
|  |             stats[conf.order_l2[i]][conf.order_l3[j]] = []; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $D('startButton').value = "Running"; | ||||||
|  |     $D('startButton').disabled = true; | ||||||
|  |  | ||||||
|  |     message("running " + conf.iterations + " test iterations"); | ||||||
|  |     iteration = 1; | ||||||
|  |     setTimeout(run_next_iteration, 250); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function finish() { | ||||||
|  |     var totalTime, arrayType, testType, times; | ||||||
|  |     message("tests finished"); | ||||||
|  |  | ||||||
|  |     for (j = 0; j < conf.order_l3.length; j++) { | ||||||
|  |         testType = conf.order_l3[j]; | ||||||
|  |         message("Test '" + testType + "'"); | ||||||
|  |         for (i = 0; i < conf.order_l2.length; i++) { | ||||||
|  |             arrayType = conf.order_l2[i]; | ||||||
|  |             message("  Array Type '" + arrayType); | ||||||
|  |             times = stats[arrayType][testType]; | ||||||
|  |             message("    Average : " + times.mean() + "ms" + | ||||||
|  |                     " (Total: " + times.sum() + "ms)"); | ||||||
|  |             message("    Min/Max : " + times.min() + "ms/" + | ||||||
|  |                                         times.max() + "ms"); | ||||||
|  |             message("    StdDev  : " + times.stdDev() + "ms"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     vmessage("array_chart.py JSON data:"); | ||||||
|  |     chart_data = {'conf' : conf, 'stats' : { } }; | ||||||
|  |     chart_data.stats[browser] = stats; | ||||||
|  |     chart_data.stats['next_browser'] = {}; | ||||||
|  |     vmessage(JSON.stringify(chart_data, null, 2)); | ||||||
|  |  | ||||||
|  |     $D('startButton').disabled = false; | ||||||
|  |     $D('startButton').value = "Run Tests"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function run_next_iteration() { | ||||||
|  |     var arrayType, testType, deltaTime; | ||||||
|  |      | ||||||
|  |     for (i = 0; i < conf.order_l2.length; i++) { | ||||||
|  |         arrayType = conf.order_l2[i]; | ||||||
|  |         if (arrayType === 'imageData' && (!do_imageData)) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (arrayType === 'arrayBuffer' && (!do_arrayBuffer)) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         for (j = 0; j < conf.order_l3.length; j++) { | ||||||
|  |             testType = conf.order_l3[j]; | ||||||
|  |  | ||||||
|  |             deltaTime = testFunc[arrayType + "_" + testType](); | ||||||
|  |  | ||||||
|  |             stats[arrayType][testType].push(deltaTime); | ||||||
|  |             vmessage("test " + (arrayType + "_" + testType) + | ||||||
|  |                         " time: " + (deltaTime) + "ms"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     message("finished test iteration " + iteration); | ||||||
|  |     if (iteration >= conf.iterations) { | ||||||
|  |         setTimeout(finish, 1); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     iteration++; | ||||||
|  |     setTimeout(run_next_iteration, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |     * Test functions | ||||||
|  |     */ | ||||||
|  |  | ||||||
|  | testFunc["normal_create"] = function() { | ||||||
|  |     var cnt, arrNormal, startTime, endTime; | ||||||
|  |     vmessage("create normal array " + conf.create_cnt + "x, initialized to 0"); | ||||||
|  |  | ||||||
|  |     startTime = (new Date()).getTime(); | ||||||
|  |     for (cnt = 0; cnt < conf.create_cnt; cnt++) { | ||||||
|  |         arrNormal = new_normal(); | ||||||
|  |     } | ||||||
|  |     endTime = (new Date()).getTime(); | ||||||
|  |  | ||||||
|  |     return endTime - startTime; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | testFunc["imageData_create"] = function() { | ||||||
|  |     var cnt, arrImage, startTime, endTime; | ||||||
|  |     vmessage("create imageData array " + conf.create_cnt + "x, initialized to 0"); | ||||||
|  |  | ||||||
|  |     startTime = (new Date()).getTime(); | ||||||
|  |     for (cnt = 0; cnt < conf.create_cnt; cnt++) { | ||||||
|  |         arrImage = new_imageData(); | ||||||
|  |     } | ||||||
|  |     endTime = (new Date()).getTime(); | ||||||
|  |  | ||||||
|  |     if (arrImage[103] !== 0) { | ||||||
|  |         message("Initialization failed, arrImage[103] is: " + arrImage[103]); | ||||||
|  |         throw("Initialization failed, arrImage[103] is: " + arrImage[103]); | ||||||
|  |     } | ||||||
|  |     return endTime - startTime; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | testFunc["arrayBuffer_create"] = function() { | ||||||
|  |     var cnt, arrBuffer, startTime, endTime; | ||||||
|  |     vmessage("create arrayBuffer array " + conf.create_cnt + "x, initialized to 0"); | ||||||
|  |  | ||||||
|  |     startTime = (new Date()).getTime(); | ||||||
|  |     for (cnt = 0; cnt < conf.create_cnt; cnt++) { | ||||||
|  |         arrBuffer = new_arrayBuffer(); | ||||||
|  |     } | ||||||
|  |     endTime = (new Date()).getTime(); | ||||||
|  |  | ||||||
|  |     if (arrBuffer[103] !== 0) { | ||||||
|  |         message("Initialization failed, arrBuffer[103] is: " + arrBuffer[103]); | ||||||
|  |         throw("Initialization failed, arrBuffer[103] is: " + arrBuffer[103]); | ||||||
|  |     } | ||||||
|  |     return endTime - startTime; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | function test_sequentialRead(arr) { | ||||||
|  |     var i, j, cnt, startTime, endTime; | ||||||
|  |     /* Initialize the array */ | ||||||
|  |     copy_randlist(arr); | ||||||
|  |  | ||||||
|  |     startTime = (new Date()).getTime(); | ||||||
|  |     i = 0; | ||||||
|  |     j = 0; | ||||||
|  |     for (cnt = 0; cnt < conf.read_cnt; cnt++) { | ||||||
|  |         j = arr[i]; | ||||||
|  |         i++; | ||||||
|  |         if (i >= arraySize) { | ||||||
|  |             i = 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     endTime = (new Date()).getTime(); | ||||||
|  |  | ||||||
|  |     return endTime - startTime; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function test_randomRead(arr) { | ||||||
|  |     var i, cnt, startTime, endTime; | ||||||
|  |     /* Initialize the array */ | ||||||
|  |     copy_randlist(arr);   // used as jumplist | ||||||
|  |  | ||||||
|  |     startTime = (new Date()).getTime(); | ||||||
|  |     i = 0; | ||||||
|  |     for (cnt = 0; cnt < conf.read_cnt; cnt++) { | ||||||
|  |         i = (arr[i] + cnt) % arraySize; | ||||||
|  |     } | ||||||
|  |     endTime = (new Date()).getTime(); | ||||||
|  |  | ||||||
|  |     return endTime - startTime; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function test_sequentialWrite(arr) { | ||||||
|  |     var i, cnt, startTime, endTime; | ||||||
|  |     /* Initialize the array */ | ||||||
|  |     copy_randlist(arr); | ||||||
|  |  | ||||||
|  |     startTime = (new Date()).getTime(); | ||||||
|  |     i = 0; | ||||||
|  |     for (cnt = 0; cnt < conf.write_cnt; cnt++) { | ||||||
|  |         arr[i] = (cnt % 256); | ||||||
|  |         i++; | ||||||
|  |         if (i >= arraySize) { | ||||||
|  |             i = 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     endTime = (new Date()).getTime(); | ||||||
|  |  | ||||||
|  |     return endTime - startTime; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Sequential Read Tests */ | ||||||
|  | testFunc["normal_sequentialRead"] = function() { | ||||||
|  |     vmessage("read normal array " + conf.read_cnt + "x"); | ||||||
|  |     return test_sequentialRead(new_normal()); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | testFunc["imageData_sequentialRead"] = function() { | ||||||
|  |     vmessage("read imageData array " + conf.read_cnt + "x"); | ||||||
|  |     return test_sequentialRead(new_imageData()); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | testFunc["arrayBuffer_sequentialRead"] = function() { | ||||||
|  |     vmessage("read arrayBuffer array " + conf.read_cnt + "x"); | ||||||
|  |     return test_sequentialRead(new_arrayBuffer()); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Random Read Tests */ | ||||||
|  | testFunc["normal_randomRead"] = function() { | ||||||
|  |     vmessage("read normal array " + conf.read_cnt + "x"); | ||||||
|  |     return test_randomRead(new_normal()); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | testFunc["imageData_randomRead"] = function() { | ||||||
|  |     vmessage("read imageData array " + conf.read_cnt + "x"); | ||||||
|  |     return test_randomRead(new_imageData()); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | testFunc["arrayBuffer_randomRead"] = function() { | ||||||
|  |     vmessage("read arrayBuffer array " + conf.read_cnt + "x"); | ||||||
|  |     return test_randomRead(new_arrayBuffer()); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Sequential Write Tests */ | ||||||
|  | testFunc["normal_sequentialWrite"] = function() { | ||||||
|  |     vmessage("write normal array " + conf.write_cnt + "x"); | ||||||
|  |     return test_sequentialWrite(new_normal()); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | testFunc["imageData_sequentialWrite"] = function() { | ||||||
|  |     vmessage("write imageData array " + conf.write_cnt + "x"); | ||||||
|  |     return test_sequentialWrite(new_imageData()); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | testFunc["arrayBuffer_sequentialWrite"] = function() { | ||||||
|  |     vmessage("write arrayBuffer array " + conf.write_cnt + "x"); | ||||||
|  |     return test_sequentialWrite(new_arrayBuffer()); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | init = function() { | ||||||
|  |     vmessage(">> init"); | ||||||
|  |  | ||||||
|  |     $D('iterations').value = 10; | ||||||
|  |     $D('arraySize').value = 10; | ||||||
|  |     arraySize = parseInt($D('arraySize').value, 10) * 1024; | ||||||
|  |  | ||||||
|  |     message("Browser: " + browser); | ||||||
|  |  | ||||||
|  |     /* Determine browser binary array support */ | ||||||
|  |     try { | ||||||
|  |         ctx = $D('canvas').getContext('2d'); | ||||||
|  |         new_imageData = new_imageData_createImageData; | ||||||
|  |         new_imageData(); | ||||||
|  |         do_imageData = true; | ||||||
|  |     } catch (exc) { | ||||||
|  |         vmessage("createImageData not supported: " + exc); | ||||||
|  |         try { | ||||||
|  |             ctx = $D('canvas').getContext('2d'); | ||||||
|  |             new_imageData = new_imageData_getImageData; | ||||||
|  |             blah = new_imageData(); | ||||||
|  |             do_imageData = true; | ||||||
|  |         } catch (exc) { | ||||||
|  |             vmessage("getImageData not supported: " + exc); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (! do_imageData) { | ||||||
|  |         message("imageData arrays not supported"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     try { | ||||||
|  |         new_arrayBuffer(); | ||||||
|  |         do_arrayBuffer = true; | ||||||
|  |     } catch (exc) { | ||||||
|  |         vmessage("Typed Arrays not supported: " + exc); | ||||||
|  |     } | ||||||
|  |     if (! do_arrayBuffer) { | ||||||
|  |         message("Typed Arrays (ArrayBuffers) not suppoted"); | ||||||
|  |     } | ||||||
|  |     vmessage("<< init"); | ||||||
|  | } | ||||||
							
								
								
									
										98
									
								
								public/novnc/tests/assertions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,98 @@ | |||||||
|  | // some useful assertions for noVNC | ||||||
|  | chai.use(function (_chai, utils) { | ||||||
|  |     _chai.Assertion.addMethod('displayed', function (target_data) { | ||||||
|  |         var obj = this._obj; | ||||||
|  |         var data_cl = obj._drawCtx.getImageData(0, 0, obj._viewportLoc.w, obj._viewportLoc.h).data; | ||||||
|  |         // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray, so work around that | ||||||
|  |         var data = new Uint8Array(data_cl); | ||||||
|  |         var same = true; | ||||||
|  |         var len = data_cl.length; | ||||||
|  |         if (len != target_data.length) { | ||||||
|  |             same = false; | ||||||
|  |         } else { | ||||||
|  |             for (var i = 0; i < len; i++) { | ||||||
|  |                 if (data[i] != target_data[i]) { | ||||||
|  |                     same = false; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!same) { | ||||||
|  |             console.log("expected data: %o, actual data: %o", target_data, data); | ||||||
|  |         } | ||||||
|  |         this.assert(same, | ||||||
|  |             "expected #{this} to have displayed the image #{exp}, but instead it displayed #{act}", | ||||||
|  |             "expected #{this} not to have displayed the image #{act}", | ||||||
|  |             target_data, | ||||||
|  |             data); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     _chai.Assertion.addMethod('sent', function (target_data) { | ||||||
|  |         var obj = this._obj; | ||||||
|  |         obj.inspect = function () { | ||||||
|  |             var res = { _websocket: obj._websocket, rQi: obj._rQi, _rQ: new Uint8Array(obj._rQ.buffer, 0, obj._rQlen), | ||||||
|  |                         _sQ: new Uint8Array(obj._sQ.buffer, 0, obj._sQlen) }; | ||||||
|  |             res.prototype = obj; | ||||||
|  |             return res; | ||||||
|  |         }; | ||||||
|  |         var data = obj._websocket._get_sent_data(); | ||||||
|  |         var same = true; | ||||||
|  |         for (var i = 0; i < obj.length; i++) { | ||||||
|  |             if (data[i] != target_data[i]) { | ||||||
|  |                 same = false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!same) { | ||||||
|  |             console.log("expected data: %o, actual data: %o", target_data, data); | ||||||
|  |         } | ||||||
|  |         this.assert(same, | ||||||
|  |             "expected #{this} to have sent the data #{exp}, but it actually sent #{act}", | ||||||
|  |             "expected #{this} not to have sent the data #{act}", | ||||||
|  |             Array.prototype.slice.call(target_data), | ||||||
|  |             Array.prototype.slice.call(data)); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     _chai.Assertion.addProperty('array', function () { | ||||||
|  |         utils.flag(this, 'array', true); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     _chai.Assertion.overwriteMethod('equal', function (_super) { | ||||||
|  |         return function assertArrayEqual(target) { | ||||||
|  |             if (utils.flag(this, 'array')) { | ||||||
|  |                 var obj = this._obj; | ||||||
|  |  | ||||||
|  |                 var i; | ||||||
|  |                 var same = true; | ||||||
|  |  | ||||||
|  |                 if (utils.flag(this, 'deep')) { | ||||||
|  |                     for (i = 0; i < obj.length; i++) { | ||||||
|  |                         if (!utils.eql(obj[i], target[i])) { | ||||||
|  |                             same = false; | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     this.assert(same, | ||||||
|  |                         "expected #{this} to have elements deeply equal to #{exp}", | ||||||
|  |                         "expected #{this} not to have elements deeply equal to #{exp}", | ||||||
|  |                         Array.prototype.slice.call(target)); | ||||||
|  |                 } else { | ||||||
|  |                     for (i = 0; i < obj.length; i++) { | ||||||
|  |                         if (obj[i] != target[i]) { | ||||||
|  |                             same = false; | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     this.assert(same, | ||||||
|  |                         "expected #{this} to have elements equal to #{exp}", | ||||||
|  |                         "expected #{this} not to have elements equal to #{exp}", | ||||||
|  |                         Array.prototype.slice.call(target)); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 _super.apply(this, arguments); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |     }); | ||||||
|  | }); | ||||||
							
								
								
									
										91
									
								
								public/novnc/tests/base64.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,91 @@ | |||||||
|  | <!DOCTYPE html>  | ||||||
|  | <html xmlns="http://www.w3.org/1999/xhtml">  | ||||||
|  |   <head>  | ||||||
|  |     <title>Native Base64 Tests</title>  | ||||||
|  |     <script src="../include/util.js"></script>  | ||||||
|  |     <script src="../include/webutil.js"></script>  | ||||||
|  |     <script src="../include/base64.js"></script>  | ||||||
|  |   </head>  | ||||||
|  |   <body>  | ||||||
|  |         <h1>Native Base64 Tests</h1>  | ||||||
|  |   | ||||||
|  |         <br>  | ||||||
|  |         Messages:<br>  | ||||||
|  |         <textarea id="debug" style="font-size: 9px;" cols=80 rows=25></textarea>  | ||||||
|  |      | ||||||
|  |         <br> | ||||||
|  |   </body>  | ||||||
|  |   | ||||||
|  | <script>  | ||||||
|  |     function debug(str) { | ||||||
|  |         console.log(str); | ||||||
|  |         cell = $D('debug'); | ||||||
|  |         cell.textContent += str + "\n"; | ||||||
|  |         cell.scrollTop = cell.scrollHeight; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function assertRun(code, result) { | ||||||
|  |         try { | ||||||
|  |             var actual = eval(code); | ||||||
|  |         } catch (exc) { | ||||||
|  |             debug("FAIL: '" + code + "' threw an exception"); | ||||||
|  |             fail += 1; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         if (actual !== result) { | ||||||
|  |             debug("FAIL: '" + code + "' returned '" + actual + "', expected '" + result + "'"); | ||||||
|  |             fail += 1; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         debug("PASS: '" + code + "' returned expected '" + result +"'"); | ||||||
|  |         pass += 1; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function Base64_decode(data) { | ||||||
|  |         var arr = Base64.decode (data); | ||||||
|  |         return arr.map(function (num) { | ||||||
|  |             return String.fromCharCode(num); } ).join(''); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     window.onload = function() { | ||||||
|  |         var str; | ||||||
|  |         debug('onload'); | ||||||
|  |         fail = 0; | ||||||
|  |         pass = 0; | ||||||
|  |         assertRun('window.btoa("hello world")', 'aGVsbG8gd29ybGQ='); | ||||||
|  |         assertRun('window.btoa("a")', 'YQ=='); | ||||||
|  |         assertRun('window.btoa("ab")', 'YWI='); | ||||||
|  |         assertRun('window.btoa("abc")', 'YWJj'); | ||||||
|  |         assertRun('window.btoa("abcd")', 'YWJjZA=='); | ||||||
|  |         assertRun('window.btoa("abcde")', 'YWJjZGU='); | ||||||
|  |         assertRun('window.btoa("abcdef")', 'YWJjZGVm'); | ||||||
|  |         assertRun('window.btoa("abcdefg")', 'YWJjZGVmZw=='); | ||||||
|  |         assertRun('window.btoa("abcdefgh")', 'YWJjZGVmZ2g='); | ||||||
|  |  | ||||||
|  |         assertRun('window.atob("aGVsbG8gd29ybGQ=")', 'hello world'); | ||||||
|  |         assertRun('Base64_decode("aGVsbG8gd29ybGQ=")', 'hello world'); | ||||||
|  |         assertRun('window.atob("YQ==")', 'a'); | ||||||
|  |         assertRun('Base64_decode("YQ==")', 'a'); | ||||||
|  |         assertRun('window.atob("YWI=")', 'ab'); | ||||||
|  |         assertRun('Base64_decode("YWI=")', 'ab'); | ||||||
|  |         assertRun('window.atob("YWJj")', 'abc'); | ||||||
|  |         assertRun('Base64_decode("YWJj")', 'abc'); | ||||||
|  |         assertRun('window.atob("YWJjZA==")', 'abcd'); | ||||||
|  |         assertRun('Base64_decode("YWJjZA==")', 'abcd'); | ||||||
|  |         assertRun('window.atob("YWJjZGU=")', 'abcde'); | ||||||
|  |         assertRun('Base64_decode("YWJjZGU=")', 'abcde'); | ||||||
|  |         assertRun('window.atob("YWJjZGVm")', 'abcdef'); | ||||||
|  |         assertRun('Base64_decode("YWJjZGVm")', 'abcdef'); | ||||||
|  |  | ||||||
|  |         assertRun('typeof window.btoa', 'function'); | ||||||
|  |         assertRun('window.btoa("")', ''); | ||||||
|  |         assertRun('window.btoa(null)', ''); | ||||||
|  |         assertRun('window.atob(window.btoa(window))', window.toString()); // "[object DOMWindow]" | ||||||
|  |         assertRun('window.btoa("\\u0080\\u0081")', 'gIE='); | ||||||
|  |  | ||||||
|  |         debug("Tests failed: " + fail); | ||||||
|  |         debug("Tests passed: " + pass); | ||||||
|  |     } | ||||||
|  | </script>  | ||||||
							
								
								
									
										12
									
								
								public/novnc/tests/base64.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,12 @@ | |||||||
|  | // The following results in 'hello [MANGLED]' | ||||||
|  | // | ||||||
|  | // Filed as https://github.com/ry/node/issues/issue/402 | ||||||
|  |  | ||||||
|  | var sys = require("sys"), | ||||||
|  |     buf = new Buffer(1024), len, | ||||||
|  |     str1 = "aGVsbG8g",  // 'hello ' | ||||||
|  |     str2 = "d29ybGQ=",  // 'world' | ||||||
|  |  | ||||||
|  | len = buf.write(str1, 0, 'base64'); | ||||||
|  | len += buf.write(str2, len, 'base64'); | ||||||
|  | sys.log("decoded result: " + buf.toString('binary', 0, len)); | ||||||
							
								
								
									
										134
									
								
								public/novnc/tests/browser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,134 @@ | |||||||
|  | /* | ||||||
|  |  * From: | ||||||
|  |  * http://www.quirksmode.org/js/detect.html | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | var Browser = { | ||||||
|  |     init: function () { | ||||||
|  |         this.browser = this.searchString(this.dataBrowser) || "An unknown browser"; | ||||||
|  |         this.version = this.searchVersion(navigator.userAgent) | ||||||
|  |             || this.searchVersion(navigator.appVersion) | ||||||
|  |             || "an unknown version"; | ||||||
|  |         this.majorVersion = this.searchMajorVersion(navigator.userAgent) | ||||||
|  |             || this.searchMajorVersion(navigator.appVersion) | ||||||
|  |             || "an unknown version"; | ||||||
|  |         this.fullVersion = this.searchFullVersion(navigator.userAgent) | ||||||
|  |             || this.searchFullVersion(navigator.appVersion) | ||||||
|  |             || "an unknown version"; | ||||||
|  |         this.OS = this.searchString(this.dataOS) || "an unknown OS"; | ||||||
|  |     }, | ||||||
|  |     searchString: function (data) { | ||||||
|  |         for (var i=0;i<data.length;i++) { | ||||||
|  |             var dataString = data[i].string; | ||||||
|  |             var dataProp = data[i].prop; | ||||||
|  |             this.versionSearchString = data[i].versionSearch || data[i].identity; | ||||||
|  |             if (dataString) { | ||||||
|  |                 if (dataString.indexOf(data[i].subString) != -1) | ||||||
|  |                     return data[i].identity; | ||||||
|  |             } | ||||||
|  |             else if (dataProp) | ||||||
|  |                 return data[i].identity; | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     searchFullVersion: function (dataString) { | ||||||
|  |         var index = dataString.indexOf(this.versionSearchString); | ||||||
|  |         if (index == -1) return; | ||||||
|  |         return dataString.substring(index+this.versionSearchString.length+1); | ||||||
|  |     }, | ||||||
|  |     searchVersion: function (dataString) { | ||||||
|  |         return this.searchFullVersion(dataString).split(" ")[0]; | ||||||
|  |     }, | ||||||
|  |     searchMajorVersion: function (dataString) { | ||||||
|  |         return parseFloat(this.searchFullVersion(dataString).split(".")[0]); | ||||||
|  |     }, | ||||||
|  |     dataBrowser: [ | ||||||
|  |         { | ||||||
|  |             string: navigator.userAgent, | ||||||
|  |             subString: "Chrome", | ||||||
|  |             identity: "Chrome" | ||||||
|  |         }, | ||||||
|  |         {       string: navigator.userAgent, | ||||||
|  |             subString: "OmniWeb", | ||||||
|  |             versionSearch: "OmniWeb/", | ||||||
|  |             identity: "OmniWeb" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             string: navigator.vendor, | ||||||
|  |             subString: "Apple", | ||||||
|  |             identity: "Safari", | ||||||
|  |             versionSearch: "Version" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             prop: window.opera, | ||||||
|  |             identity: "Opera", | ||||||
|  |             versionSearch: "Version" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             string: navigator.vendor, | ||||||
|  |             subString: "iCab", | ||||||
|  |             identity: "iCab" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             string: navigator.vendor, | ||||||
|  |             subString: "KDE", | ||||||
|  |             identity: "Konqueror" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             string: navigator.userAgent, | ||||||
|  |             subString: "Firefox", | ||||||
|  |             identity: "Firefox" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             string: navigator.vendor, | ||||||
|  |             subString: "Camino", | ||||||
|  |             identity: "Camino" | ||||||
|  |         }, | ||||||
|  |         {               // for newer Netscapes (6+) | ||||||
|  |             string: navigator.userAgent, | ||||||
|  |             subString: "Netscape", | ||||||
|  |             identity: "Netscape" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             string: navigator.userAgent, | ||||||
|  |             subString: "MSIE", | ||||||
|  |             identity: "Explorer", | ||||||
|  |             versionSearch: "MSIE" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             string: navigator.userAgent, | ||||||
|  |             subString: "Gecko", | ||||||
|  |             identity: "Mozilla", | ||||||
|  |             versionSearch: "rv" | ||||||
|  |         }, | ||||||
|  |         {               // for older Netscapes (4-) | ||||||
|  |             string: navigator.userAgent, | ||||||
|  |             subString: "Mozilla", | ||||||
|  |             identity: "Netscape", | ||||||
|  |             versionSearch: "Mozilla" | ||||||
|  |         } | ||||||
|  |     ], | ||||||
|  |     dataOS : [ | ||||||
|  |         { | ||||||
|  |             string: navigator.platform, | ||||||
|  |             subString: "Win", | ||||||
|  |             identity: "Windows" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             string: navigator.platform, | ||||||
|  |             subString: "Mac", | ||||||
|  |             identity: "Mac" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |                string: navigator.userAgent, | ||||||
|  |                subString: "iPhone", | ||||||
|  |                identity: "iPhone/iPod" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             string: navigator.platform, | ||||||
|  |             subString: "Linux", | ||||||
|  |             identity: "Linux" | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  | }; | ||||||
|  | Browser.init(); | ||||||
							
								
								
									
										148
									
								
								public/novnc/tests/canvas.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,148 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |     <head> | ||||||
|  |         <title>Canvas Performance Test</title> | ||||||
|  |         <!-- | ||||||
|  |         <script type='text/javascript'  | ||||||
|  |             src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> | ||||||
|  |         --> | ||||||
|  |         <script src="../include/util.js"></script> | ||||||
|  |         <script src="../include/webutil.js"></script> | ||||||
|  |         <script src="../include/base64.js"></script> | ||||||
|  |         <script src="../include/display.js"></script> | ||||||
|  |         <script src="face.png.js"></script> | ||||||
|  |     </head> | ||||||
|  |     <body> | ||||||
|  |         Iterations: <input id='iterations' style='width:50' value="100">  | ||||||
|  |  | ||||||
|  |         Width: <input id='width' style='width:50' value="640">  | ||||||
|  |         Height: <input id='height' style='width:50' value="480">  | ||||||
|  |  | ||||||
|  |         <input id='startButton' type='button' value='Do Performance Test' | ||||||
|  |             style='width:150px' onclick="begin();">  | ||||||
|  |  | ||||||
|  |         <br><br> | ||||||
|  |  | ||||||
|  |         <b>Canvas</b> (should see three squares and two happy faces):<br> | ||||||
|  |         <canvas id="canvas" width="200" height="100" | ||||||
|  |                 style="border-style: dotted; border-width: 1px;"> | ||||||
|  |             Canvas not supported. | ||||||
|  |         </canvas> | ||||||
|  |  | ||||||
|  |         <br> | ||||||
|  |         Results:<br> | ||||||
|  |         <textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea> | ||||||
|  |     </body> | ||||||
|  |  | ||||||
|  |     <script> | ||||||
|  |         var msg_cnt = 0; | ||||||
|  |         var display, start_width = 300, start_height = 100; | ||||||
|  |         var iterations; | ||||||
|  |  | ||||||
|  |         function message(str) { | ||||||
|  |             console.log(str); | ||||||
|  |             cell = $D('messages'); | ||||||
|  |             cell.textContent += msg_cnt + ": " + str + "\n"; | ||||||
|  |             cell.scrollTop = cell.scrollHeight; | ||||||
|  |             msg_cnt += 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function test_functions () { | ||||||
|  |             var img, x, y, w, h, ctx = display.get_context(); | ||||||
|  |             w = display.get_width(); | ||||||
|  |             h = display.get_height(); | ||||||
|  |             display.fillRect(0, 0, w, h, [240,240,240]); | ||||||
|  |  | ||||||
|  |             display.blitStringImage("data:image/png;base64," + face64, 150, 10); | ||||||
|  |  | ||||||
|  |             var himg = new Image(); | ||||||
|  |             himg.onload = function () { | ||||||
|  |                 ctx.drawImage(himg, 200, 40); }; | ||||||
|  |             himg.src = "face.png"; | ||||||
|  |  | ||||||
|  |             /* Test array image data */ | ||||||
|  |             data = []; | ||||||
|  |             for (y=0; y< 50; y++) { | ||||||
|  |                 for (x=0; x< 50; x++) { | ||||||
|  |                     data[(y*50 + x)*4 + 0] = 255 - parseInt((255 / 50) * y, 10); | ||||||
|  |                     data[(y*50 + x)*4 + 1] = parseInt((255 / 50) * y, 10); | ||||||
|  |                     data[(y*50 + x)*4 + 2] = parseInt((255 / 50) * x, 10); | ||||||
|  |                     data[(y*50 + x)*4 + 3] = 255; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             display.blitImage(30, 10, 50, 50, data, 0); | ||||||
|  |  | ||||||
|  |             img = display.getTile(5,5,16,16,[0,128,128]); | ||||||
|  |             display.putTile(img); | ||||||
|  |  | ||||||
|  |             img = display.getTile(90,15,16,16,[0,0,0]); | ||||||
|  |             display.setSubTile(img, 0,0,16,16,[128,128,0]); | ||||||
|  |             display.putTile(img); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function begin () { | ||||||
|  |             $D('startButton').value = "Running"; | ||||||
|  |             $D('startButton').disabled = true; | ||||||
|  |             setTimeout(start_delayed, 250); | ||||||
|  |             iterations = $D('iterations').value; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function start_delayed () { | ||||||
|  |             var ret; | ||||||
|  |  | ||||||
|  |             ret = display.set_prefer_js(true); | ||||||
|  |             if (ret) { | ||||||
|  |                 message("Running test: prefer Javascript ops"); | ||||||
|  |                 var time1 = run_test(); | ||||||
|  |                 message("prefer Javascript ops: " + time1 + "ms total, " + | ||||||
|  |                         (time1 / iterations) + "ms per frame"); | ||||||
|  |             } else { | ||||||
|  |                 message("Could not run: prefer Javascript ops"); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             display.set_prefer_js(false); | ||||||
|  |             message("Running test: prefer Canvas ops"); | ||||||
|  |             var time2 = run_test(); | ||||||
|  |             message("prefer Canvas ops: " + time2 + "ms total, " + | ||||||
|  |                     (time2 / iterations) + "ms per frame"); | ||||||
|  |  | ||||||
|  |             if (Util.get_logging() !== 'debug') { | ||||||
|  |                 display.resize(start_width, start_height, true); | ||||||
|  |                 test_functions(); | ||||||
|  |             } | ||||||
|  |             $D('startButton').disabled = false; | ||||||
|  |             $D('startButton').value = "Do Performance Test"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function run_test () { | ||||||
|  |             var width, height; | ||||||
|  |             width = $D('width').value; | ||||||
|  |             height = $D('height').value; | ||||||
|  |             display.resize(width, height); | ||||||
|  |             var color, start_time = (new Date()).getTime(), w, h; | ||||||
|  |             for (var i=0; i < iterations; i++) { | ||||||
|  |                 color = [128, 128, (255 / iterations) * i, 0]; | ||||||
|  |                 for (var x=0; x < width; x = x + 16) { | ||||||
|  |                     for (var y=0; y < height; y = y + 16) { | ||||||
|  |                         w = Math.min(16, width - x); | ||||||
|  |                         h = Math.min(16, height - y); | ||||||
|  |                         var tile = display.getTile(x, y, w, h, color); | ||||||
|  |                         display.setSubTile(tile, 0, 0, w, h, color); | ||||||
|  |                         display.putTile(tile); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             var end_time = (new Date()).getTime(); | ||||||
|  |             return (end_time - start_time); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         window.onload = function() { | ||||||
|  |             message("in onload"); | ||||||
|  |             $D('iterations').value = 10; | ||||||
|  |             display = new Display({'target' : $D('canvas')}); | ||||||
|  |             display.resize(start_width, start_height, true); | ||||||
|  |             message("Canvas initialized"); | ||||||
|  |             test_functions(); | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
|  | </html> | ||||||
							
								
								
									
										135
									
								
								public/novnc/tests/cursor.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,135 @@ | |||||||
|  | <!DOCTYPE html>  | ||||||
|  | <html xmlns="http://www.w3.org/1999/xhtml">  | ||||||
|  |   <head>  | ||||||
|  |     <title>Cursor Change test</title>  | ||||||
|  |     <meta charset="UTF-8">  | ||||||
|  |     <!-- | ||||||
|  |     <script type='text/javascript'  | ||||||
|  |                     src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> | ||||||
|  |     --> | ||||||
|  |     <script src="../include/util.js"></script>  | ||||||
|  |     <script src="../include/webutil.js"></script>  | ||||||
|  |     <script src="../include/base64.js"></script>  | ||||||
|  |     <script src="../include/canvas.js"></script>  | ||||||
|  |   </head>  | ||||||
|  |   <body>  | ||||||
|  |         <h1>Roll over the buttons to test cursors</h1> | ||||||
|  |         <br> | ||||||
|  |         <input id=button1 type="button" value="Cursor from file (smiley face)"> | ||||||
|  |         <input id=button2 type="button" value="Data URI cursor (crosshair)"> | ||||||
|  |      | ||||||
|  |         <br>  | ||||||
|  |         <br>  | ||||||
|  |         <br>  | ||||||
|  |         Debug:<br>  | ||||||
|  |         <textarea id="debug" style="font-size: 9px;" cols=80 rows=25></textarea>  | ||||||
|  |         <br> | ||||||
|  |         <br> | ||||||
|  |         <canvas id="testcanvas" width="100px" height="20px"> | ||||||
|  |             Canvas not supported. | ||||||
|  |         </canvas> | ||||||
|  |      | ||||||
|  |   </body>  | ||||||
|  |   | ||||||
|  |   <script>  | ||||||
|  |     function debug(str) { | ||||||
|  |         console.log(str); | ||||||
|  |         cell = $D('debug'); | ||||||
|  |         cell.textContent += str + "\n"; | ||||||
|  |         cell.scrollTop = cell.scrollHeight; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function makeCursor() { | ||||||
|  |         var arr = [], x, y, w = 32, h = 32, hx = 16, hy = 16; | ||||||
|  |  | ||||||
|  |         var IHDRsz = 40; | ||||||
|  |         var ANDsz = w * h * 4; | ||||||
|  |         var XORsz = Math.ceil( (w * h) / 8.0 ); | ||||||
|  |  | ||||||
|  |         // Push multi-byte little-endian values | ||||||
|  |         arr.push16le = function (num) { | ||||||
|  |             this.push((num     ) & 0xFF, | ||||||
|  |                       (num >> 8) & 0xFF  ); | ||||||
|  |         }; | ||||||
|  |         arr.push32le = function (num) { | ||||||
|  |             this.push((num      ) & 0xFF, | ||||||
|  |                       (num >>  8) & 0xFF, | ||||||
|  |                       (num >> 16) & 0xFF, | ||||||
|  |                       (num >> 24) & 0xFF  ); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         // Main header | ||||||
|  |         arr.push16le(0);      // Reserved | ||||||
|  |         arr.push16le(2);      // .CUR type | ||||||
|  |         arr.push16le(1);      // Number of images, 1 for non-animated arr | ||||||
|  |  | ||||||
|  |         // Cursor #1 | ||||||
|  |         arr.push(w);          // width | ||||||
|  |         arr.push(h);          // height | ||||||
|  |         arr.push(0);          // colors, 0 -> true-color | ||||||
|  |         arr.push(0);          // reserved | ||||||
|  |         arr.push16le(hx);     // hotspot x coordinate | ||||||
|  |         arr.push16le(hy);     // hotspot y coordinate | ||||||
|  |         arr.push32le(IHDRsz + XORsz + ANDsz); // cursor data byte size | ||||||
|  |         arr.push32le(22);     // offset of cursor data in the file | ||||||
|  |  | ||||||
|  |         // Infoheader for Cursor #1 | ||||||
|  |         arr.push32le(IHDRsz); // Infoheader size | ||||||
|  |         arr.push32le(w);      // Cursor width | ||||||
|  |         arr.push32le(h*2);    // XOR+AND height | ||||||
|  |         arr.push16le(1);      // number of planes | ||||||
|  |         arr.push16le(32);     // bits per pixel | ||||||
|  |         arr.push32le(0);      // type of compression | ||||||
|  |         arr.push32le(XORsz + ANDsz); // Size of Image | ||||||
|  |         arr.push32le(0); | ||||||
|  |         arr.push32le(0); | ||||||
|  |         arr.push32le(0); | ||||||
|  |         arr.push32le(0); | ||||||
|  |  | ||||||
|  |         // XOR/color data | ||||||
|  |         for (y = h-1; y >= 0; y--) { | ||||||
|  |             for (x = 0; x < w; x++) { | ||||||
|  |                 //if ((x === hx) || (y === (h-hy-1))) { | ||||||
|  |                 if ((x === hx) || (y === hy)) { | ||||||
|  |                     arr.push(0xe0);  // blue | ||||||
|  |                     arr.push(0x00);  // green | ||||||
|  |                     arr.push(0x00);  // red | ||||||
|  |                     arr.push(0xff);  // alpha | ||||||
|  |                 } else { | ||||||
|  |                     arr.push(0x05);  // blue | ||||||
|  |                     arr.push(0xe6);  // green | ||||||
|  |                     arr.push(0x00);  // red | ||||||
|  |                     arr.push(0x80);  // alpha | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // AND/bitmask data (seems to be ignored) | ||||||
|  |         for (y = 0; y < h; y++) { | ||||||
|  |             for (x = 0; x < Math.ceil(w / 8); x++) { | ||||||
|  |                 arr.push(0x00); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         debug("cursor generated"); | ||||||
|  |         return arr; | ||||||
|  |     } | ||||||
|  |   | ||||||
|  |     window.onload = function() { | ||||||
|  |         debug("onload"); | ||||||
|  |         var canvas, cross, cursor, cursor64; | ||||||
|  |  | ||||||
|  |         canvas = new Canvas({'target' : $D("testcanvas")}); | ||||||
|  |         debug("canvas indicates Data URI cursor support is: " + canvas.get_cursor_uri()); | ||||||
|  |  | ||||||
|  |         $D('button1').style.cursor="url(face.png), default"; | ||||||
|  |  | ||||||
|  |         cursor = makeCursor(); | ||||||
|  |         cursor64 = Base64.encode(cursor); | ||||||
|  |         //debug("cursor: " + cursor.slice(0,100) + " (" + cursor.length + ")"); | ||||||
|  |         //debug("cursor64: " + cursor64.slice(0,100) + " (" + cursor64.length + ")"); | ||||||
|  |         $D('button2').style.cursor="url(data:image/x-icon;base64," + cursor64 + "), default"; | ||||||
|  |  | ||||||
|  |         debug("onload complete"); | ||||||
|  |     } | ||||||
|  |   </script>  | ||||||
							
								
								
									
										
											BIN
										
									
								
								public/novnc/tests/face.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.2 KiB | 
							
								
								
									
										1
									
								
								public/novnc/tests/face.png.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | var face64 = 'iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAIAAACRuyQOAAAAA3NCSVQICAjb4U/gAAAAGXRFWHRTb2Z0d2FyZQBnbm9tZS1zY3JlZW5zaG907wO/PgAACJJJREFUSIm1lltsXMUdxr8558zZq9d3OxebJDYhJLhNIAmUWyFKIBUtVaGqSgtUlIJKeahoEahgIZU+VC0oQiVVC60obckDgVIp3KRCQkmhhIhA4oY4wjg2ufmS9drec/bc5vbvw9prJwq85dP/YWfP7Pfb/8w3s8v6339l2fkrbMvGuZQ2mkUTA0bpc4qpyjrX3dTkAATQ5z0WUrqcAwjL/eXirmBqj0yKSTTBwNxMM0+15JuurG/dlClcOH/yWcVEaVBKUR3Eidizr2946Nhr/9q5b//BsudZzDLG5DK4sDt3443XrFm34bkX9x4ZPimkWNBa/+MfrB84+O7rbxz4+JPQD8liljY6n8t9uWfld2/++vp1F3ct6cikU2eSnvr7P7e99OqC9vaTJ0ccMtl8loyJ4igKwzAIK0GglersWv7sM08VCrk4joY/O/rLXz3mTYzmcnnXdZXWcRzHURwEQRCEHUuXdS/vnp4qP/CT2zdvuAKAQwCB4kRse+m1LY//Wojkscd/57opKUQUJ8wyzFaOq7OGGGPcdZ/f/sKbu3YT0YZrr3JT7pq1l3qeH4SBqgRETBljDKXSqXyh/i9PP/W/Q31btz59zVXrUpxb1dYsixUK+c7Fi59/YUdz2yInnbXcLHfTtpu23ZRlu4ZZiRBTp8Z37HjlhW1/evnFZ9/a+VZdLsecFOMpx83ydJanc24q67iuFOr48NC1G6+fKBY7zutIElFNBAC4nN99602XXLzutjvvETqAlcqktVQin0QiLsRxEAUBaRVUfBh1QfcigmzIuw0NTe2LOjNlL07iOArDwA88z0unGWNTk5P1dfkf3XH3BT2r9b23zZKIAHxr81f/uGpF/8G+Fau+VPbKp8ZHpqdKSRiEYWiMMVopJSuVyl+f3UpIQKL34btvvf2BxuZWN5Umo7TWFiNDDHCampob6utLpRKz7Hvv+E5jfR5ELCkNShFXOytOTH7vjrsOfXJ0wcLFF63sXr1mfXtbS6FQB4BZyGYzX7l0TWtrvWVpUGxUMFEa2bv3Q9+bNCaECX2/NFEc3bd/4r19/tR0uLC98c+/3/LVy9fWzhNq56m1pfEPvabnut2OI8EvBMAYAxhgAWz3u3tuvuWeRx/56aYNa0Hy3fc/euiRZx596IZvbF5Dpgw9CdMI0waqaMrEScPgvtdWXH5JzdzC7NElIPQH3GyTk+4ABCgCEpAkMgRGcLb/49WGxqYtTzwNaJDa/tJ7DU1tW558GaYCEwESYGAWwEidTOcWM8tElcGauTP/ivDGd7V3fxv6JGCBIpBDjIMxgIM5B/YfjMJwfGwEMIA40DcQhcn46DGAzX7p6gIwBhj5WUvH8vLYG+nu8+d6qimY2lPXup70GFEEE9baAhRIj5w8cfz4MSESkJw3FLOfnrvSCETqs3xTd2Vyd+1Na/4MmRRt3gBTgfGJKkQhTAQTwgQgv2tpR8X3Vq5YCiiC7lrSXPG9lRe0AmZ2hQxo5jXpspNqEElxPmlOIi5ZThYUgBKYKRgPxgMFMAGM/+D9P2xuLPQ+dBcoAYkHf/bN5sZM74M3gHS1acBUi0gZ4zk8J5NyzdzBGSIJkoANCqsrwgBAg+zN1605Mfw6IIkiUHL9xouODzwBE4ACkKrGBNBkBEgSKSIz39gxRkuRVAduulHLCZtZoARkzybTAFU2m7GjBBSDkmoRJYCc3U5lSBgjAFeJae4Wauan9WSnWlU0aqdtUAXElAicVDNIgfHZaJkZU0pAESgmCJAACUCApJIBKCITg+VVMuWm2+btEwFE1coVLvOKe2HVE8UwUd/OXi0nQZXZ8kH+7HIFoIgoqvKqzWkV9L2zy5jQ6Ig5nX5pOFd/Vc3cmv9zW9eyYfzITmY1giKiMJNtCiYPw1RgPBh/psiHqcAEZAJQBFMlxaDEnyqmc3mjY2NCiy+bHB3Kt2w8I+UzxTPLlAzjygCz6kFBx6qNg/ue84p9M7AZRoWoQhSAqumfacsrnRg6uH9Rd4/RFWafl1RGjLJ5ZknNnIXjh+PQB0BEQkqv9L4sb1t59cMU74GVKxcnhg5sdzN1jQtX5grtqVyj46ZtywIJrUOZeCKYCLxTU+PHkzhZ2vO1XH5MRIfcwvcHP9qRafp5XfN6l3PGGIA5ktJaJEJINXnkvmWrNza0rSBxEFYbnE6veGRq9IPQO54Ep5QItRYAs22Hu1k315QtdDYsuCzf1KHDt0XlbTu3ySuVRo6MNnc/6XLHTbmObc+QotAHIJUSQiSJTKLR4Nh9Pdc+kM44JA+D5RhfBud8ZjeD5WHVMVYHqwAYmGkyUyRPqPDfMnhTxcNW+jKpGj/94NX8eVtTmYWpFHddlzsOABaOzZGkkImQUsrI/1iVfrPq6vszuSyJD0EasGEVmN0KlgXLgYGMT6qkkwEthrQuG53Y2U0icT79YIfb2pup6+Gcp1zOXV4j9VdJxhghpJBSSCmEjL0+XXqsa+0tTYvWQ/aTHJrZW9JEkowwJjYmMjo0OmR8uZ1eNz12+Nih/zgtv0gXVrsur1Jcl1uWNUsK/GoQldZSSCGllEpIGYcndOm36Vyqa/VNmboFRh4ldZR02ZhpMhJwCGnmLGZ8SewXj/bvTkLDW3pT2UUu55w7Lufc5dVNAsCCsf4o8Gqpr8KkUlIqpZRUKim/Y/y/pVLZ1s5V+Zbl3C3Ybp5Iq2RKxhP+xFBxZFAmwi7cmaq/kjuO4zicO9xx5mPOQqrGvYZRWmulldYqGlLBf3X8EfQkSR8A43WMN1nuWid3hZPpcmzbdmzHtmuwarjnkw5FldNIczyljDZKa62NNpoM1QSA1WQx27Jt23Js27It7pzJmLthz/7/nzHOOThcImPoNBIIAMNpJMtiNcBZDZ3PfVIjgtkWsy3riyZ9AaFGMlozhuqCnDsxxv4PC7uS+QV5eeoAAAAASUVORK5CYII='; | ||||||
							
								
								
									
										91
									
								
								public/novnc/tests/fake.websocket.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,91 @@ | |||||||
|  | var FakeWebSocket; | ||||||
|  |  | ||||||
|  | (function () { | ||||||
|  |     // PhantomJS can't create Event objects directly, so we need to use this | ||||||
|  |     function make_event(name, props) { | ||||||
|  |         var evt = document.createEvent('Event'); | ||||||
|  |         evt.initEvent(name, true, true); | ||||||
|  |         if (props) { | ||||||
|  |             for (var prop in props) { | ||||||
|  |                 evt[prop] = props[prop]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return evt; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     FakeWebSocket = function (uri, protocols) { | ||||||
|  |         this.url = uri; | ||||||
|  |         this.binaryType = "arraybuffer"; | ||||||
|  |         this.extensions = ""; | ||||||
|  |  | ||||||
|  |         if (!protocols || typeof protocols === 'string') { | ||||||
|  |             this.protocol = protocols; | ||||||
|  |         } else { | ||||||
|  |             this.protocol = protocols[0]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this._send_queue = new Uint8Array(20000); | ||||||
|  |  | ||||||
|  |         this.readyState = FakeWebSocket.CONNECTING; | ||||||
|  |         this.bufferedAmount = 0; | ||||||
|  |  | ||||||
|  |         this.__is_fake = true; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     FakeWebSocket.prototype = { | ||||||
|  |         close: function (code, reason) { | ||||||
|  |             this.readyState = FakeWebSocket.CLOSED; | ||||||
|  |             if (this.onclose) { | ||||||
|  |                 this.onclose(make_event("close", { 'code': code, 'reason': reason, 'wasClean': true })); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         send: function (data) { | ||||||
|  |             if (this.protocol == 'base64') { | ||||||
|  |                 data = Base64.decode(data); | ||||||
|  |             } else { | ||||||
|  |                 data = new Uint8Array(data); | ||||||
|  |             } | ||||||
|  |             this._send_queue.set(data, this.bufferedAmount); | ||||||
|  |             this.bufferedAmount += data.length; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _get_sent_data: function () { | ||||||
|  |             var res = new Uint8Array(this._send_queue.buffer, 0, this.bufferedAmount); | ||||||
|  |             this.bufferedAmount = 0; | ||||||
|  |             return res; | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _open: function (data) { | ||||||
|  |             this.readyState = FakeWebSocket.OPEN; | ||||||
|  |             if (this.onopen) { | ||||||
|  |                 this.onopen(make_event('open')); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         _receive_data: function (data) { | ||||||
|  |             this.onmessage(make_event("message", { 'data': data })); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     FakeWebSocket.OPEN = WebSocket.OPEN; | ||||||
|  |     FakeWebSocket.CONNECTING = WebSocket.CONNECTING; | ||||||
|  |     FakeWebSocket.CLOSING = WebSocket.CLOSING; | ||||||
|  |     FakeWebSocket.CLOSED = WebSocket.CLOSED; | ||||||
|  |  | ||||||
|  |     FakeWebSocket.__is_fake = true; | ||||||
|  |  | ||||||
|  |     FakeWebSocket.replace = function () { | ||||||
|  |         if (!WebSocket.__is_fake) { | ||||||
|  |             var real_version = WebSocket; | ||||||
|  |             WebSocket = FakeWebSocket; | ||||||
|  |             FakeWebSocket.__real_version = real_version; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     FakeWebSocket.restore = function () { | ||||||
|  |         if (WebSocket.__is_fake) { | ||||||
|  |             WebSocket = WebSocket.__real_version; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | })(); | ||||||
							
								
								
									
										132
									
								
								public/novnc/tests/input.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,132 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |     <head><title>Input Test</title></head> | ||||||
|  |     <body> | ||||||
|  |         <br><br> | ||||||
|  |  | ||||||
|  |         Canvas: | ||||||
|  |         <span id="button-selection" style="display: none;"> | ||||||
|  |             <input id="button1" type="button" value="L"><input id="button2" type="button" value="M"><input id="button4" type="button" value="R"> | ||||||
|  |         </span> | ||||||
|  |         <br> | ||||||
|  |         <canvas id="canvas" width="640" height="20" | ||||||
|  |                 style="border-style: dotted; border-width: 1px;"> | ||||||
|  |             Canvas not supported. | ||||||
|  |         </canvas> | ||||||
|  |  | ||||||
|  |         <br> | ||||||
|  |         Results:<br> | ||||||
|  |         <textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea> | ||||||
|  |     </body> | ||||||
|  |  | ||||||
|  |     <!-- | ||||||
|  |     <script type='text/javascript'  | ||||||
|  |         src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> | ||||||
|  |     --> | ||||||
|  |     <script src="../include/util.js"></script> | ||||||
|  |     <script src="../include/webutil.js"></script>  | ||||||
|  |     <script src="../include/base64.js"></script> | ||||||
|  |     <script src="../include/keysym.js"></script> | ||||||
|  |     <script src="../include/keysymdef.js"></script>  | ||||||
|  |     <script src="../include/keyboard.js"></script>  | ||||||
|  |     <script src="../include/input.js"></script>  | ||||||
|  |     <script src="../include/display.js"></script> | ||||||
|  |     <script> | ||||||
|  |         var msg_cnt = 0, iterations, | ||||||
|  |             width = 400, height = 200, | ||||||
|  |             canvas, keyboard, mouse; | ||||||
|  |  | ||||||
|  |         var newline = "\n"; | ||||||
|  |         if (Util.Engine.trident) { | ||||||
|  |             var newline = "<br>\n"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function message(str) { | ||||||
|  |             console.log(str); | ||||||
|  |             cell = $D('messages'); | ||||||
|  |             cell.textContent += msg_cnt + ": " + str + newline; | ||||||
|  |             cell.scrollTop = cell.scrollHeight; | ||||||
|  |             msg_cnt++; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function mouseButton(x, y, down, bmask) { | ||||||
|  |             msg = 'mouse x,y: ' + x + ',' + y + '  down: ' + down; | ||||||
|  |             msg += ' bmask: ' + bmask; | ||||||
|  |             message(msg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function mouseMove(x, y) { | ||||||
|  |             msg = 'mouse x,y: ' + x + ',' + y; | ||||||
|  |             //console.log(msg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function rfbKeyPress(keysym, down) { | ||||||
|  |             var d = down ? "down" : " up "; | ||||||
|  |             var key = keysyms.lookup(keysym); | ||||||
|  |             var msg = "RFB keypress " + d + " keysym: " + keysym; | ||||||
|  |             if (key && key.keyname) { | ||||||
|  |                 msg += " key name: " + key.keyname; | ||||||
|  |             } | ||||||
|  |             message(msg); | ||||||
|  |         } | ||||||
|  |         function rawKey(e) { | ||||||
|  |             msg = "raw key event " + e.type + | ||||||
|  |                   " (key: " + e.keyCode + ", char: " + e.charCode + | ||||||
|  |                   ", which: " + e.which +")"; | ||||||
|  |             message(msg); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function selectButton(num) { | ||||||
|  |             var b, blist = [1,2,4]; | ||||||
|  |  | ||||||
|  |             if (typeof num === 'undefined') { | ||||||
|  |                 // Show the default | ||||||
|  |                 num = mouse.get_touchButton(); | ||||||
|  |             } else if (num === mouse.get_touchButton()) { | ||||||
|  |                 // Set all buttons off (no clicks) | ||||||
|  |                 mouse.set_touchButton(0); | ||||||
|  |                 num = 0; | ||||||
|  |             } else { | ||||||
|  |                 // Turn on one button | ||||||
|  |                 mouse.set_touchButton(num); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             for (b = 0; b < blist.length; b++) { | ||||||
|  |                 if (blist[b] === num) { | ||||||
|  |                     $D('button' + blist[b]).style.backgroundColor = "black"; | ||||||
|  |                     $D('button' + blist[b]).style.color = "lightgray"; | ||||||
|  |                 } else { | ||||||
|  |                     $D('button' + blist[b]).style.backgroundColor = ""; | ||||||
|  |                     $D('button' + blist[b]).style.color = ""; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         window.onload = function() { | ||||||
|  |             canvas = new Display({'target' : $D('canvas')}); | ||||||
|  |             keyboard = new Keyboard({'target': document, | ||||||
|  |                                     'onKeyPress': rfbKeyPress}); | ||||||
|  |            Util.addEvent(document, 'keypress', rawKey); | ||||||
|  |            Util.addEvent(document, 'keydown', rawKey); | ||||||
|  |            Util.addEvent(document, 'keyup', rawKey); | ||||||
|  |             mouse    = new Mouse({'target': $D('canvas'), | ||||||
|  |                                 'onMouseButton': mouseButton, | ||||||
|  |                                 'onMouseMove': mouseMove}); | ||||||
|  |  | ||||||
|  |             canvas.resize(width, height, true); | ||||||
|  |             keyboard.grab(); | ||||||
|  |             mouse.grab(); | ||||||
|  |             message("Display initialized"); | ||||||
|  |  | ||||||
|  |             if ('ontouchstart' in document.documentElement) { | ||||||
|  |                 message("Touch device detected"); | ||||||
|  |                 $D('button-selection').style.display = "inline"; | ||||||
|  |                 $D('button1').onclick = function(){ selectButton(1) }; | ||||||
|  |                 $D('button2').onclick = function(){ selectButton(2) }; | ||||||
|  |                 $D('button4').onclick = function(){ selectButton(4) }; | ||||||
|  |                 selectButton(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
|  | </html> | ||||||
							
								
								
									
										29
									
								
								public/novnc/tests/keyboard-tests.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,29 @@ | |||||||
|  | <!doctype html> | ||||||
|  | <html> | ||||||
|  | <head> | ||||||
|  |   <meta charset="utf-8"> | ||||||
|  |   <title>Mocha Tests</title> | ||||||
|  |   <link rel="stylesheet" href="node_modules/mocha/mocha.css" /> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |   <!-- | ||||||
|  |     To run tests | ||||||
|  |     cd .../noVNC/tests | ||||||
|  |     npm install chai mocha | ||||||
|  |     open keyboard-tests.html in a browser | ||||||
|  |   --> | ||||||
|  |   <div id="mocha"></div> | ||||||
|  |   <script src="node_modules/chai/chai.js"></script> | ||||||
|  |   <script src="node_modules/mocha/mocha.js"></script> | ||||||
|  |   <script>mocha.setup('bdd')</script> | ||||||
|  |   <script src="../include/keysymdef.js"></script> | ||||||
|  |   <script src="../include/keyboard.js"></script> | ||||||
|  |   <script src="test.keyboard.js"></script> | ||||||
|  |   <script src="test.helper.js"></script> | ||||||
|  |   <script> | ||||||
|  |     mocha.checkLeaks(); | ||||||
|  |     mocha.globals(['navigator']); | ||||||
|  |     mocha.run(); | ||||||
|  |   </script> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										114
									
								
								public/novnc/tests/run_from_console.casper.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,114 @@ | |||||||
|  | var Spooky = require('spooky'); | ||||||
|  | var path = require('path'); | ||||||
|  |  | ||||||
|  | var phantom_path = require('phantomjs').path; | ||||||
|  | var casper_path = path.resolve(__dirname, '../node_modules/casperjs/bin/casperjs'); | ||||||
|  | process.env.PHANTOMJS_EXECUTABLE = phantom_path; | ||||||
|  | var casper_opts = { | ||||||
|  |   child: { | ||||||
|  |     transport: 'http', | ||||||
|  |     command: casper_path | ||||||
|  |   }, | ||||||
|  |   casper: { | ||||||
|  |     logLevel: 'debug', | ||||||
|  |     verbose: true | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | var provide_emitter = function(file_paths, debug_port) { | ||||||
|  |   if (debug_port) { | ||||||
|  |       casper_opts.child['remote-debugger-port'] = debug_port; | ||||||
|  |       var debug_url = ('https://localhost:' + debug_port + | ||||||
|  |                        '/webkit/inspector/inspector.html?page='); | ||||||
|  |       console.info('[remote-debugger] Navigate to ' + debug_url + '1 and ' + | ||||||
|  |                    'run `__run();` in the console to continue loading.' + | ||||||
|  |                    '\n[remote-debugger] Navigate to ' + debug_url + '2 to ' + | ||||||
|  |                    'view the actual page source.\n' + | ||||||
|  |                    '[remote-debugger] Use the `debugger;` statement to ' + | ||||||
|  |                    'trigger an initial breakpoint.'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   var spooky = new Spooky(casper_opts, function(err) { | ||||||
|  |     if (err) { | ||||||
|  |       if (err.stack) console.warn(err.stack); | ||||||
|  |       else console.warn(err); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     spooky.start('about:blank'); | ||||||
|  |  | ||||||
|  |     file_paths.forEach(function(file_path, path_ind) { | ||||||
|  |       spooky.thenOpen('file://'+file_path); | ||||||
|  |       spooky.waitFor(function() { | ||||||
|  |         return this.getGlobal('__mocha_done') === true; | ||||||
|  |       }, | ||||||
|  |       [{ path_ind: path_ind }, function() { | ||||||
|  |         var res_json = { | ||||||
|  |           file_ind: path_ind | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         res_json.num_tests = this.evaluate(function() { return document.querySelectorAll('li.test').length; }); | ||||||
|  |         res_json.num_passes = this.evaluate(function() { return document.querySelectorAll('li.test.pass').length; }); | ||||||
|  |         res_json.num_fails = this.evaluate(function() { return document.querySelectorAll('li.test.fail').length; }); | ||||||
|  |         res_json.num_slow = this.evaluate(function() { return document.querySelectorAll('li.test.pass:not(.fast):not(.pending)').length; }); | ||||||
|  |         res_json.num_skipped = this.evaluate(function () { return document.querySelectorAll('li.test.pending').length; }); | ||||||
|  |         res_json.duration = this.evaluate(function() { return document.querySelector('li.duration em').textContent; }); | ||||||
|  |  | ||||||
|  |         res_json.suites = this.evaluate(function() { | ||||||
|  |           var traverse_node = function(elem) { | ||||||
|  |             var res; | ||||||
|  |             if (elem.classList.contains('suite')) { | ||||||
|  |               res = { | ||||||
|  |                 type: 'suite', | ||||||
|  |                 name: elem.querySelector('h1').textContent, | ||||||
|  |                 has_subfailures: elem.querySelectorAll('li.test.fail').length > 0, | ||||||
|  |               }; | ||||||
|  |  | ||||||
|  |               var child_elems = elem.querySelector('ul').children; | ||||||
|  |               res.children = Array.prototype.map.call(child_elems, traverse_node); | ||||||
|  |               return res; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |               var h2_content = elem.querySelector('h2').childNodes; | ||||||
|  |               res = { | ||||||
|  |                 type: 'test', | ||||||
|  |                 text: h2_content[0].textContent, | ||||||
|  |               }; | ||||||
|  |  | ||||||
|  |               if (elem.classList.contains('pass')) { | ||||||
|  |                 res.pass = true; | ||||||
|  |                 if (elem.classList.contains('pending')) { | ||||||
|  |                   res.slow = false; | ||||||
|  |                   res.skipped = true; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                   res.slow = !elem.classList.contains('fast'); | ||||||
|  |                   res.skipped = false; | ||||||
|  |                   res.duration = h2_content[1].textContent; | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |               else { | ||||||
|  |                 res.error = elem.querySelector('pre.error').textContent; | ||||||
|  |               } | ||||||
|  |  | ||||||
|  |               return res; | ||||||
|  |             } | ||||||
|  |           }; | ||||||
|  |           var top_suites = document.querySelectorAll('#mocha-report > li.suite'); | ||||||
|  |           return Array.prototype.map.call(top_suites, traverse_node); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         res_json.replay = this.evaluate(function() { return document.querySelector('a.replay').textContent; }); | ||||||
|  |  | ||||||
|  |         this.emit('test_ready', res_json); | ||||||
|  |       }]); | ||||||
|  |     }); | ||||||
|  |     spooky.run(); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   return spooky; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | module.exports = { | ||||||
|  |   provide_emitter: provide_emitter, | ||||||
|  |   name: 'SpookyJS (CapserJS on PhantomJS)' | ||||||
|  | }; | ||||||
							
								
								
									
										361
									
								
								public/novnc/tests/run_from_console.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,361 @@ | |||||||
|  | #!/usr/bin/env node | ||||||
|  | var ansi = require('ansi'); | ||||||
|  | var program = require('commander'); | ||||||
|  | var path = require('path'); | ||||||
|  | var fs = require('fs'); | ||||||
|  |  | ||||||
|  | var make_list = function(val) { | ||||||
|  |   return val.split(','); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | program | ||||||
|  |   .option('-t, --tests <testlist>', 'Run the specified html-file-based test(s). \'testlist\' should be a comma-separated list', make_list, []) | ||||||
|  |   .option('-a, --print-all', 'Print all tests, not just the failures') | ||||||
|  |   .option('--disable-color', 'Explicitly disable color') | ||||||
|  |   .option('-c, --color', 'Explicitly enable color (default is to use color when not outputting to a pipe)') | ||||||
|  |   .option('-i, --auto-inject <includefiles>', 'Treat the test list as a set of mocha JS files, and automatically generate HTML files with which to test test.  \'includefiles\' should be a comma-separated list of paths to javascript files to include in each of the generated HTML files', make_list, null) | ||||||
|  |   .option('-p, --provider <name>', 'Use the given provider (defaults to "casper").  Currently, may be "casper" or "zombie"', 'casper') | ||||||
|  |   .option('-g, --generate-html', 'Instead of running the tests, just return the path to the generated HTML file, then wait for user interaction to exit (should be used with .js tests).') | ||||||
|  |   .option('-o, --open-in-browser', 'Open the generated HTML files in a web browser using the "open" module (must be used with the "-g"/"--generate-html" option).') | ||||||
|  |   .option('--output-html', 'Instead of running the tests, just output the generated HTML source to STDOUT (should be used with .js tests)') | ||||||
|  |   .option('-d, --debug', 'Show debug output (the "console" event) from the provider') | ||||||
|  |   .option('-r, --relative', 'Use relative paths in the generated HTML file') | ||||||
|  |   .option('--debugger <port>', 'Enable the remote debugger for CasperJS') | ||||||
|  |   .parse(process.argv); | ||||||
|  |  | ||||||
|  | if (program.tests.length === 0) { | ||||||
|  |   program.tests = fs.readdirSync(__dirname).filter(function(f) { return (/^test\.(\w|\.|-)+\.js$/).test(f); }); | ||||||
|  |   program.tests = program.tests.map(function (f) { return path.resolve(__dirname, f); }); // add full paths in | ||||||
|  |   console.log('using files %s', program.tests); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var file_paths = []; | ||||||
|  |  | ||||||
|  | var all_js = program.tests.reduce(function(a,e) { return a && e.slice(-3) == '.js'; }, true); | ||||||
|  |  | ||||||
|  | var get_path = function (/* arguments */) { | ||||||
|  |   if (program.relative) { | ||||||
|  |     return path.join.apply(null, arguments); | ||||||
|  |   } else { | ||||||
|  |     var args = Array.prototype.slice.call(arguments); | ||||||
|  |     args.unshift(__dirname, '..'); | ||||||
|  |     return path.resolve.apply(null, args); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | var get_path_cwd = function (/* arguments */) { | ||||||
|  |   if (program.relative) { | ||||||
|  |     var part_path = path.join.apply(null, arguments); | ||||||
|  |     return path.relative(path.join(__dirname, '..'), path.resolve(process.cwd(), part_path)); | ||||||
|  |   } else { | ||||||
|  |     var args = Array.prototype.slice.call(arguments); | ||||||
|  |     args.unshift(process.cwd()); | ||||||
|  |     return path.resolve.apply(null, args); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | if (all_js && !program.autoInject) { | ||||||
|  |   var all_modules = {}; | ||||||
|  |  | ||||||
|  |   // uses the first instance of the string 'requires local modules: ' | ||||||
|  |   program.tests.forEach(function (testname) { | ||||||
|  |     var full_path = path.resolve(process.cwd(), testname); | ||||||
|  |     var content = fs.readFileSync(full_path).toString(); | ||||||
|  |     var ind = content.indexOf('requires local modules: '); | ||||||
|  |     if (ind > -1) { | ||||||
|  |       ind += 'requires local modules: '.length; | ||||||
|  |       var eol = content.indexOf('\n', ind); | ||||||
|  |       var modules = content.slice(ind, eol).split(/,\s*/); | ||||||
|  |       modules.forEach(function (mod) { | ||||||
|  |         all_modules[get_path('include/', mod) + '.js'] = 1; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var fakes_ind = content.indexOf('requires test modules: '); | ||||||
|  |     if (fakes_ind > -1) { | ||||||
|  |       fakes_ind += 'requires test modules: '.length; | ||||||
|  |       var fakes_eol = content.indexOf('\n', fakes_ind); | ||||||
|  |       var fakes_modules = content.slice(fakes_ind, fakes_eol).split(/,\s*/); | ||||||
|  |       fakes_modules.forEach(function (mod) { | ||||||
|  |         all_modules[get_path('tests/', mod) + '.js'] = 1; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   program.autoInject = Object.keys(all_modules); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if (program.autoInject) { | ||||||
|  |   var temp = require('temp'); | ||||||
|  |   temp.track(); | ||||||
|  |  | ||||||
|  |   var template = { | ||||||
|  |     header: "<html>\n<head>\n<meta charset='utf-8' />\n<link rel='stylesheet' href='" + get_path('node_modules/mocha/mocha.css') + "'/>\n</head>\n<body><div id='mocha'></div>", | ||||||
|  |     script_tag: function(p) { return "<script src='" + p + "'></script>"; }, | ||||||
|  |     footer: "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__']);\nmocha.run(function () { window.__mocha_done = true; });\n</script>\n</body>\n</html>" | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   template.header += "\n" + template.script_tag(get_path('node_modules/chai/chai.js')); | ||||||
|  |   template.header += "\n" + template.script_tag(get_path('node_modules/mocha/mocha.js')); | ||||||
|  |   template.header += "\n" + template.script_tag(get_path('node_modules/sinon/pkg/sinon.js')); | ||||||
|  |   template.header += "\n" + template.script_tag(get_path('node_modules/sinon-chai/lib/sinon-chai.js')); | ||||||
|  |   template.header += "\n" + template.script_tag(get_path('node_modules/sinon-chai/lib/sinon-chai.js')); | ||||||
|  |   template.header += "\n<script>mocha.setup('bdd');</script>"; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   template.header = program.autoInject.reduce(function(acc, sn) { | ||||||
|  |     return acc + "\n" + template.script_tag(get_path_cwd(sn)); | ||||||
|  |   }, template.header); | ||||||
|  |  | ||||||
|  |   file_paths = program.tests.map(function(jsn, ind) { | ||||||
|  |     var templ = template.header; | ||||||
|  |     templ += "\n"; | ||||||
|  |     templ += template.script_tag(get_path_cwd(jsn)); | ||||||
|  |     templ += template.footer; | ||||||
|  |  | ||||||
|  |     var tempfile = temp.openSync({ prefix: 'novnc-zombie-inject-', suffix: '-file_num-'+ind+'.html' }); | ||||||
|  |     fs.writeSync(tempfile.fd, templ); | ||||||
|  |     fs.closeSync(tempfile.fd); | ||||||
|  |     return tempfile.path; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  | } | ||||||
|  | else { | ||||||
|  |   file_paths = program.tests.map(function(fn) { | ||||||
|  |     return path.resolve(process.cwd(), fn); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var use_ansi = false; | ||||||
|  | if (program.color) use_ansi = true; | ||||||
|  | else if (program.disableColor) use_ansi = false; | ||||||
|  | else if (process.stdout.isTTY) use_ansi = true; | ||||||
|  |  | ||||||
|  | var cursor = ansi(process.stdout, { enabled: use_ansi }); | ||||||
|  |  | ||||||
|  | if (program.outputHtml) { | ||||||
|  |   file_paths.forEach(function(path, path_ind) { | ||||||
|  |     fs.readFile(path, function(err, data) { | ||||||
|  |       if (err) { | ||||||
|  |         console.warn(error.stack); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (use_ansi) { | ||||||
|  |           cursor | ||||||
|  |             .bold() | ||||||
|  |             .write(program.tests[path_ind]) | ||||||
|  |             .reset() | ||||||
|  |             .write("\n") | ||||||
|  |             .write(Array(program.tests[path_ind].length+1).join('=')) | ||||||
|  |             .write("\n\n"); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       cursor | ||||||
|  |         .write(data) | ||||||
|  |         .write("\n\n"); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if (program.generateHtml) { | ||||||
|  |   var open_browser; | ||||||
|  |   if (program.openInBrowser) { | ||||||
|  |     open_browser = require('open'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   file_paths.forEach(function(path, path_ind) { | ||||||
|  |     cursor | ||||||
|  |       .bold() | ||||||
|  |       .write(program.tests[path_ind]) | ||||||
|  |       .write(": ") | ||||||
|  |       .reset() | ||||||
|  |       .write(path) | ||||||
|  |       .write("\n"); | ||||||
|  |  | ||||||
|  |     if (program.openInBrowser) { | ||||||
|  |       open_browser(path); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |   console.log(''); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if (program.generateHtml) { | ||||||
|  |   process.stdin.resume();  // pause until C-c | ||||||
|  |   process.on('SIGINT', function() { | ||||||
|  |     process.stdin.pause();  // exit | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if (!program.outputHtml && !program.generateHtml) { | ||||||
|  |   var failure_count = 0; | ||||||
|  |  | ||||||
|  |   var prov = require(path.resolve(__dirname, 'run_from_console.'+program.provider+'.js')); | ||||||
|  |  | ||||||
|  |   cursor | ||||||
|  |     .write("Running tests ") | ||||||
|  |     .bold() | ||||||
|  |     .write(program.tests.join(', ')) | ||||||
|  |     .reset() | ||||||
|  |     .grey() | ||||||
|  |     .write(' using provider '+prov.name) | ||||||
|  |     .reset() | ||||||
|  |     .write("\n"); | ||||||
|  |   //console.log("Running tests %s using provider %s", program.tests.join(', '), prov.name); | ||||||
|  |  | ||||||
|  |   var provider = prov.provide_emitter(file_paths, program.debugger); | ||||||
|  |   provider.on('test_ready', function(test_json) { | ||||||
|  |     console.log(''); | ||||||
|  |  | ||||||
|  |     filename = program.tests[test_json.file_ind]; | ||||||
|  |  | ||||||
|  |     cursor.bold(); | ||||||
|  |     console.log('Results for %s:', filename); | ||||||
|  |     console.log(Array('Results for :'.length+filename.length+1).join('=')); | ||||||
|  |     cursor.reset(); | ||||||
|  |  | ||||||
|  |     console.log(''); | ||||||
|  |  | ||||||
|  |     cursor | ||||||
|  |       .write(''+test_json.num_tests+' tests run, ') | ||||||
|  |       .green() | ||||||
|  |       .write(''+test_json.num_passes+' passed'); | ||||||
|  |     if (test_json.num_slow > 0) { | ||||||
|  |       cursor | ||||||
|  |         .reset() | ||||||
|  |         .write(' ('); | ||||||
|  |       cursor | ||||||
|  |         .yellow() | ||||||
|  |         .write(''+test_json.num_slow+' slow') | ||||||
|  |         .reset() | ||||||
|  |         .write(')'); | ||||||
|  |     } | ||||||
|  |     cursor | ||||||
|  |       .reset() | ||||||
|  |       .write(', '); | ||||||
|  |     cursor | ||||||
|  |       .red() | ||||||
|  |       .write(''+test_json.num_fails+' failed'); | ||||||
|  |     if (test_json.num_skipped > 0) { | ||||||
|  |       cursor | ||||||
|  |         .reset() | ||||||
|  |         .write(', ') | ||||||
|  |         .grey() | ||||||
|  |         .write(''+test_json.num_skipped+' skipped'); | ||||||
|  |     } | ||||||
|  |     cursor | ||||||
|  |       .reset() | ||||||
|  |       .write(' -- duration: '+test_json.duration+"s\n"); | ||||||
|  |  | ||||||
|  |     console.log(''); | ||||||
|  |  | ||||||
|  |     if (test_json.num_fails > 0 || program.printAll) { | ||||||
|  |       var extract_error_lines = function (err) { | ||||||
|  |         // the split is to avoid a weird thing where in PhantomJS where we get a stack trace too | ||||||
|  |         var err_lines = err.split('\n'); | ||||||
|  |         if (err_lines.length == 1) { | ||||||
|  |           return err_lines[0]; | ||||||
|  |         } else { | ||||||
|  |           var ind; | ||||||
|  |           for (ind = 0; ind < err_lines.length; ind++) { | ||||||
|  |             var at_ind = err_lines[ind].trim().indexOf('at '); | ||||||
|  |             if (at_ind === 0) { | ||||||
|  |               break; | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           return err_lines.slice(0, ind).join('\n'); | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |       var traverse_tree = function(indentation, node) { | ||||||
|  |         if (node.type == 'suite') { | ||||||
|  |           if (!node.has_subfailures && !program.printAll) return; | ||||||
|  |  | ||||||
|  |           if (indentation === 0) { | ||||||
|  |             cursor.bold(); | ||||||
|  |             console.log(node.name); | ||||||
|  |             console.log(Array(node.name.length+1).join('-')); | ||||||
|  |             cursor.reset(); | ||||||
|  |           } | ||||||
|  |           else { | ||||||
|  |             cursor | ||||||
|  |               .write(Array(indentation+3).join('#')) | ||||||
|  |               .bold() | ||||||
|  |               .write(' '+node.name+' ') | ||||||
|  |               .reset() | ||||||
|  |               .write(Array(indentation+3).join('#')) | ||||||
|  |               .write("\n"); | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           console.log(''); | ||||||
|  |  | ||||||
|  |           for (var i = 0; i < node.children.length; i++) { | ||||||
|  |             traverse_tree(indentation+1, node.children[i]); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |           if (!node.pass) { | ||||||
|  |             cursor.magenta(); | ||||||
|  |             console.log('- failed: '+node.text+test_json.replay); | ||||||
|  |             cursor.red(); | ||||||
|  |             console.log('          '+extract_error_lines(node.error)); | ||||||
|  |             cursor.reset(); | ||||||
|  |             console.log(''); | ||||||
|  |           } | ||||||
|  |           else if (program.printAll) { | ||||||
|  |             if (node.skipped) { | ||||||
|  |               cursor | ||||||
|  |                 .grey() | ||||||
|  |                 .write('- skipped: '+node.text); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |               if (node.slow) cursor.yellow(); | ||||||
|  |               else cursor.green(); | ||||||
|  |  | ||||||
|  |               cursor | ||||||
|  |                 .write('- pass: '+node.text) | ||||||
|  |                 .grey() | ||||||
|  |                 .write(' ('+node.duration+') '); | ||||||
|  |             } | ||||||
|  |             /*if (node.slow) cursor.yellow(); | ||||||
|  |             else cursor.green();*/ | ||||||
|  |             cursor | ||||||
|  |               //.write(test_json.replay) | ||||||
|  |               .reset() | ||||||
|  |               .write("\n"); | ||||||
|  |             console.log(''); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |       for (var i = 0; i < test_json.suites.length; i++) { | ||||||
|  |         traverse_tree(0, test_json.suites[i]); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (test_json.num_fails === 0) { | ||||||
|  |       cursor.fg.green(); | ||||||
|  |       console.log('all tests passed :-)'); | ||||||
|  |       cursor.reset(); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   if (program.debug) { | ||||||
|  |     provider.on('console', function(line) { | ||||||
|  |       // log to stderr | ||||||
|  |       console.error(line); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   provider.on('error', function(line) { | ||||||
|  |     // log to stderr | ||||||
|  |     console.error('ERROR: ' + line); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   /*gprom.finally(function(ph) { | ||||||
|  |     ph.exit(); | ||||||
|  |     // exit with a status code that actually gives information | ||||||
|  |     if (program.exitWithFailureCount) process.exit(failure_count); | ||||||
|  |   });*/ | ||||||
|  | } | ||||||
							
								
								
									
										82
									
								
								public/novnc/tests/run_from_console.zombie.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,82 @@ | |||||||
|  | var Browser = require('zombie'); | ||||||
|  | var path = require('path'); | ||||||
|  | var EventEmitter = require('events').EventEmitter; | ||||||
|  | var Q = require('q'); | ||||||
|  |  | ||||||
|  | var provide_emitter = function(file_paths) { | ||||||
|  |   var emitter = new EventEmitter(); | ||||||
|  |  | ||||||
|  |   file_paths.reduce(function(prom, file_path, path_ind) { | ||||||
|  |     return prom.then(function(browser) { | ||||||
|  |       browser.visit('file://'+file_path, function() { | ||||||
|  |         if (browser.error) throw new Error(browser.errors); | ||||||
|  |  | ||||||
|  |         var res_json = {}; | ||||||
|  |         res_json.file_ind = path_ind; | ||||||
|  |  | ||||||
|  |         res_json.num_tests = browser.querySelectorAll('li.test').length; | ||||||
|  |         res_json.num_fails = browser.querySelectorAll('li.test.fail').length; | ||||||
|  |         res_json.num_passes = browser.querySelectorAll('li.test.pass').length; | ||||||
|  |         res_json.num_slow = browser.querySelectorAll('li.test.pass:not(.fast)').length; | ||||||
|  |         res_json.num_skipped = browser.querySelectorAll('li.test.pending').length; | ||||||
|  |         res_json.duration = browser.text('li.duration em'); | ||||||
|  |  | ||||||
|  |         var traverse_node = function(elem) { | ||||||
|  |           var classList = elem.className.split(' '); | ||||||
|  |           var res; | ||||||
|  |           if (classList.indexOf('suite') > -1) { | ||||||
|  |             res = { | ||||||
|  |               type: 'suite', | ||||||
|  |               name: elem.querySelector('h1').textContent, | ||||||
|  |               has_subfailures: elem.querySelectorAll('li.test.fail').length > 0 | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             var child_elems = elem.querySelector('ul').children; | ||||||
|  |             res.children = Array.prototype.map.call(child_elems, traverse_node); | ||||||
|  |             return res; | ||||||
|  |           } | ||||||
|  |           else { | ||||||
|  |             var h2_content = elem.querySelector('h2').childNodes; | ||||||
|  |             res = { | ||||||
|  |               type: 'test', | ||||||
|  |               text: h2_content[0].textContent | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             if (classList.indexOf('pass') > -1) { | ||||||
|  |               res.pass = true; | ||||||
|  |               if (classList.indexOf('pending') > -1) { | ||||||
|  |                 res.slow = false; | ||||||
|  |                 res.skipped = true; | ||||||
|  |               } | ||||||
|  |               else { | ||||||
|  |                 res.slow = classList.indexOf('fast') < 0; | ||||||
|  |                 res.skipped = false; | ||||||
|  |                 res.duration = h2_content[1].textContent; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |               res.error = elem.querySelector('pre.error').textContent; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return res; | ||||||
|  |           } | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         var top_suites = browser.querySelectorAll('#mocha-report > li.suite'); | ||||||
|  |         res_json.suites = Array.prototype.map.call(top_suites, traverse_node); | ||||||
|  |         res_json.replay = browser.querySelector('a.replay').textContent; | ||||||
|  |  | ||||||
|  |         emitter.emit('test_ready', res_json); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       return new Browser(); | ||||||
|  |     }); | ||||||
|  |   }, Q(new Browser())); | ||||||
|  |  | ||||||
|  |   return emitter; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | module.exports = { | ||||||
|  |   provide_emitter: provide_emitter, | ||||||
|  |   name: 'ZombieJS' | ||||||
|  | }; | ||||||
							
								
								
									
										53
									
								
								public/novnc/tests/stats.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,53 @@ | |||||||
|  | /* | ||||||
|  |  * Define some useful statistical functions on arrays of numbers | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | Array.prototype.sum = function() { | ||||||
|  |     var i, sum = 0; | ||||||
|  |     for (i = 0; i < this.length; i++) { | ||||||
|  |         sum += this[i]; | ||||||
|  |     } | ||||||
|  |     return sum; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Array.prototype.max = function() { | ||||||
|  |     return Math.max.apply(null, this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Array.prototype.min = function() { | ||||||
|  |     return Math.min.apply(null, this); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Array.prototype.mean = function() { | ||||||
|  |     return this.sum() / this.length; | ||||||
|  | } | ||||||
|  | Array.prototype.average = Array.prototype.mean; | ||||||
|  |  | ||||||
|  | Array.prototype.median = function() { | ||||||
|  |     var sorted = this.sort( function(a,b) { return a-b; }), | ||||||
|  |         len = sorted.length; | ||||||
|  |     if (len % 2) { | ||||||
|  |         return sorted[Math.floor(len / 2)]; // Odd | ||||||
|  |     } else { | ||||||
|  |         return (sorted[len/2 - 1] + sorted[len/2]) / 2; // Even | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Array.prototype.stdDev = function(sample) { | ||||||
|  |     var i, sumSqr = 0, mean = this.mean(), N; | ||||||
|  |  | ||||||
|  |     if (sample) { | ||||||
|  |         // Population correction if this is a sample | ||||||
|  |         N = this.length - 1; | ||||||
|  |     } else { | ||||||
|  |         // Standard deviation of just the array | ||||||
|  |         N = this.length; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (i = 0; i < this.length; i++) { | ||||||
|  |         sumSqr += Math.pow(this[i] - mean, 2); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Math.sqrt(sumSqr / N); | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										33
									
								
								public/novnc/tests/test.base64.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,33 @@ | |||||||
|  | // requires local modules: base64 | ||||||
|  | var assert = chai.assert; | ||||||
|  | var expect = chai.expect; | ||||||
|  |  | ||||||
|  | describe('Base64 Tools', function() { | ||||||
|  |     "use strict"; | ||||||
|  |  | ||||||
|  |     var BIN_ARR = new Array(256); | ||||||
|  |     for (var i = 0; i < 256; i++) { | ||||||
|  |         BIN_ARR[i] = i; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     var B64_STR = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     describe('encode', function() { | ||||||
|  |         it('should encode a binary string into Base64', function() { | ||||||
|  |             var encoded = Base64.encode(BIN_ARR); | ||||||
|  |             expect(encoded).to.equal(B64_STR); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('decode', function() { | ||||||
|  |         it('should decode a Base64 string into a normal string', function() { | ||||||
|  |             var decoded = Base64.decode(B64_STR); | ||||||
|  |             expect(decoded).to.deep.equal(BIN_ARR); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should throw an error if we have extra characters at the end of the string', function() { | ||||||
|  |             expect(function () { Base64.decode(B64_STR+'abcdef'); }).to.throw(Error); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
							
								
								
									
										461
									
								
								public/novnc/tests/test.display.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,461 @@ | |||||||
|  | // requires local modules: util, base64, display | ||||||
|  | // requires test modules: assertions | ||||||
|  | /* jshint expr: true */ | ||||||
|  | var expect = chai.expect; | ||||||
|  |  | ||||||
|  | describe('Display/Canvas Helper', function () { | ||||||
|  |     var checked_data = [ | ||||||
|  |         0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, | ||||||
|  |         0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, | ||||||
|  |         0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, | ||||||
|  |         0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255 | ||||||
|  |     ]; | ||||||
|  |     checked_data = new Uint8Array(checked_data); | ||||||
|  |  | ||||||
|  |     var basic_data = [0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255]; | ||||||
|  |     basic_data = new Uint8Array(basic_data); | ||||||
|  |  | ||||||
|  |     function make_image_canvas (input_data) { | ||||||
|  |         var canvas = document.createElement('canvas'); | ||||||
|  |         canvas.width = 4; | ||||||
|  |         canvas.height = 4; | ||||||
|  |         var ctx = canvas.getContext('2d'); | ||||||
|  |         var data = ctx.createImageData(4, 4); | ||||||
|  |         for (var i = 0; i < checked_data.length; i++) { data.data[i] = input_data[i]; } | ||||||
|  |         ctx.putImageData(data, 0, 0); | ||||||
|  |         return canvas; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     describe('checking for cursor uri support', function () { | ||||||
|  |         beforeEach(function () { | ||||||
|  |             this._old_browser_supports_cursor_uris = Util.browserSupportsCursorURIs; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should disable cursor URIs if there is no support', function () { | ||||||
|  |             Util.browserSupportsCursorURIs = function () { return false; }; | ||||||
|  |             var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false }); | ||||||
|  |             expect(display._cursor_uri).to.be.false; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should enable cursor URIs if there is support', function () { | ||||||
|  |             Util.browserSupportsCursorURIs = function () { return true; }; | ||||||
|  |             var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false }); | ||||||
|  |             expect(display._cursor_uri).to.be.true; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('respect the cursor_uri option if there is support', function () { | ||||||
|  |             Util.browserSupportsCursorURIs = function () { return false; }; | ||||||
|  |             var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false, cursor_uri: false }); | ||||||
|  |             expect(display._cursor_uri).to.be.false; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         afterEach(function () { | ||||||
|  |             Util.browserSupportsCursorURIs = this._old_browser_supports_cursor_uris; | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('viewport handling', function () { | ||||||
|  |         var display; | ||||||
|  |         beforeEach(function () { | ||||||
|  |             display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); | ||||||
|  |             display.resize(5, 5); | ||||||
|  |             display.viewportChangeSize(3, 3); | ||||||
|  |             display.viewportChangePos(1, 1); | ||||||
|  |             display.getCleanDirtyReset(); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should take viewport location into consideration when drawing images', function () { | ||||||
|  |             display.set_width(4); | ||||||
|  |             display.set_height(4); | ||||||
|  |             display.viewportChangeSize(2, 2); | ||||||
|  |             display.drawImage(make_image_canvas(basic_data), 1, 1); | ||||||
|  |  | ||||||
|  |             var expected = new Uint8Array(16); | ||||||
|  |             var i; | ||||||
|  |             for (i = 0; i < 8; i++) { expected[i] = basic_data[i]; } | ||||||
|  |             for (i = 8; i < 16; i++) { expected[i] = 0; } | ||||||
|  |             expect(display).to.have.displayed(expected); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should redraw the left side when shifted left', function () { | ||||||
|  |             display.viewportChangePos(-1, 0); | ||||||
|  |             var cdr = display.getCleanDirtyReset(); | ||||||
|  |             expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 2, h: 3 }); | ||||||
|  |             expect(cdr.dirtyBoxes).to.have.length(1); | ||||||
|  |             expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 1, w: 2, h: 3 }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should redraw the right side when shifted right', function () { | ||||||
|  |             display.viewportChangePos(1, 0); | ||||||
|  |             var cdr = display.getCleanDirtyReset(); | ||||||
|  |             expect(cdr.cleanBox).to.deep.equal({ x: 2, y: 1, w: 2, h: 3 }); | ||||||
|  |             expect(cdr.dirtyBoxes).to.have.length(1); | ||||||
|  |             expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 4, y: 1, w: 1, h: 3 }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should redraw the top part when shifted up', function () { | ||||||
|  |             display.viewportChangePos(0, -1); | ||||||
|  |             var cdr = display.getCleanDirtyReset(); | ||||||
|  |             expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 3, h: 2 }); | ||||||
|  |             expect(cdr.dirtyBoxes).to.have.length(1); | ||||||
|  |             expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 0, w: 3, h: 1 }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should redraw the bottom part when shifted down', function () { | ||||||
|  |             display.viewportChangePos(0, 1); | ||||||
|  |             var cdr = display.getCleanDirtyReset(); | ||||||
|  |             expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 2 }); | ||||||
|  |             expect(cdr.dirtyBoxes).to.have.length(1); | ||||||
|  |             expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 4, w: 3, h: 1 }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should reset the entire viewport to being clean after calculating the clean/dirty boxes', function () { | ||||||
|  |             display.viewportChangePos(0, 1); | ||||||
|  |             var cdr1 = display.getCleanDirtyReset(); | ||||||
|  |             var cdr2 = display.getCleanDirtyReset(); | ||||||
|  |             expect(cdr1).to.not.deep.equal(cdr2); | ||||||
|  |             expect(cdr2.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 3 }); | ||||||
|  |             expect(cdr2.dirtyBoxes).to.be.empty; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should simply mark the whole display area as dirty if not using viewports', function () { | ||||||
|  |             display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false }); | ||||||
|  |             display.resize(5, 5); | ||||||
|  |             var cdr = display.getCleanDirtyReset(); | ||||||
|  |             expect(cdr.cleanBox).to.deep.equal({ x: 0, y: 0, w: 0, h: 0 }); | ||||||
|  |             expect(cdr.dirtyBoxes).to.have.length(1); | ||||||
|  |             expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 0, w: 5, h: 5 }); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('clipping', function () { | ||||||
|  |         var display; | ||||||
|  |         beforeEach(function () { | ||||||
|  |             display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); | ||||||
|  |             display.resize(4, 3); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should report true when no max-size and framebuffer > viewport', function () { | ||||||
|  |             display.viewportChangeSize(2,2); | ||||||
|  |             var clipping = display.clippingDisplay(); | ||||||
|  |             expect(clipping).to.be.true; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should report false when no max-size and framebuffer = viewport', function () { | ||||||
|  |             var clipping = display.clippingDisplay(); | ||||||
|  |             expect(clipping).to.be.false; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should report true when viewport > max-size and framebuffer > viewport', function () { | ||||||
|  |             display.viewportChangeSize(2,2); | ||||||
|  |             display.set_maxWidth(1); | ||||||
|  |             display.set_maxHeight(2); | ||||||
|  |             var clipping = display.clippingDisplay(); | ||||||
|  |             expect(clipping).to.be.true; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should report true when viewport > max-size and framebuffer = viewport', function () { | ||||||
|  |             display.set_maxWidth(1); | ||||||
|  |             display.set_maxHeight(2); | ||||||
|  |             var clipping = display.clippingDisplay(); | ||||||
|  |             expect(clipping).to.be.true; | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('resizing', function () { | ||||||
|  |         var display; | ||||||
|  |         beforeEach(function () { | ||||||
|  |             display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); | ||||||
|  |             display.resize(4, 3); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should change the size of the logical canvas', function () { | ||||||
|  |             display.resize(5, 7); | ||||||
|  |             expect(display._fb_width).to.equal(5); | ||||||
|  |             expect(display._fb_height).to.equal(7); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should update the viewport dimensions', function () { | ||||||
|  |             sinon.spy(display, 'viewportChangeSize'); | ||||||
|  |             display.resize(2, 2); | ||||||
|  |             expect(display.viewportChangeSize).to.have.been.calledOnce; | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('rescaling', function () { | ||||||
|  |         var display; | ||||||
|  |         var canvas; | ||||||
|  |  | ||||||
|  |         beforeEach(function () { | ||||||
|  |             display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); | ||||||
|  |             display.resize(4, 3); | ||||||
|  |             canvas = display.get_target(); | ||||||
|  |             document.body.appendChild(canvas); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         afterEach(function () { | ||||||
|  |             document.body.removeChild(canvas); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should not change the bitmap size of the canvas', function () { | ||||||
|  |             display.set_scale(0.5); | ||||||
|  |             expect(canvas.width).to.equal(4); | ||||||
|  |             expect(canvas.height).to.equal(3); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should change the effective rendered size of the canvas', function () { | ||||||
|  |             display.set_scale(0.5); | ||||||
|  |             expect(canvas.clientWidth).to.equal(2); | ||||||
|  |             expect(canvas.clientHeight).to.equal(2); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('autoscaling', function () { | ||||||
|  |         var display; | ||||||
|  |         var canvas; | ||||||
|  |  | ||||||
|  |         beforeEach(function () { | ||||||
|  |             display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true }); | ||||||
|  |             display.resize(4, 3); | ||||||
|  |             canvas = display.get_target(); | ||||||
|  |             document.body.appendChild(canvas); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         afterEach(function () { | ||||||
|  |             document.body.removeChild(canvas); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should preserve aspect ratio while autoscaling', function () { | ||||||
|  |             display.autoscale(16, 9); | ||||||
|  |             expect(canvas.clientWidth / canvas.clientHeight).to.equal(4 / 3); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should use width to determine scale when the current aspect ratio is wider than the target', function () { | ||||||
|  |             expect(display.autoscale(9, 16)).to.equal(9 / 4); | ||||||
|  |             expect(canvas.clientWidth).to.equal(9); | ||||||
|  |             expect(canvas.clientHeight).to.equal(7); // round 9 / (4 / 3) | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should use height to determine scale when the current aspect ratio is taller than the target', function () { | ||||||
|  |             expect(display.autoscale(16, 9)).to.equal(3); // 9 / 3 | ||||||
|  |             expect(canvas.clientWidth).to.equal(12);  // 16 * (4 / 3) | ||||||
|  |             expect(canvas.clientHeight).to.equal(9); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should not change the bitmap size of the canvas', function () { | ||||||
|  |             display.autoscale(16, 9); | ||||||
|  |             expect(canvas.width).to.equal(4); | ||||||
|  |             expect(canvas.height).to.equal(3); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should not upscale when downscaleOnly is true', function () { | ||||||
|  |             expect(display.autoscale(2, 2, true)).to.equal(0.5); | ||||||
|  |             expect(canvas.clientWidth).to.equal(2); | ||||||
|  |             expect(canvas.clientHeight).to.equal(2); | ||||||
|  |  | ||||||
|  |             expect(display.autoscale(16, 9, true)).to.equal(1.0); | ||||||
|  |             expect(canvas.clientWidth).to.equal(4); | ||||||
|  |             expect(canvas.clientHeight).to.equal(3); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('drawing', function () { | ||||||
|  |  | ||||||
|  |         // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the | ||||||
|  |         //                     basic cases | ||||||
|  |         function drawing_tests (pref_js) { | ||||||
|  |             var display; | ||||||
|  |             beforeEach(function () { | ||||||
|  |                 display = new Display({ target: document.createElement('canvas'), prefer_js: pref_js }); | ||||||
|  |                 display.resize(4, 4); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should clear the screen on #clear without a logo set', function () { | ||||||
|  |                 display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]); | ||||||
|  |                 display._logo = null; | ||||||
|  |                 display.clear(); | ||||||
|  |                 display.resize(4, 4); | ||||||
|  |                 var empty = []; | ||||||
|  |                 for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; } | ||||||
|  |                 expect(display).to.have.displayed(new Uint8Array(empty)); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should draw the logo on #clear with a logo set', function (done) { | ||||||
|  |                 display._logo = { width: 4, height: 4, data: make_image_canvas(checked_data).toDataURL() }; | ||||||
|  |                 display._drawCtx._act_drawImg = display._drawCtx.drawImage; | ||||||
|  |                 display._drawCtx.drawImage = function (img, x, y) { | ||||||
|  |                     this._act_drawImg(img, x, y); | ||||||
|  |                     expect(display).to.have.displayed(checked_data); | ||||||
|  |                     done(); | ||||||
|  |                 }; | ||||||
|  |                 display.clear(); | ||||||
|  |                 expect(display._fb_width).to.equal(4); | ||||||
|  |                 expect(display._fb_height).to.equal(4); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should support filling a rectangle with particular color via #fillRect', function () { | ||||||
|  |                 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); | ||||||
|  |                 display.fillRect(0, 0, 2, 2, [0xff, 0, 0]); | ||||||
|  |                 display.fillRect(2, 2, 2, 2, [0xff, 0, 0]); | ||||||
|  |                 expect(display).to.have.displayed(checked_data); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should support copying an portion of the canvas via #copyImage', function () { | ||||||
|  |                 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]); | ||||||
|  |                 display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]); | ||||||
|  |                 display.copyImage(0, 0, 2, 2, 2, 2); | ||||||
|  |                 expect(display).to.have.displayed(checked_data); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should support drawing tile data with a background color and sub tiles', function () { | ||||||
|  |                 display.startTile(0, 0, 4, 4, [0, 0xff, 0]); | ||||||
|  |                 display.subTile(0, 0, 2, 2, [0xff, 0, 0]); | ||||||
|  |                 display.subTile(2, 2, 2, 2, [0xff, 0, 0]); | ||||||
|  |                 display.finishTile(); | ||||||
|  |                 expect(display).to.have.displayed(checked_data); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should support drawing BGRX blit images with true color via #blitImage', function () { | ||||||
|  |                 var data = []; | ||||||
|  |                 for (var i = 0; i < 16; i++) { | ||||||
|  |                     data[i * 4] = checked_data[i * 4 + 2]; | ||||||
|  |                     data[i * 4 + 1] = checked_data[i * 4 + 1]; | ||||||
|  |                     data[i * 4 + 2] = checked_data[i * 4]; | ||||||
|  |                     data[i * 4 + 3] = checked_data[i * 4 + 3]; | ||||||
|  |                 } | ||||||
|  |                 display.blitImage(0, 0, 4, 4, data, 0); | ||||||
|  |                 expect(display).to.have.displayed(checked_data); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should support drawing RGB blit images with true color via #blitRgbImage', function () { | ||||||
|  |                 var data = []; | ||||||
|  |                 for (var i = 0; i < 16; i++) { | ||||||
|  |                     data[i * 3] = checked_data[i * 4]; | ||||||
|  |                     data[i * 3 + 1] = checked_data[i * 4 + 1]; | ||||||
|  |                     data[i * 3 + 2] = checked_data[i * 4 + 2]; | ||||||
|  |                 } | ||||||
|  |                 display.blitRgbImage(0, 0, 4, 4, data, 0); | ||||||
|  |                 expect(display).to.have.displayed(checked_data); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should support drawing blit images from a data URL via #blitStringImage', function (done) { | ||||||
|  |                 var img_url = make_image_canvas(checked_data).toDataURL(); | ||||||
|  |                 display._drawCtx._act_drawImg = display._drawCtx.drawImage; | ||||||
|  |                 display._drawCtx.drawImage = function (img, x, y) { | ||||||
|  |                     this._act_drawImg(img, x, y); | ||||||
|  |                     expect(display).to.have.displayed(checked_data); | ||||||
|  |                     done(); | ||||||
|  |                 }; | ||||||
|  |                 display.blitStringImage(img_url, 0, 0); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should support drawing solid colors with color maps', function () { | ||||||
|  |                 display._true_color = false; | ||||||
|  |                 display.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] }); | ||||||
|  |                 display.fillRect(0, 0, 4, 4, 1); | ||||||
|  |                 display.fillRect(0, 0, 2, 2, 0); | ||||||
|  |                 display.fillRect(2, 2, 2, 2, 0); | ||||||
|  |                 expect(display).to.have.displayed(checked_data); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should support drawing blit images with color maps', function () { | ||||||
|  |                 display._true_color = false; | ||||||
|  |                 display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] }); | ||||||
|  |                 var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; }); | ||||||
|  |                 display.blitImage(0, 0, 4, 4, data, 0); | ||||||
|  |                 expect(display).to.have.displayed(checked_data); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should support drawing an image object via #drawImage', function () { | ||||||
|  |                 var img = make_image_canvas(checked_data); | ||||||
|  |                 display.drawImage(img, 0, 0); | ||||||
|  |                 expect(display).to.have.displayed(checked_data); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         describe('(prefering native methods)', function () { drawing_tests.call(this, false); }); | ||||||
|  |         describe('(prefering JavaScript)', function () { drawing_tests.call(this, true); }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('the render queue processor', function () { | ||||||
|  |         var display; | ||||||
|  |         beforeEach(function () { | ||||||
|  |             display = new Display({ target: document.createElement('canvas'), prefer_js: false }); | ||||||
|  |             display.resize(4, 4); | ||||||
|  |             sinon.spy(display, '_scan_renderQ'); | ||||||
|  |             this.old_requestAnimFrame = window.requestAnimFrame; | ||||||
|  |             window.requestAnimFrame = function (cb) { | ||||||
|  |                 this.next_frame_cb = cb; | ||||||
|  |             }.bind(this); | ||||||
|  |             this.next_frame = function () { this.next_frame_cb(); }; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         afterEach(function () { | ||||||
|  |             window.requestAnimFrame = this.old_requestAnimFrame; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should try to process an item when it is pushed on, if nothing else is on the queue', function () { | ||||||
|  |             display.renderQ_push({ type: 'noop' });  // does nothing | ||||||
|  |             expect(display._scan_renderQ).to.have.been.calledOnce; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should not try to process an item when it is pushed on if we are waiting for other items', function () { | ||||||
|  |             display._renderQ.length = 2; | ||||||
|  |             display.renderQ_push({ type: 'noop' }); | ||||||
|  |             expect(display._scan_renderQ).to.not.have.been.called; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () { | ||||||
|  |             var img = { complete: false }; | ||||||
|  |             display._renderQ = [{ type: 'img', x: 3, y: 4, img: img }, | ||||||
|  |                                 { type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }]; | ||||||
|  |             display.drawImage = sinon.spy(); | ||||||
|  |             display.fillRect = sinon.spy(); | ||||||
|  |  | ||||||
|  |             display._scan_renderQ(); | ||||||
|  |             expect(display.drawImage).to.not.have.been.called; | ||||||
|  |             expect(display.fillRect).to.not.have.been.called; | ||||||
|  |  | ||||||
|  |             display._renderQ[0].img.complete = true; | ||||||
|  |             this.next_frame(); | ||||||
|  |             expect(display.drawImage).to.have.been.calledOnce; | ||||||
|  |             expect(display.fillRect).to.have.been.calledOnce; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should draw a blit image on type "blit"', function () { | ||||||
|  |             display.blitImage = sinon.spy(); | ||||||
|  |             display.renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] }); | ||||||
|  |             expect(display.blitImage).to.have.been.calledOnce; | ||||||
|  |             expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should draw a blit RGB image on type "blitRgb"', function () { | ||||||
|  |             display.blitRgbImage = sinon.spy(); | ||||||
|  |             display.renderQ_push({ type: 'blitRgb', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] }); | ||||||
|  |             expect(display.blitRgbImage).to.have.been.calledOnce; | ||||||
|  |             expect(display.blitRgbImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should copy a region on type "copy"', function () { | ||||||
|  |             display.copyImage = sinon.spy(); | ||||||
|  |             display.renderQ_push({ type: 'copy', x: 3, y: 4, width: 5, height: 6, old_x: 7, old_y: 8 }); | ||||||
|  |             expect(display.copyImage).to.have.been.calledOnce; | ||||||
|  |             expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should fill a rect with a given color on type "fill"', function () { | ||||||
|  |             display.fillRect = sinon.spy(); | ||||||
|  |             display.renderQ_push({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]}); | ||||||
|  |             expect(display.fillRect).to.have.been.calledOnce; | ||||||
|  |             expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should draw an image from an image object on type "img" (if complete)', function () { | ||||||
|  |             display.drawImage = sinon.spy(); | ||||||
|  |             display.renderQ_push({ type: 'img', x: 3, y: 4, img: { complete: true } }); | ||||||
|  |             expect(display.drawImage).to.have.been.calledOnce; | ||||||
|  |             expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
							
								
								
									
										262
									
								
								public/novnc/tests/test.helper.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,262 @@ | |||||||
|  | // requires local modules: keysym, keysymdef, keyboard | ||||||
|  |  | ||||||
|  | var assert = chai.assert; | ||||||
|  | var expect = chai.expect; | ||||||
|  |  | ||||||
|  | describe('Helpers', function() { | ||||||
|  |     "use strict"; | ||||||
|  |     describe('keysymFromKeyCode', function() { | ||||||
|  |         it('should map known keycodes to keysyms', function() { | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0x41, false), 'a').to.be.equal(0x61); | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0x41, true), 'A').to.be.equal(0x41); | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0xd, false), 'enter').to.be.equal(0xFF0D); | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0x11, false), 'ctrl').to.be.equal(0xFFE3); | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0x12, false), 'alt').to.be.equal(0xFFE9); | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0xe1, false), 'altgr').to.be.equal(0xFE03); | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0x1b, false), 'esc').to.be.equal(0xFF1B); | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0x26, false), 'up').to.be.equal(0xFF52); | ||||||
|  |         }); | ||||||
|  |         it('should return null for unknown keycodes', function() { | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0xc0, false), 'DK æ').to.be.null; | ||||||
|  |             expect(kbdUtil.keysymFromKeyCode(0xde, false), 'DK ø').to.be.null; | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('keysyms.fromUnicode', function() { | ||||||
|  |         it('should map ASCII characters to keysyms', function() { | ||||||
|  |             expect(keysyms.fromUnicode('a'.charCodeAt())).to.have.property('keysym', 0x61); | ||||||
|  |             expect(keysyms.fromUnicode('A'.charCodeAt())).to.have.property('keysym', 0x41); | ||||||
|  |             }); | ||||||
|  |         it('should map Latin-1 characters to keysyms', function() { | ||||||
|  |             expect(keysyms.fromUnicode('ø'.charCodeAt())).to.have.property('keysym', 0xf8); | ||||||
|  |  | ||||||
|  |             expect(keysyms.fromUnicode('é'.charCodeAt())).to.have.property('keysym', 0xe9); | ||||||
|  |         }); | ||||||
|  |         it('should map characters that are in Windows-1252 but not in Latin-1 to keysyms', function() { | ||||||
|  |             expect(keysyms.fromUnicode('Š'.charCodeAt())).to.have.property('keysym', 0x01a9); | ||||||
|  |         }); | ||||||
|  |         it('should map characters which aren\'t in Latin1 *or* Windows-1252 to keysyms', function() { | ||||||
|  |             expect(keysyms.fromUnicode('ŵ'.charCodeAt())).to.have.property('keysym', 0x1000175); | ||||||
|  |         }); | ||||||
|  |         it('should return undefined for unknown codepoints', function() { | ||||||
|  |             expect(keysyms.fromUnicode('\n'.charCodeAt())).to.be.undefined; | ||||||
|  |             expect(keysyms.fromUnicode('\u1F686'.charCodeAt())).to.be.undefined; | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('substituteCodepoint', function() { | ||||||
|  |         it('should replace characters which don\'t have a keysym', function() { | ||||||
|  |             expect(kbdUtil.substituteCodepoint('Ș'.charCodeAt())).to.equal('Ş'.charCodeAt()); | ||||||
|  |             expect(kbdUtil.substituteCodepoint('ș'.charCodeAt())).to.equal('ş'.charCodeAt()); | ||||||
|  |             expect(kbdUtil.substituteCodepoint('Ț'.charCodeAt())).to.equal('Ţ'.charCodeAt()); | ||||||
|  |             expect(kbdUtil.substituteCodepoint('ț'.charCodeAt())).to.equal('ţ'.charCodeAt()); | ||||||
|  |         }); | ||||||
|  |         it('should pass other characters through unchanged', function() { | ||||||
|  |             expect(kbdUtil.substituteCodepoint('T'.charCodeAt())).to.equal('T'.charCodeAt()); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('nonCharacterKey', function() { | ||||||
|  |         it('should  recognize the right keys', function() { | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: 0xd}), 'enter').to.be.defined; | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: 0x08}), 'backspace').to.be.defined; | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: 0x09}), 'tab').to.be.defined; | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: 0x10}), 'shift').to.be.defined; | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: 0x11}), 'ctrl').to.be.defined; | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: 0x12}), 'alt').to.be.defined; | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: 0xe0}), 'meta').to.be.defined; | ||||||
|  |         }); | ||||||
|  |         it('should  not recognize character keys', function() { | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: 'A'}), 'A').to.be.null; | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: '1'}), '1').to.be.null; | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: '.'}), '.').to.be.null; | ||||||
|  |             expect(kbdUtil.nonCharacterKey({keyCode: ' '}), 'space').to.be.null; | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('getKeysym', function() { | ||||||
|  |         it('should prefer char', function() { | ||||||
|  |             expect(kbdUtil.getKeysym({char : 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x61); | ||||||
|  |         }); | ||||||
|  |         it('should use charCode if no char', function() { | ||||||
|  |             expect(kbdUtil.getKeysym({char : '', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9); | ||||||
|  |             expect(kbdUtil.getKeysym({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9); | ||||||
|  |             expect(kbdUtil.getKeysym({char : 'hello', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9); | ||||||
|  |         }); | ||||||
|  |         it('should use keyCode if no charCode', function() { | ||||||
|  |             expect(kbdUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: false})).to.have.property('keysym', 0x62); | ||||||
|  |             expect(kbdUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: true})).to.have.property('keysym', 0x42); | ||||||
|  |         }); | ||||||
|  |         it('should use which if no keyCode', function() { | ||||||
|  |             expect(kbdUtil.getKeysym({which: 0x43, shiftKey: false})).to.have.property('keysym', 0x63); | ||||||
|  |             expect(kbdUtil.getKeysym({which: 0x43, shiftKey: true})).to.have.property('keysym', 0x43); | ||||||
|  |         }); | ||||||
|  |         it('should substitute where applicable', function() { | ||||||
|  |             expect(kbdUtil.getKeysym({char : 'Ș'})).to.have.property('keysym', 0x1aa); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('Modifier Sync', function() { // return a list of fake events necessary to fix modifier state | ||||||
|  |         describe('Toggle all modifiers', function() { | ||||||
|  |             var sync = kbdUtil.ModifierSync(); | ||||||
|  |             it ('should do nothing if all modifiers are up as expected', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     ctrlKey: false, | ||||||
|  |                     altKey: false, | ||||||
|  |                     altGraphKey: false, | ||||||
|  |                     shiftKey: false, | ||||||
|  |                     metaKey: false}) | ||||||
|  |                     ).to.have.lengthOf(0); | ||||||
|  |             }); | ||||||
|  |             it ('should synthesize events if all keys are unexpectedly down', function() { | ||||||
|  |                 var result = sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     ctrlKey: true, | ||||||
|  |                     altKey: true, | ||||||
|  |                     altGraphKey: true, | ||||||
|  |                     shiftKey: true, | ||||||
|  |                     metaKey: true | ||||||
|  |                 }); | ||||||
|  |                 expect(result).to.have.lengthOf(5); | ||||||
|  |                 var keysyms = {}; | ||||||
|  |                 for (var i = 0; i < result.length; ++i) { | ||||||
|  |                     keysyms[result[i].keysym] = (result[i].type == 'keydown'); | ||||||
|  |                 } | ||||||
|  |                 expect(keysyms[0xffe3]); | ||||||
|  |                 expect(keysyms[0xffe9]); | ||||||
|  |                 expect(keysyms[0xfe03]); | ||||||
|  |                 expect(keysyms[0xffe1]); | ||||||
|  |                 expect(keysyms[0xffe7]); | ||||||
|  |             }); | ||||||
|  |             it ('should do nothing if all modifiers are down as expected', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     ctrlKey: true, | ||||||
|  |                     altKey: true, | ||||||
|  |                     altGraphKey: true, | ||||||
|  |                     shiftKey: true, | ||||||
|  |                     metaKey: true | ||||||
|  |                     })).to.have.lengthOf(0); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('Toggle Ctrl', function() { | ||||||
|  |             var sync = kbdUtil.ModifierSync(); | ||||||
|  |             it('should sync if modifier is suddenly down', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     ctrlKey: true, | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe3), type: 'keydown'}]); | ||||||
|  |             }); | ||||||
|  |             it('should sync if modifier is suddenly up', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     ctrlKey: false | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe3), type: 'keyup'}]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('Toggle Alt', function() { | ||||||
|  |             var sync = kbdUtil.ModifierSync(); | ||||||
|  |             it('should sync if modifier is suddenly down', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     altKey: true, | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]); | ||||||
|  |             }); | ||||||
|  |             it('should sync if modifier is suddenly up', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     altKey: false | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keyup'}]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('Toggle AltGr', function() { | ||||||
|  |             var sync = kbdUtil.ModifierSync(); | ||||||
|  |             it('should sync if modifier is suddenly down', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     altGraphKey: true, | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xfe03), type: 'keydown'}]); | ||||||
|  |             }); | ||||||
|  |             it('should sync if modifier is suddenly up', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     altGraphKey: false | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xfe03), type: 'keyup'}]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('Toggle Shift', function() { | ||||||
|  |             var sync = kbdUtil.ModifierSync(); | ||||||
|  |             it('should sync if modifier is suddenly down', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     shiftKey: true, | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe1), type: 'keydown'}]); | ||||||
|  |             }); | ||||||
|  |             it('should sync if modifier is suddenly up', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     shiftKey: false | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe1), type: 'keyup'}]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('Toggle Meta', function() { | ||||||
|  |             var sync = kbdUtil.ModifierSync(); | ||||||
|  |             it('should sync if modifier is suddenly down', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     metaKey: true, | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe7), type: 'keydown'}]); | ||||||
|  |             }); | ||||||
|  |             it('should sync if modifier is suddenly up', function() { | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     metaKey: false | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe7), type: 'keyup'}]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('Modifier keyevents', function() { | ||||||
|  |             it('should not sync a modifier on its own events', function() { | ||||||
|  |                 expect(kbdUtil.ModifierSync().keydown({ | ||||||
|  |                     keyCode: 0x11, | ||||||
|  |                     ctrlKey: false | ||||||
|  |                 })).to.be.deep.equal([]); | ||||||
|  |                 expect(kbdUtil.ModifierSync().keydown({ | ||||||
|  |                     keyCode: 0x11, | ||||||
|  |                     ctrlKey: true | ||||||
|  |                 }), 'B').to.be.deep.equal([]); | ||||||
|  |             }) | ||||||
|  |             it('should update state on modifier keyevents', function() { | ||||||
|  |                 var sync = kbdUtil.ModifierSync(); | ||||||
|  |                 sync.keydown({ | ||||||
|  |                     keyCode: 0x11, | ||||||
|  |                 }); | ||||||
|  |                 expect(sync.keydown({ | ||||||
|  |                     keyCode: 0x41, | ||||||
|  |                     ctrlKey: true, | ||||||
|  |                 })).to.be.deep.equal([]); | ||||||
|  |             }); | ||||||
|  |             it('should sync other modifiers on ctrl events', function() { | ||||||
|  |                 expect(kbdUtil.ModifierSync().keydown({ | ||||||
|  |                     keyCode: 0x11, | ||||||
|  |                     altKey: true | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]); | ||||||
|  |             }) | ||||||
|  |         }); | ||||||
|  |         describe('sync modifiers on non-key events', function() { | ||||||
|  |             it('should generate sync events when receiving non-keyboard events', function() { | ||||||
|  |                 expect(kbdUtil.ModifierSync().syncAny({ | ||||||
|  |                     altKey: true | ||||||
|  |                 })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('do not treat shift as a modifier key', function() { | ||||||
|  |             it('should not treat shift as a shortcut modifier', function() { | ||||||
|  |                 expect(kbdUtil.hasShortcutModifier([], {0xffe1 : true})).to.be.false; | ||||||
|  |             }); | ||||||
|  |             it('should not treat shift as a char modifier', function() { | ||||||
|  |                 expect(kbdUtil.hasCharModifier([], {0xffe1 : true})).to.be.false; | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
							
								
								
									
										842
									
								
								public/novnc/tests/test.keyboard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,842 @@ | |||||||
|  | // requires local modules: input, keyboard, keysymdef | ||||||
|  | var assert = chai.assert; | ||||||
|  | var expect = chai.expect; | ||||||
|  |  | ||||||
|  | /* jshint newcap: false, expr: true */ | ||||||
|  | describe('Key Event Pipeline Stages', function() { | ||||||
|  |     "use strict"; | ||||||
|  |     describe('Decode Keyboard Events', function() { | ||||||
|  |         it('should pass events to the next stage', function(done) { | ||||||
|  |             KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                 expect(evt).to.be.an.object; | ||||||
|  |                 done(); | ||||||
|  |             }).keydown({keyCode: 0x41}); | ||||||
|  |         }); | ||||||
|  |         it('should pass the right keysym through', function(done) { | ||||||
|  |             KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                 expect(evt.keysym).to.be.deep.equal(keysyms.lookup(0x61)); | ||||||
|  |                 done(); | ||||||
|  |             }).keypress({keyCode: 0x41}); | ||||||
|  |         }); | ||||||
|  |         it('should pass the right keyid through', function(done) { | ||||||
|  |             KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                 expect(evt).to.have.property('keyId', 0x41); | ||||||
|  |                 done(); | ||||||
|  |             }).keydown({keyCode: 0x41}); | ||||||
|  |         }); | ||||||
|  |         it('should not sync modifiers on a keypress', function() { | ||||||
|  |             // Firefox provides unreliable modifier state on keypress events | ||||||
|  |             var count = 0; | ||||||
|  |             KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                 ++count; | ||||||
|  |             }).keypress({keyCode: 0x41, ctrlKey: true}); | ||||||
|  |             expect(count).to.be.equal(1); | ||||||
|  |         }); | ||||||
|  |         it('should sync modifiers if necessary', function(done) { | ||||||
|  |             var count = 0; | ||||||
|  |             KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                 switch (count) { | ||||||
|  |                 case 0: // fake a ctrl keydown | ||||||
|  |                     expect(evt).to.be.deep.equal({keysym: keysyms.lookup(0xffe3), type: 'keydown'}); | ||||||
|  |                     ++count; | ||||||
|  |                     break; | ||||||
|  |                 case 1: | ||||||
|  |                     expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown', keysym: keysyms.lookup(0x61)}); | ||||||
|  |                     done(); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             }).keydown({keyCode: 0x41, ctrlKey: true}); | ||||||
|  |         }); | ||||||
|  |         it('should forward keydown events with the right type', function(done) { | ||||||
|  |             KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                 expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'}); | ||||||
|  |                 done(); | ||||||
|  |             }).keydown({keyCode: 0x41}); | ||||||
|  |         }); | ||||||
|  |         it('should forward keyup events with the right type', function(done) { | ||||||
|  |             KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                 expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keyup'}); | ||||||
|  |                 done(); | ||||||
|  |             }).keyup({keyCode: 0x41}); | ||||||
|  |         }); | ||||||
|  |         it('should forward keypress events with the right type', function(done) { | ||||||
|  |             KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                 expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keypress'}); | ||||||
|  |                 done(); | ||||||
|  |             }).keypress({keyCode: 0x41}); | ||||||
|  |         }); | ||||||
|  |         it('should generate stalls if a char modifier is down while a key is pressed', function(done) { | ||||||
|  |             var count = 0; | ||||||
|  |             KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) { | ||||||
|  |                 switch (count) { | ||||||
|  |                 case 0: // fake altgr | ||||||
|  |                     expect(evt).to.be.deep.equal({keysym: keysyms.lookup(0xfe03), type: 'keydown'}); | ||||||
|  |                     ++count; | ||||||
|  |                     break; | ||||||
|  |                 case 1: // stall before processing the 'a' keydown | ||||||
|  |                     expect(evt).to.be.deep.equal({type: 'stall'}); | ||||||
|  |                     ++count; | ||||||
|  |                     break; | ||||||
|  |                 case 2: // 'a' | ||||||
|  |                     expect(evt).to.be.deep.equal({ | ||||||
|  |                         type: 'keydown', | ||||||
|  |                         keyId: 0x41, | ||||||
|  |                         keysym: keysyms.lookup(0x61) | ||||||
|  |                     }); | ||||||
|  |  | ||||||
|  |                     done(); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             }).keydown({keyCode: 0x41, altGraphKey: true}); | ||||||
|  |  | ||||||
|  |         }); | ||||||
|  |         describe('suppress the right events at the right time', function() { | ||||||
|  |             it('should suppress anything while a shortcut modifier is down', function() { | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {}); | ||||||
|  |  | ||||||
|  |                 obj.keydown({keyCode: 0x11}); // press ctrl | ||||||
|  |                 expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.true; | ||||||
|  |                 expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.true; | ||||||
|  |                 expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.true; | ||||||
|  |                 expect(obj.keydown({keyCode: 0x3c})).to.be.true; // < key on DK Windows | ||||||
|  |                 expect(obj.keydown({keyCode: 0xde})).to.be.true; // Ø key on DK | ||||||
|  |             }); | ||||||
|  |             it('should suppress non-character keys', function() { | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {}); | ||||||
|  |  | ||||||
|  |                 expect(obj.keydown({keyCode: 0x08}), 'a').to.be.true; | ||||||
|  |                 expect(obj.keydown({keyCode: 0x09}), 'b').to.be.true; | ||||||
|  |                 expect(obj.keydown({keyCode: 0x11}), 'd').to.be.true; | ||||||
|  |                 expect(obj.keydown({keyCode: 0x12}), 'e').to.be.true; | ||||||
|  |             }); | ||||||
|  |             it('should not suppress shift', function() { | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {}); | ||||||
|  |  | ||||||
|  |                 expect(obj.keydown({keyCode: 0x10}), 'd').to.be.false; | ||||||
|  |             }); | ||||||
|  |             it('should generate event for shift keydown', function() { | ||||||
|  |                 var called = false; | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                     expect(evt).to.have.property('keysym'); | ||||||
|  |                     called = true; | ||||||
|  |                 }).keydown({keyCode: 0x10}); | ||||||
|  |                 expect(called).to.be.true; | ||||||
|  |             }); | ||||||
|  |             it('should not suppress character keys', function() { | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {}); | ||||||
|  |  | ||||||
|  |                 expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false; | ||||||
|  |                 expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false; | ||||||
|  |                 expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.false; | ||||||
|  |                 expect(obj.keydown({keyCode: 0x3c})).to.be.false; // < key on DK Windows | ||||||
|  |                 expect(obj.keydown({keyCode: 0xde})).to.be.false; // Ø key on DK | ||||||
|  |             }); | ||||||
|  |             it('should not suppress if a char modifier is down', function() { | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {}); | ||||||
|  |  | ||||||
|  |                 obj.keydown({keyCode: 0xe1}); // press altgr | ||||||
|  |                 expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false; | ||||||
|  |                 expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false; | ||||||
|  |                 expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.false; | ||||||
|  |                 expect(obj.keydown({keyCode: 0x3c})).to.be.false; // < key on DK Windows | ||||||
|  |                 expect(obj.keydown({keyCode: 0xde})).to.be.false; // Ø key on DK | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('Keypress and keyup events', function() { | ||||||
|  |             it('should always suppress event propagation', function() { | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {}); | ||||||
|  |  | ||||||
|  |                 expect(obj.keypress({keyCode: 'A'.charCodeAt()})).to.be.true; | ||||||
|  |                 expect(obj.keypress({keyCode: 0x3c})).to.be.true; // < key on DK Windows | ||||||
|  |                 expect(obj.keypress({keyCode: 0x11})).to.be.true; | ||||||
|  |  | ||||||
|  |                 expect(obj.keyup({keyCode: 'A'.charCodeAt()})).to.be.true; | ||||||
|  |                 expect(obj.keyup({keyCode: 0x3c})).to.be.true; // < key on DK Windows | ||||||
|  |                 expect(obj.keyup({keyCode: 0x11})).to.be.true; | ||||||
|  |             }); | ||||||
|  |             it('should never generate stalls', function() { | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                     expect(evt.type).to.not.be.equal('stall'); | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 obj.keypress({keyCode: 'A'.charCodeAt()}); | ||||||
|  |                 obj.keypress({keyCode: 0x3c}); | ||||||
|  |                 obj.keypress({keyCode: 0x11}); | ||||||
|  |  | ||||||
|  |                 obj.keyup({keyCode: 'A'.charCodeAt()}); | ||||||
|  |                 obj.keyup({keyCode: 0x3c}); | ||||||
|  |                 obj.keyup({keyCode: 0x11}); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('mark events if a char modifier is down', function() { | ||||||
|  |             it('should not mark modifiers on a keydown event', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) { | ||||||
|  |                     switch (times_called++) { | ||||||
|  |                     case 0: //altgr | ||||||
|  |                         break; | ||||||
|  |                     case 1: // 'a' | ||||||
|  |                         expect(evt).to.not.have.property('escape'); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 obj.keydown({keyCode: 0xe1}); // press altgr | ||||||
|  |                 obj.keydown({keyCode: 'A'.charCodeAt()}); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should indicate on events if a single-key char modifier is down', function(done) { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) { | ||||||
|  |                     switch (times_called++) { | ||||||
|  |                     case 0: //altgr | ||||||
|  |                         break; | ||||||
|  |                     case 1: // 'a' | ||||||
|  |                         expect(evt).to.be.deep.equal({ | ||||||
|  |                             type: 'keypress', | ||||||
|  |                             keyId: 'A'.charCodeAt(), | ||||||
|  |                             keysym: keysyms.lookup('a'.charCodeAt()), | ||||||
|  |                             escape: [0xfe03] | ||||||
|  |                         }); | ||||||
|  |                         done(); | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 obj.keydown({keyCode: 0xe1}); // press altgr | ||||||
|  |                 obj.keypress({keyCode: 'A'.charCodeAt()}); | ||||||
|  |             }); | ||||||
|  |             it('should indicate on events if a multi-key char modifier is down', function(done) { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xffe9, 0xffe3]), function(evt) { | ||||||
|  |                     switch (times_called++) { | ||||||
|  |                     case 0: //ctrl | ||||||
|  |                         break; | ||||||
|  |                     case 1: //alt | ||||||
|  |                         break; | ||||||
|  |                     case 2: // 'a' | ||||||
|  |                         expect(evt).to.be.deep.equal({ | ||||||
|  |                             type: 'keypress', | ||||||
|  |                             keyId: 'A'.charCodeAt(), | ||||||
|  |                             keysym: keysyms.lookup('a'.charCodeAt()), | ||||||
|  |                             escape: [0xffe9, 0xffe3] | ||||||
|  |                         }); | ||||||
|  |                         done(); | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 obj.keydown({keyCode: 0x11}); // press ctrl | ||||||
|  |                 obj.keydown({keyCode: 0x12}); // press alt | ||||||
|  |                 obj.keypress({keyCode: 'A'.charCodeAt()}); | ||||||
|  |             }); | ||||||
|  |             it('should not consider a char modifier to be down on the modifier key itself', function() { | ||||||
|  |                 var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) { | ||||||
|  |                     expect(evt).to.not.have.property('escape'); | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 obj.keydown({keyCode: 0xe1}); // press altgr | ||||||
|  |  | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('add/remove keysym', function() { | ||||||
|  |             it('should remove keysym from keydown if a char key and no modifier', function() { | ||||||
|  |                 KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                     expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'}); | ||||||
|  |                 }).keydown({keyCode: 0x41}); | ||||||
|  |             }); | ||||||
|  |             it('should not remove keysym from keydown if a shortcut modifier is down', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                     switch (times_called++) { | ||||||
|  |                     case 1: | ||||||
|  |                         expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keydown'}); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 }).keydown({keyCode: 0x41, ctrlKey: true}); | ||||||
|  |                 expect(times_called).to.be.equal(2); | ||||||
|  |             }); | ||||||
|  |             it('should not remove keysym from keydown if a char modifier is down', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) { | ||||||
|  |                     switch (times_called++) { | ||||||
|  |                     case 2: | ||||||
|  |                         expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keydown'}); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 }).keydown({keyCode: 0x41, altGraphKey: true}); | ||||||
|  |                 expect(times_called).to.be.equal(3); | ||||||
|  |             }); | ||||||
|  |             it('should not remove keysym from keydown if key is noncharacter', function() { | ||||||
|  |                 KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                     expect(evt, 'bacobjpace').to.be.deep.equal({keyId: 0x09, keysym: keysyms.lookup(0xff09), type: 'keydown'}); | ||||||
|  |                 }).keydown({keyCode: 0x09}); | ||||||
|  |  | ||||||
|  |                 KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                     expect(evt, 'ctrl').to.be.deep.equal({keyId: 0x11, keysym: keysyms.lookup(0xffe3), type: 'keydown'}); | ||||||
|  |                 }).keydown({keyCode: 0x11}); | ||||||
|  |             }); | ||||||
|  |             it('should never remove keysym from keypress', function() { | ||||||
|  |                 KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                     expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keypress'}); | ||||||
|  |                 }).keypress({keyCode: 0x41}); | ||||||
|  |             }); | ||||||
|  |             it('should never remove keysym from keyup', function() { | ||||||
|  |                 KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) { | ||||||
|  |                     expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keyup'}); | ||||||
|  |                 }).keyup({keyCode: 0x41}); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         // on keypress, keyup(?), always set keysym | ||||||
|  |         // on keydown, only do it if we don't expect a keypress: if noncharacter OR modifier is down | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('Verify that char modifiers are active', function() { | ||||||
|  |         it('should pass keydown events through if there is no stall', function(done) { | ||||||
|  |             var obj = VerifyCharModifier(function(evt){ | ||||||
|  |                 expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)}); | ||||||
|  |                 done(); | ||||||
|  |             })({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)}); | ||||||
|  |         }); | ||||||
|  |         it('should pass keyup events through if there is no stall', function(done) { | ||||||
|  |             var obj = VerifyCharModifier(function(evt){ | ||||||
|  |                 expect(evt).to.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x41)}); | ||||||
|  |                 done(); | ||||||
|  |             })({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x41)}); | ||||||
|  |         }); | ||||||
|  |         it('should pass keypress events through if there is no stall', function(done) { | ||||||
|  |             var obj = VerifyCharModifier(function(evt){ | ||||||
|  |                 expect(evt).to.deep.equal({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x41)}); | ||||||
|  |                 done(); | ||||||
|  |             })({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x41)}); | ||||||
|  |         }); | ||||||
|  |         it('should not pass stall events through', function(done){ | ||||||
|  |             var obj = VerifyCharModifier(function(evt){ | ||||||
|  |                 // should only be called once, for the keydown | ||||||
|  |                 expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)}); | ||||||
|  |                 done(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             obj({type: 'stall'}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)}); | ||||||
|  |         }); | ||||||
|  |         it('should merge keydown and keypress events if they come after a stall', function(done) { | ||||||
|  |             var next_called = false; | ||||||
|  |             var obj = VerifyCharModifier(function(evt){ | ||||||
|  |                 // should only be called once, for the keydown | ||||||
|  |                 expect(next_called).to.be.false; | ||||||
|  |                 next_called = true; | ||||||
|  |                 expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x44)}); | ||||||
|  |                 done(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             obj({type: 'stall'}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |             obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44)}); | ||||||
|  |             expect(next_called).to.be.false; | ||||||
|  |         }); | ||||||
|  |         it('should preserve modifier attribute when merging if keysyms differ', function(done) { | ||||||
|  |             var next_called = false; | ||||||
|  |             var obj = VerifyCharModifier(function(evt){ | ||||||
|  |                 // should only be called once, for the keydown | ||||||
|  |                 expect(next_called).to.be.false; | ||||||
|  |                 next_called = true; | ||||||
|  |                 expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x44), escape: [0xffe3]}); | ||||||
|  |                 done(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             obj({type: 'stall'}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |             obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44), escape: [0xffe3]}); | ||||||
|  |             expect(next_called).to.be.false; | ||||||
|  |         }); | ||||||
|  |         it('should not preserve modifier attribute when merging if keysyms are the same', function() { | ||||||
|  |             var obj = VerifyCharModifier(function(evt){ | ||||||
|  |                 expect(evt).to.not.have.property('escape'); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             obj({type: 'stall'}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |             obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x42), escape: [0xffe3]}); | ||||||
|  |         }); | ||||||
|  |         it('should not merge keydown and keypress events if there is no stall', function(done) { | ||||||
|  |             var times_called = 0; | ||||||
|  |             var obj = VerifyCharModifier(function(evt){ | ||||||
|  |                 switch(times_called) { | ||||||
|  |                 case 0: | ||||||
|  |                     expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |                     break; | ||||||
|  |                 case 1: | ||||||
|  |                     expect(evt).to.deep.equal({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44)}); | ||||||
|  |                     done(); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 ++times_called; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |             obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44)}); | ||||||
|  |         }); | ||||||
|  |         it('should not merge keydown and keypress events if separated by another event', function(done) { | ||||||
|  |             var times_called = 0; | ||||||
|  |             var obj = VerifyCharModifier(function(evt){ | ||||||
|  |                 switch(times_called) { | ||||||
|  |                 case 0: | ||||||
|  |                     expect(evt,1).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |                     break; | ||||||
|  |                 case 1: | ||||||
|  |                     expect(evt,2).to.deep.equal({type: 'keyup', keyId: 0x43, keysym: keysyms.lookup(0x44)}); | ||||||
|  |                     break; | ||||||
|  |                 case 2: | ||||||
|  |                     expect(evt,3).to.deep.equal({type: 'keypress', keyId: 0x45, keysym: keysyms.lookup(0x46)}); | ||||||
|  |                     done(); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 ++times_called; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             obj({type: 'stall'}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |             obj({type: 'keyup', keyId: 0x43, keysym: keysyms.lookup(0x44)}); | ||||||
|  |             obj({type: 'keypress', keyId: 0x45, keysym: keysyms.lookup(0x46)}); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('Track Key State', function() { | ||||||
|  |         it('should do nothing on keyup events if no keys are down', function() { | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 expect(true).to.be.false; | ||||||
|  |             }); | ||||||
|  |             obj({type: 'keyup', keyId: 0x41}); | ||||||
|  |         }); | ||||||
|  |         it('should insert into the queue on keydown if no keys are down', function() { | ||||||
|  |             var times_called = 0; | ||||||
|  |             var elem = null; | ||||||
|  |             var keysymsdown = {}; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 ++times_called; | ||||||
|  |                 if (elem.type == 'keyup') { | ||||||
|  |                 expect(evt).to.have.property('keysym'); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     delete keysymsdown[evt.keysym.keysym]; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     expect(evt).to.be.deep.equal(elem); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                 } | ||||||
|  |                 elem = null; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x42).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keyup', keyId: 0x41}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             expect(times_called).to.be.equal(2); | ||||||
|  |         }); | ||||||
|  |         it('should insert into the queue on keypress if no keys are down', function() { | ||||||
|  |             var times_called = 0; | ||||||
|  |             var elem = null; | ||||||
|  |             var keysymsdown = {}; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 ++times_called; | ||||||
|  |                 if (elem.type == 'keyup') { | ||||||
|  |                     expect(evt).to.have.property('keysym'); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     delete keysymsdown[evt.keysym.keysym]; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     expect(evt).to.be.deep.equal(elem); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                 } | ||||||
|  |                 elem = null; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x42)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x42).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keyup', keyId: 0x41}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             expect(times_called).to.be.equal(2); | ||||||
|  |         }); | ||||||
|  |         it('should add keysym to last key entry if keyId matches', function() { | ||||||
|  |             // this implies that a single keyup will release both keysyms | ||||||
|  |             var times_called = 0; | ||||||
|  |             var elem = null; | ||||||
|  |             var keysymsdown = {}; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 ++times_called; | ||||||
|  |                 if (elem.type == 'keyup') { | ||||||
|  |                     expect(evt).to.have.property('keysym'); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     delete keysymsdown[evt.keysym.keysym]; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     expect(evt).to.be.deep.equal(elem); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     elem = null; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x42)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x42).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x43)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x43).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keyup', keyId: 0x41}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(4); | ||||||
|  |         }); | ||||||
|  |         it('should create new key entry if keyId matches and keysym does not', function() { | ||||||
|  |             // this implies that a single keyup will release both keysyms | ||||||
|  |             var times_called = 0; | ||||||
|  |             var elem = null; | ||||||
|  |             var keysymsdown = {}; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 ++times_called; | ||||||
|  |                 if (elem.type == 'keyup') { | ||||||
|  |                     expect(evt).to.have.property('keysym'); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     delete keysymsdown[evt.keysym.keysym]; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     expect(evt).to.be.deep.equal(elem); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     elem = null; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x42).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x43)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x43).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(2); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keyup', keyId: 0}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(3); | ||||||
|  |             elem = {type: 'keyup', keyId: 0}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(4); | ||||||
|  |         }); | ||||||
|  |         it('should merge key entry if keyIds are zero and keysyms match', function() { | ||||||
|  |             // this implies that a single keyup will release both keysyms | ||||||
|  |             var times_called = 0; | ||||||
|  |             var elem = null; | ||||||
|  |             var keysymsdown = {}; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 ++times_called; | ||||||
|  |                 if (elem.type == 'keyup') { | ||||||
|  |                     expect(evt).to.have.property('keysym'); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     delete keysymsdown[evt.keysym.keysym]; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     expect(evt).to.be.deep.equal(elem); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     elem = null; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x42).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x42).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(2); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keyup', keyId: 0}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(3); | ||||||
|  |         }); | ||||||
|  |         it('should add keysym as separate entry if keyId does not match last event', function() { | ||||||
|  |             // this implies that separate keyups are required | ||||||
|  |             var times_called = 0; | ||||||
|  |             var elem = null; | ||||||
|  |             var keysymsdown = {}; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 ++times_called; | ||||||
|  |                 if (elem.type == 'keyup') { | ||||||
|  |                     expect(evt).to.have.property('keysym'); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     delete keysymsdown[evt.keysym.keysym]; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     expect(evt).to.be.deep.equal(elem); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     elem = null; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x42)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x42).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keypress', keyId: 0x42, keysym: keysyms.lookup(0x43)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x43).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keyup', keyId: 0x41}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(4); | ||||||
|  |             elem = {type: 'keyup', keyId: 0x42}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(4); | ||||||
|  |         }); | ||||||
|  |         it('should add keysym as separate entry if keyId does not match last event and first is zero', function() { | ||||||
|  |             // this implies that separate keyups are required | ||||||
|  |             var times_called = 0; | ||||||
|  |             var elem = null; | ||||||
|  |             var keysymsdown = {}; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 ++times_called; | ||||||
|  |                 if (elem.type == 'keyup') { | ||||||
|  |                     expect(evt).to.have.property('keysym'); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     delete keysymsdown[evt.keysym.keysym]; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     expect(evt).to.be.deep.equal(elem); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     elem = null; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x42).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x43)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x43).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             expect(times_called).to.be.equal(2); | ||||||
|  |             elem = {type: 'keyup', keyId: 0}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(3); | ||||||
|  |             elem = {type: 'keyup', keyId: 0x42}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(4); | ||||||
|  |         }); | ||||||
|  |         it('should add keysym as separate entry if keyId does not match last event and second is zero', function() { | ||||||
|  |             // this implies that a separate keyups are required | ||||||
|  |             var times_called = 0; | ||||||
|  |             var elem = null; | ||||||
|  |             var keysymsdown = {}; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 ++times_called; | ||||||
|  |                 if (elem.type == 'keyup') { | ||||||
|  |                     expect(evt).to.have.property('keysym'); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     delete keysymsdown[evt.keysym.keysym]; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     expect(evt).to.be.deep.equal(elem); | ||||||
|  |                     expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined; | ||||||
|  |                     elem = null; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x42).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x43)}; | ||||||
|  |             keysymsdown[keysyms.lookup(0x43).keysym] = true; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(elem).to.be.null; | ||||||
|  |             elem = {type: 'keyup', keyId: 0x41}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(3); | ||||||
|  |             elem = {type: 'keyup', keyId: 0}; | ||||||
|  |             obj(elem); | ||||||
|  |             expect(times_called).to.be.equal(4); | ||||||
|  |         }); | ||||||
|  |         it('should pop matching key event on keyup', function() { | ||||||
|  |             var times_called = 0; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 switch (times_called++) { | ||||||
|  |                     case 0: | ||||||
|  |                     case 1: | ||||||
|  |                     case 2: | ||||||
|  |                         expect(evt.type).to.be.equal('keydown'); | ||||||
|  |                         break; | ||||||
|  |                     case 3: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x42, keysym: keysyms.lookup(0x62)}); | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x61)}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x62)}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x43, keysym: keysyms.lookup(0x63)}); | ||||||
|  |             obj({type: 'keyup', keyId: 0x42}); | ||||||
|  |             expect(times_called).to.equal(4); | ||||||
|  |         }); | ||||||
|  |         it('should pop the first zero keyevent on keyup with zero keyId', function() { | ||||||
|  |             var times_called = 0; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 switch (times_called++) { | ||||||
|  |                     case 0: | ||||||
|  |                     case 1: | ||||||
|  |                     case 2: | ||||||
|  |                         expect(evt.type).to.be.equal('keydown'); | ||||||
|  |                         break; | ||||||
|  |                     case 3: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x61)}); | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             obj({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x61)}); | ||||||
|  |             obj({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x62)}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x63)}); | ||||||
|  |             obj({type: 'keyup', keyId: 0x0}); | ||||||
|  |             expect(times_called).to.equal(4); | ||||||
|  |         }); | ||||||
|  |         it('should pop the last keyevents keysym if no match is found for keyId', function() { | ||||||
|  |             var times_called = 0; | ||||||
|  |             var obj = TrackKeyState(function(evt) { | ||||||
|  |                 switch (times_called++) { | ||||||
|  |                     case 0: | ||||||
|  |                     case 1: | ||||||
|  |                     case 2: | ||||||
|  |                         expect(evt.type).to.be.equal('keydown'); | ||||||
|  |                         break; | ||||||
|  |                     case 3: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x44, keysym: keysyms.lookup(0x63)}); | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x61)}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x62)}); | ||||||
|  |             obj({type: 'keydown', keyId: 0x43, keysym: keysyms.lookup(0x63)}); | ||||||
|  |             obj({type: 'keyup', keyId: 0x44}); | ||||||
|  |             expect(times_called).to.equal(4); | ||||||
|  |         }); | ||||||
|  |         describe('Firefox sends keypress even when keydown is suppressed', function() { | ||||||
|  |             it('should discard the keypress', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 var obj = TrackKeyState(function(evt) { | ||||||
|  |                     expect(times_called).to.be.equal(0); | ||||||
|  |                     ++times_called; | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |                 expect(times_called).to.be.equal(1); | ||||||
|  |                 obj({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x43)}); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('releaseAll', function() { | ||||||
|  |             it('should do nothing if no keys have been pressed', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 var obj = TrackKeyState(function(evt) { | ||||||
|  |                     ++times_called; | ||||||
|  |                 }); | ||||||
|  |                 obj({type: 'releaseall'}); | ||||||
|  |                 expect(times_called).to.be.equal(0); | ||||||
|  |             }); | ||||||
|  |             it('should release the keys that have been pressed', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 var obj = TrackKeyState(function(evt) { | ||||||
|  |                     switch (times_called++) { | ||||||
|  |                     case 2: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x41)}); | ||||||
|  |                         break; | ||||||
|  |                     case 3: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x42)}); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)}); | ||||||
|  |                 obj({type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x42)}); | ||||||
|  |                 expect(times_called).to.be.equal(2); | ||||||
|  |                 obj({type: 'releaseall'}); | ||||||
|  |                 expect(times_called).to.be.equal(4); | ||||||
|  |                 obj({type: 'releaseall'}); | ||||||
|  |                 expect(times_called).to.be.equal(4); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('Escape Modifiers', function() { | ||||||
|  |         describe('Keydown', function() { | ||||||
|  |             it('should pass through when a char modifier is not down', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 EscapeModifiers(function(evt) { | ||||||
|  |                     expect(times_called).to.be.equal(0); | ||||||
|  |                     ++times_called; | ||||||
|  |                     expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |                 })({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |                 expect(times_called).to.be.equal(1); | ||||||
|  |             }); | ||||||
|  |             it('should generate fake undo/redo events when a char modifier is down', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 EscapeModifiers(function(evt) { | ||||||
|  |                     switch(times_called++) { | ||||||
|  |                     case 0: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0xffe9)}); | ||||||
|  |                         break; | ||||||
|  |                     case 1: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0xffe3)}); | ||||||
|  |                         break; | ||||||
|  |                     case 2: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xffe9, 0xffe3]}); | ||||||
|  |                         break; | ||||||
|  |                     case 3: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0xffe9)}); | ||||||
|  |                         break; | ||||||
|  |                     case 4: | ||||||
|  |                         expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0xffe3)}); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 })({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xffe9, 0xffe3]}); | ||||||
|  |                 expect(times_called).to.be.equal(5); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |         describe('Keyup', function() { | ||||||
|  |             it('should pass through when a char modifier is down', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 EscapeModifiers(function(evt) { | ||||||
|  |                     expect(times_called).to.be.equal(0); | ||||||
|  |                     ++times_called; | ||||||
|  |                     expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xfe03]}); | ||||||
|  |                 })({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xfe03]}); | ||||||
|  |                 expect(times_called).to.be.equal(1); | ||||||
|  |             }); | ||||||
|  |             it('should pass through when a char modifier is not down', function() { | ||||||
|  |                 var times_called = 0; | ||||||
|  |                 EscapeModifiers(function(evt) { | ||||||
|  |                     expect(times_called).to.be.equal(0); | ||||||
|  |                     ++times_called; | ||||||
|  |                     expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |                 })({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42)}); | ||||||
|  |                 expect(times_called).to.be.equal(1); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
							
								
								
									
										1929
									
								
								public/novnc/tests/test.rfb.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										105
									
								
								public/novnc/tests/test.util.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,105 @@ | |||||||
|  | // requires local modules: util | ||||||
|  | /* jshint expr: true */ | ||||||
|  |  | ||||||
|  | var assert = chai.assert; | ||||||
|  | var expect = chai.expect; | ||||||
|  |  | ||||||
|  | describe('Utils', function() { | ||||||
|  |     "use strict"; | ||||||
|  |  | ||||||
|  |     describe('Array instance methods', function () { | ||||||
|  |         describe('push8', function () { | ||||||
|  |             it('should push a byte on to the array', function () { | ||||||
|  |                 var arr = [1]; | ||||||
|  |                 arr.push8(128); | ||||||
|  |                 expect(arr).to.deep.equal([1, 128]); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should only use the least significant byte of any number passed in', function () { | ||||||
|  |                 var arr = [1]; | ||||||
|  |                 arr.push8(0xABCD); | ||||||
|  |                 expect(arr).to.deep.equal([1, 0xCD]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('push16', function () { | ||||||
|  |             it('should push two bytes on to the array', function () { | ||||||
|  |                 var arr = [1]; | ||||||
|  |                 arr.push16(0xABCD); | ||||||
|  |                 expect(arr).to.deep.equal([1, 0xAB, 0xCD]); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should only use the two least significant bytes of any number passed in', function () { | ||||||
|  |                 var arr = [1]; | ||||||
|  |                 arr.push16(0xABCDEF); | ||||||
|  |                 expect(arr).to.deep.equal([1, 0xCD, 0xEF]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('push32', function () { | ||||||
|  |             it('should push four bytes on to the array', function () { | ||||||
|  |                 var arr = [1]; | ||||||
|  |                 arr.push32(0xABCDEF12); | ||||||
|  |                 expect(arr).to.deep.equal([1, 0xAB, 0xCD, 0xEF, 0x12]); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should only use the four least significant bytes of any number passed in', function () { | ||||||
|  |                 var arr = [1]; | ||||||
|  |                 arr.push32(0xABCDEF1234); | ||||||
|  |                 expect(arr).to.deep.equal([1, 0xCD, 0xEF, 0x12, 0x34]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('logging functions', function () { | ||||||
|  |         beforeEach(function () { | ||||||
|  |             sinon.spy(console, 'log'); | ||||||
|  |             sinon.spy(console, 'warn'); | ||||||
|  |             sinon.spy(console, 'error'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         afterEach(function () { | ||||||
|  |            console.log.restore(); | ||||||
|  |            console.warn.restore(); | ||||||
|  |            console.error.restore(); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should use noop for levels lower than the min level', function () { | ||||||
|  |             Util.init_logging('warn'); | ||||||
|  |             Util.Debug('hi'); | ||||||
|  |             Util.Info('hello'); | ||||||
|  |             expect(console.log).to.not.have.been.called; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should use console.log for Debug and Info', function () { | ||||||
|  |             Util.init_logging('debug'); | ||||||
|  |             Util.Debug('dbg'); | ||||||
|  |             Util.Info('inf'); | ||||||
|  |             expect(console.log).to.have.been.calledWith('dbg'); | ||||||
|  |             expect(console.log).to.have.been.calledWith('inf'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should use console.warn for Warn', function () { | ||||||
|  |             Util.init_logging('warn'); | ||||||
|  |             Util.Warn('wrn'); | ||||||
|  |             expect(console.warn).to.have.been.called; | ||||||
|  |             expect(console.warn).to.have.been.calledWith('wrn'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should use console.error for Error', function () { | ||||||
|  |             Util.init_logging('error'); | ||||||
|  |             Util.Error('err'); | ||||||
|  |             expect(console.error).to.have.been.called; | ||||||
|  |             expect(console.error).to.have.been.calledWith('err'); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     // TODO(directxman12): test the conf_default and conf_defaults methods | ||||||
|  |     // TODO(directxman12): test decodeUTF8 | ||||||
|  |     // TODO(directxman12): test the event methods (addEvent, removeEvent, stopEvent) | ||||||
|  |     // TODO(directxman12): figure out a good way to test getPosition and getEventPosition | ||||||
|  |     // TODO(directxman12): figure out how to test the browser detection functions properly | ||||||
|  |     //                     (we can't really test them against the browsers, except for Gecko | ||||||
|  |     //                     via PhantomJS, the default test driver) | ||||||
|  |     // TODO(directxman12): figure out how to test Util.Flash | ||||||
|  | }); | ||||||
							
								
								
									
										457
									
								
								public/novnc/tests/test.websock.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,457 @@ | |||||||
|  | // requires local modules: websock, util | ||||||
|  | // requires test modules: fake.websocket, assertions | ||||||
|  | /* jshint expr: true */ | ||||||
|  | var assert = chai.assert; | ||||||
|  | var expect = chai.expect; | ||||||
|  |  | ||||||
|  | describe('Websock', function() { | ||||||
|  |     "use strict"; | ||||||
|  |  | ||||||
|  |     describe('Queue methods', function () { | ||||||
|  |         var sock; | ||||||
|  |         var RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); | ||||||
|  |  | ||||||
|  |         beforeEach(function () { | ||||||
|  |             sock = new Websock(); | ||||||
|  |             // skip init | ||||||
|  |             sock._allocate_buffers(); | ||||||
|  |             sock._rQ.set(RQ_TEMPLATE); | ||||||
|  |             sock._rQlen = RQ_TEMPLATE.length; | ||||||
|  |         }); | ||||||
|  |         describe('rQlen', function () { | ||||||
|  |             it('should return the length of the receive queue', function () { | ||||||
|  |                sock.set_rQi(0); | ||||||
|  |  | ||||||
|  |                expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it("should return the proper length if we read some from the receive queue", function () { | ||||||
|  |                 sock.set_rQi(1); | ||||||
|  |  | ||||||
|  |                 expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length - 1); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('rQpeek8', function () { | ||||||
|  |             it('should peek at the next byte without poping it off the queue', function () { | ||||||
|  |                 var bef_len = sock.rQlen(); | ||||||
|  |                 var peek = sock.rQpeek8(); | ||||||
|  |                 expect(sock.rQpeek8()).to.equal(peek); | ||||||
|  |                 expect(sock.rQlen()).to.equal(bef_len); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('rQshift8', function () { | ||||||
|  |             it('should pop a single byte from the receive queue', function () { | ||||||
|  |                 var peek = sock.rQpeek8(); | ||||||
|  |                 var bef_len = sock.rQlen(); | ||||||
|  |                 expect(sock.rQshift8()).to.equal(peek); | ||||||
|  |                 expect(sock.rQlen()).to.equal(bef_len - 1); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('rQshift16', function () { | ||||||
|  |             it('should pop two bytes from the receive queue and return a single number', function () { | ||||||
|  |                 var bef_len = sock.rQlen(); | ||||||
|  |                 var expected = (RQ_TEMPLATE[0] << 8) + RQ_TEMPLATE[1]; | ||||||
|  |                 expect(sock.rQshift16()).to.equal(expected); | ||||||
|  |                 expect(sock.rQlen()).to.equal(bef_len - 2); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('rQshift32', function () { | ||||||
|  |             it('should pop four bytes from the receive queue and return a single number', function () { | ||||||
|  |                 var bef_len = sock.rQlen(); | ||||||
|  |                 var expected = (RQ_TEMPLATE[0] << 24) + | ||||||
|  |                                (RQ_TEMPLATE[1] << 16) + | ||||||
|  |                                (RQ_TEMPLATE[2] << 8) + | ||||||
|  |                                RQ_TEMPLATE[3]; | ||||||
|  |                 expect(sock.rQshift32()).to.equal(expected); | ||||||
|  |                 expect(sock.rQlen()).to.equal(bef_len - 4); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('rQshiftStr', function () { | ||||||
|  |             it('should shift the given number of bytes off of the receive queue and return a string', function () { | ||||||
|  |                 var bef_len = sock.rQlen(); | ||||||
|  |                 var bef_rQi = sock.get_rQi(); | ||||||
|  |                 var shifted = sock.rQshiftStr(3); | ||||||
|  |                 expect(shifted).to.be.a('string'); | ||||||
|  |                 expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3)))); | ||||||
|  |                 expect(sock.rQlen()).to.equal(bef_len - 3); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should shift the entire rest of the queue off if no length is given', function () { | ||||||
|  |                 sock.rQshiftStr(); | ||||||
|  |                 expect(sock.rQlen()).to.equal(0); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('rQshiftBytes', function () { | ||||||
|  |             it('should shift the given number of bytes of the receive queue and return an array', function () { | ||||||
|  |                 var bef_len = sock.rQlen(); | ||||||
|  |                 var bef_rQi = sock.get_rQi(); | ||||||
|  |                 var shifted = sock.rQshiftBytes(3); | ||||||
|  |                 expect(shifted).to.be.an.instanceof(Uint8Array); | ||||||
|  |                 expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3)); | ||||||
|  |                 expect(sock.rQlen()).to.equal(bef_len - 3); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should shift the entire rest of the queue off if no length is given', function () { | ||||||
|  |                 sock.rQshiftBytes(); | ||||||
|  |                 expect(sock.rQlen()).to.equal(0); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('rQslice', function () { | ||||||
|  |             beforeEach(function () { | ||||||
|  |                 sock.set_rQi(0); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should not modify the receive queue', function () { | ||||||
|  |                 var bef_len = sock.rQlen(); | ||||||
|  |                 sock.rQslice(0, 2); | ||||||
|  |                 expect(sock.rQlen()).to.equal(bef_len); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should return an array containing the given slice of the receive queue', function () { | ||||||
|  |                 var sl = sock.rQslice(0, 2); | ||||||
|  |                 expect(sl).to.be.an.instanceof(Uint8Array); | ||||||
|  |                 expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 0, 2)); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should use the rest of the receive queue if no end is given', function () { | ||||||
|  |                 var sl = sock.rQslice(1); | ||||||
|  |                 expect(sl).to.have.length(RQ_TEMPLATE.length - 1); | ||||||
|  |                 expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1)); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should take the current rQi in to account', function () { | ||||||
|  |                 sock.set_rQi(1); | ||||||
|  |                 expect(sock.rQslice(0, 2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2)); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('rQwait', function () { | ||||||
|  |             beforeEach(function () { | ||||||
|  |                 sock.set_rQi(0); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should return true if there are not enough bytes in the receive queue', function () { | ||||||
|  |                 expect(sock.rQwait('hi', RQ_TEMPLATE.length + 1)).to.be.true; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should return false if there are enough bytes in the receive queue', function () { | ||||||
|  |                 expect(sock.rQwait('hi', RQ_TEMPLATE.length)).to.be.false; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should return true and reduce rQi by "goback" if there are not enough bytes', function () { | ||||||
|  |                 sock.set_rQi(5); | ||||||
|  |                 expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true; | ||||||
|  |                 expect(sock.get_rQi()).to.equal(1); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should raise an error if we try to go back more than possible', function () { | ||||||
|  |                 sock.set_rQi(5); | ||||||
|  |                 expect(function () { sock.rQwait('hi', RQ_TEMPLATE.length, 6); }).to.throw(Error); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should not reduce rQi if there are enough bytes', function () { | ||||||
|  |                 sock.set_rQi(5); | ||||||
|  |                 sock.rQwait('hi', 1, 6); | ||||||
|  |                 expect(sock.get_rQi()).to.equal(5); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('flush', function () { | ||||||
|  |             beforeEach(function () { | ||||||
|  |                 sock._websocket = { | ||||||
|  |                     send: sinon.spy() | ||||||
|  |                 }; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should actually send on the websocket if the websocket does not have too much buffered', function () { | ||||||
|  |                 sock.maxBufferedAmount = 10; | ||||||
|  |                 sock._websocket.bufferedAmount = 8; | ||||||
|  |                 sock._websocket.readyState = WebSocket.OPEN | ||||||
|  |                 sock._sQ = new Uint8Array([1, 2, 3]); | ||||||
|  |                 sock._sQlen = 3; | ||||||
|  |                 var encoded = sock._encode_message(); | ||||||
|  |  | ||||||
|  |                 sock.flush(); | ||||||
|  |                 expect(sock._websocket.send).to.have.been.calledOnce; | ||||||
|  |                 expect(sock._websocket.send).to.have.been.calledWith(encoded); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should return true if the websocket did not have too much buffered', function () { | ||||||
|  |                 sock.maxBufferedAmount = 10; | ||||||
|  |                 sock._websocket.bufferedAmount = 8; | ||||||
|  |  | ||||||
|  |                 expect(sock.flush()).to.be.true; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should not call send if we do not have anything queued up', function () { | ||||||
|  |                 sock._sQlen = 0; | ||||||
|  |                 sock.maxBufferedAmount = 10; | ||||||
|  |                 sock._websocket.bufferedAmount = 8; | ||||||
|  |  | ||||||
|  |                 sock.flush(); | ||||||
|  |  | ||||||
|  |                 expect(sock._websocket.send).not.to.have.been.called; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should not send and return false if the websocket has too much buffered', function () { | ||||||
|  |                 sock.maxBufferedAmount = 10; | ||||||
|  |                 sock._websocket.bufferedAmount = 12; | ||||||
|  |  | ||||||
|  |                 expect(sock.flush()).to.be.false; | ||||||
|  |                 expect(sock._websocket.send).to.not.have.been.called; | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('send', function () { | ||||||
|  |             beforeEach(function () { | ||||||
|  |                 sock.flush = sinon.spy(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should add to the send queue', function () { | ||||||
|  |                 sock.send([1, 2, 3]); | ||||||
|  |                 var sq = sock.get_sQ(); | ||||||
|  |                 expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3])); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should call flush', function () { | ||||||
|  |                 sock.send([1, 2, 3]); | ||||||
|  |                 expect(sock.flush).to.have.been.calledOnce; | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('send_string', function () { | ||||||
|  |             beforeEach(function () { | ||||||
|  |                 sock.send = sinon.spy(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should call send after converting the string to an array', function () { | ||||||
|  |                 sock.send_string("\x01\x02\x03"); | ||||||
|  |                 expect(sock.send).to.have.been.calledWith([1, 2, 3]); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('lifecycle methods', function () { | ||||||
|  |         var old_WS; | ||||||
|  |         before(function () { | ||||||
|  |            old_WS = WebSocket; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         var sock; | ||||||
|  |         beforeEach(function () { | ||||||
|  |            sock = new Websock(); | ||||||
|  |            WebSocket = sinon.spy(); | ||||||
|  |            WebSocket.OPEN = old_WS.OPEN; | ||||||
|  |            WebSocket.CONNECTING = old_WS.CONNECTING; | ||||||
|  |            WebSocket.CLOSING = old_WS.CLOSING; | ||||||
|  |            WebSocket.CLOSED = old_WS.CLOSED; | ||||||
|  |  | ||||||
|  |            WebSocket.prototype.binaryType = 'arraybuffer'; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('opening', function () { | ||||||
|  |             it('should pick the correct protocols if none are given' , function () { | ||||||
|  |  | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should open the actual websocket', function () { | ||||||
|  |                 sock.open('ws://localhost:8675', 'binary'); | ||||||
|  |                 expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary'); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should fail if we specify a protocol besides binary', function () { | ||||||
|  |                 expect(function () { sock.open('ws:///', 'base64'); }).to.throw(Error); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             // it('should initialize the event handlers')? | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('closing', function () { | ||||||
|  |             beforeEach(function () { | ||||||
|  |                 sock.open('ws://'); | ||||||
|  |                 sock._websocket.close = sinon.spy(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should close the actual websocket if it is open', function () { | ||||||
|  |                 sock._websocket.readyState = WebSocket.OPEN; | ||||||
|  |                 sock.close(); | ||||||
|  |                 expect(sock._websocket.close).to.have.been.calledOnce; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should close the actual websocket if it is connecting', function () { | ||||||
|  |                 sock._websocket.readyState = WebSocket.CONNECTING; | ||||||
|  |                 sock.close(); | ||||||
|  |                 expect(sock._websocket.close).to.have.been.calledOnce; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should not try to close the actual websocket if closing', function () { | ||||||
|  |                 sock._websocket.readyState = WebSocket.CLOSING; | ||||||
|  |                 sock.close(); | ||||||
|  |                 expect(sock._websocket.close).not.to.have.been.called; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should not try to close the actual websocket if closed', function () { | ||||||
|  |                 sock._websocket.readyState = WebSocket.CLOSED; | ||||||
|  |                 sock.close(); | ||||||
|  |                 expect(sock._websocket.close).not.to.have.been.called; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should reset onmessage to not call _recv_message', function () { | ||||||
|  |                 sinon.spy(sock, '_recv_message'); | ||||||
|  |                 sock.close(); | ||||||
|  |                 sock._websocket.onmessage(null); | ||||||
|  |                 try { | ||||||
|  |                     expect(sock._recv_message).not.to.have.been.called; | ||||||
|  |                 } finally { | ||||||
|  |                     sock._recv_message.restore(); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         describe('event handlers', function () { | ||||||
|  |             beforeEach(function () { | ||||||
|  |                 sock._recv_message = sinon.spy(); | ||||||
|  |                 sock.on('open', sinon.spy()); | ||||||
|  |                 sock.on('close', sinon.spy()); | ||||||
|  |                 sock.on('error', sinon.spy()); | ||||||
|  |                 sock.open('ws://'); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should call _recv_message on a message', function () { | ||||||
|  |                 sock._websocket.onmessage(null); | ||||||
|  |                 expect(sock._recv_message).to.have.been.calledOnce; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should fail if a protocol besides binary is requested', function () { | ||||||
|  |                 sock._websocket.protocol = 'base64'; | ||||||
|  |                 expect(sock._websocket.onopen).to.throw(Error); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should assume binary if no protocol was available on opening', function () { | ||||||
|  |                 sock._websocket.protocol = null; | ||||||
|  |                 sock._websocket.onopen(); | ||||||
|  |                 expect(sock._mode).to.equal('binary'); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should call the open event handler on opening', function () { | ||||||
|  |                 sock._websocket.onopen(); | ||||||
|  |                 expect(sock._eventHandlers.open).to.have.been.calledOnce; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should call the close event handler on closing', function () { | ||||||
|  |                 sock._websocket.onclose(); | ||||||
|  |                 expect(sock._eventHandlers.close).to.have.been.calledOnce; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should call the error event handler on error', function () { | ||||||
|  |                 sock._websocket.onerror(); | ||||||
|  |                 expect(sock._eventHandlers.error).to.have.been.calledOnce; | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         after(function () { | ||||||
|  |             WebSocket = old_WS; | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('WebSocket Receiving', function () { | ||||||
|  |         var sock; | ||||||
|  |         beforeEach(function () { | ||||||
|  |            sock = new Websock(); | ||||||
|  |            sock._allocate_buffers(); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should support adding binary Uint8Array data to the receive queue', function () { | ||||||
|  |             var msg = { data: new Uint8Array([1, 2, 3]) }; | ||||||
|  |             sock._mode = 'binary'; | ||||||
|  |             sock._recv_message(msg); | ||||||
|  |             expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03'); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should call the message event handler if present', function () { | ||||||
|  |             sock._eventHandlers.message = sinon.spy(); | ||||||
|  |             var msg = { data: new Uint8Array([1, 2, 3]).buffer }; | ||||||
|  |             sock._mode = 'binary'; | ||||||
|  |             sock._recv_message(msg); | ||||||
|  |             expect(sock._eventHandlers.message).to.have.been.calledOnce; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should not call the message event handler if there is nothing in the receive queue', function () { | ||||||
|  |             sock._eventHandlers.message = sinon.spy(); | ||||||
|  |             var msg = { data: new Uint8Array([]).buffer }; | ||||||
|  |             sock._mode = 'binary'; | ||||||
|  |             sock._recv_message(msg); | ||||||
|  |             expect(sock._eventHandlers.message).not.to.have.been.called; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should compact the receive queue', function () { | ||||||
|  |             // NB(sross): while this is an internal implementation detail, it's important to | ||||||
|  |             //            test, otherwise the receive queue could become very large very quickly | ||||||
|  |             sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]); | ||||||
|  |             sock._rQlen = 6; | ||||||
|  |             sock.set_rQi(6); | ||||||
|  |             sock._rQmax = 3; | ||||||
|  |             var msg = { data: new Uint8Array([1, 2, 3]).buffer }; | ||||||
|  |             sock._mode = 'binary'; | ||||||
|  |             sock._recv_message(msg); | ||||||
|  |             expect(sock._rQlen).to.equal(3); | ||||||
|  |             expect(sock.get_rQi()).to.equal(0); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should automatically resize the receive queue if the incoming message is too large', function () { | ||||||
|  |             sock._rQ = new Uint8Array(20); | ||||||
|  |             sock._rQlen = 0; | ||||||
|  |             sock.set_rQi(0); | ||||||
|  |             sock._rQbufferSize = 20; | ||||||
|  |             sock._rQmax = 2; | ||||||
|  |             var msg = { data: new Uint8Array(30).buffer }; | ||||||
|  |             sock._mode = 'binary'; | ||||||
|  |             sock._recv_message(msg); | ||||||
|  |             expect(sock._rQlen).to.equal(30); | ||||||
|  |             expect(sock.get_rQi()).to.equal(0); | ||||||
|  |             expect(sock._rQ.length).to.equal(240);  // keep the invariant that rQbufferSize / 8 >= rQlen | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         it('should call the error event handler on an exception', function () { | ||||||
|  |             sock._eventHandlers.error = sinon.spy(); | ||||||
|  |             sock._eventHandlers.message = sinon.stub().throws(); | ||||||
|  |             var msg = { data: new Uint8Array([1, 2, 3]).buffer }; | ||||||
|  |             sock._mode = 'binary'; | ||||||
|  |             sock._recv_message(msg); | ||||||
|  |             expect(sock._eventHandlers.error).to.have.been.calledOnce; | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     describe('Data encoding', function () { | ||||||
|  |         before(function () { FakeWebSocket.replace(); }); | ||||||
|  |         after(function () { FakeWebSocket.restore(); }); | ||||||
|  |  | ||||||
|  |         describe('as binary data', function () { | ||||||
|  |             var sock; | ||||||
|  |             beforeEach(function () { | ||||||
|  |                 sock = new Websock(); | ||||||
|  |                 sock.open('ws://', 'binary'); | ||||||
|  |                 sock._websocket._open(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should only send the send queue up to the send queue length', function () { | ||||||
|  |                 sock._sQ = new Uint8Array([1, 2, 3, 4, 5]); | ||||||
|  |                 sock._sQlen = 3; | ||||||
|  |                 var res = sock._encode_message(); | ||||||
|  |                 expect(res).to.array.equal(new Uint8Array([1, 2, 3])); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             it('should properly pass the encoded data off to the actual WebSocket', function () { | ||||||
|  |                 sock.send([1, 2, 3]); | ||||||
|  |                 expect(sock._websocket._get_sent_data()).to.array.equal(new Uint8Array([1, 2, 3])); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
							
								
								
									
										43
									
								
								public/novnc/tests/viewport.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,43 @@ | |||||||
|  | html,body { | ||||||
|  |     margin: 0px; | ||||||
|  |     padding: 0px; | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .flex-layout { | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  |  | ||||||
|  |     display: box; | ||||||
|  |     display: -webkit-box; | ||||||
|  |     display: -moz-box; | ||||||
|  |     display: -ms-box; | ||||||
|  |  | ||||||
|  |     box-orient: vertical; | ||||||
|  |     -webkit-box-orient: vertical; | ||||||
|  |     -moz-box-orient: vertical; | ||||||
|  |     -ms-box-orient: vertical; | ||||||
|  |  | ||||||
|  |     box-align: stretch; | ||||||
|  |     -webkit-box-align: stretch; | ||||||
|  |     -moz-box-align: stretch; | ||||||
|  |     -ms-box-align: stretch; | ||||||
|  | } | ||||||
|  | .flex-box { | ||||||
|  |     box-flex: 1; | ||||||
|  |     -webkit-box-flex: 1; | ||||||
|  |     -moz-box-flex: 1; | ||||||
|  |     -ms-box-flex: 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .container { | ||||||
|  |     margin: 0px; | ||||||
|  |     padding: 0px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .canvas { | ||||||
|  |     position: absolute; | ||||||
|  |     border-style: dotted; | ||||||
|  |     border-width: 1px; | ||||||
|  | } | ||||||
							
								
								
									
										203
									
								
								public/novnc/tests/viewport.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,203 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |     <head><title>Viewport Test</title> | ||||||
|  |         <link rel="stylesheet" href="viewport.css"> | ||||||
|  |         <!-- | ||||||
|  |         <meta name="apple-mobile-web-app-capable" content="yes" /> | ||||||
|  |         <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> | ||||||
|  |         --> | ||||||
|  |         <meta name=viewport content="width=device-width, initial-scale=1.0, user-scalable=no"> | ||||||
|  |     </head> | ||||||
|  |     <body> | ||||||
|  |         <div class="flex-layout"> | ||||||
|  |             <div> | ||||||
|  |                 Canvas: | ||||||
|  |                     <input id="move-selector" type="button" value="Move" | ||||||
|  |                         onclick="toggleMove();"> | ||||||
|  |                 <br> | ||||||
|  |             </div> | ||||||
|  |             <div class="container flex-box"> | ||||||
|  |                 <canvas id="canvas" class="canvas"> | ||||||
|  |                     Canvas not supported. | ||||||
|  |                 </canvas> | ||||||
|  |                 <br> | ||||||
|  |             </div> | ||||||
|  |             <div> | ||||||
|  |                 <br> | ||||||
|  |                 Results:<br> | ||||||
|  |                 <textarea id="messages" style="font-size: 9;" cols=80 rows=8></textarea> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </body> | ||||||
|  |  | ||||||
|  |     <!-- | ||||||
|  |     <script type='text/javascript'  | ||||||
|  |         src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> | ||||||
|  |     --> | ||||||
|  |     <script src="../include/util.js"></script> | ||||||
|  |     <script src="../include/webutil.js"></script>  | ||||||
|  |     <script src="../include/base64.js"></script> | ||||||
|  |     <script src="../include/keysymdef.js"></script> | ||||||
|  |     <script src="../include/keyboard.js"></script> | ||||||
|  |     <script src="../include/input.js"></script>  | ||||||
|  |     <script src="../include/display.js"></script> | ||||||
|  |     <script> | ||||||
|  |         var msg_cnt = 0, iterations, | ||||||
|  |             penDown = false, doMove = false, | ||||||
|  |             inMove = false, lastPos = {}, | ||||||
|  |             padW = 0, padH = 0, | ||||||
|  |             display, ctx, keyboard, mouse; | ||||||
|  |  | ||||||
|  |         var newline = "\n"; | ||||||
|  |         if (Util.Engine.trident) { | ||||||
|  |             var newline = "<br>\n"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function message(str) { | ||||||
|  |             console.log(str); | ||||||
|  |             cell = $D('messages'); | ||||||
|  |             cell.textContent += msg_cnt + ": " + str + newline; | ||||||
|  |             cell.scrollTop = cell.scrollHeight; | ||||||
|  |             msg_cnt++; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function mouseButton(x, y, down, bmask) { | ||||||
|  |             //msg = 'mouse x,y: ' + x + ',' + y + '  down: ' + down; | ||||||
|  |             //msg += ' bmask: ' + bmask; | ||||||
|  |             //message(msg); | ||||||
|  |  | ||||||
|  |             if (doMove) { | ||||||
|  |                 if (down && !inMove) { | ||||||
|  |                     inMove = true; | ||||||
|  |                     lastPos = {'x': x, 'y': y}; | ||||||
|  |                 } else if (!down && inMove) { | ||||||
|  |                     inMove = false; | ||||||
|  |                     //dirtyRedraw(); | ||||||
|  |                 } | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (down && ! penDown) { | ||||||
|  |                 penDown = true; | ||||||
|  |                 ctx.beginPath(); | ||||||
|  |                 ctx.moveTo(x, y); | ||||||
|  |             } else if (!down && penDown) { | ||||||
|  |                 penDown = false; | ||||||
|  |                 ctx.closePath(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function mouseMove(x, y) { | ||||||
|  |             var deltaX, deltaY; | ||||||
|  |  | ||||||
|  |             if (inMove) { | ||||||
|  |                 //deltaX = x - lastPos.x; // drag viewport | ||||||
|  |                 deltaX = lastPos.x - x; // drag frame buffer | ||||||
|  |                 //deltaY = y - lastPos.y; // drag viewport | ||||||
|  |                 deltaY = lastPos.y - y; // drag frame buffer | ||||||
|  |                 lastPos = {'x': x, 'y': y}; | ||||||
|  |  | ||||||
|  |                 display.viewportChangePos(deltaX, deltaY); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (penDown) { | ||||||
|  |                 ctx.lineTo(x, y); | ||||||
|  |                 ctx.stroke(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function dirtyRedraw() { | ||||||
|  |             if (inMove) { | ||||||
|  |                 // Wait for user to stop moving viewport | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var d = display.getCleanDirtyReset(); | ||||||
|  |  | ||||||
|  |             for (i = 0; i < d.dirtyBoxes.length; i++) { | ||||||
|  |                 //showBox(d.dirtyBoxes[i], "dirty[" + i + "]: "); | ||||||
|  |                 drawArea(d.dirtyBoxes[i]); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function drawArea(b) { | ||||||
|  |             var data = [], pixel, x, y; | ||||||
|  |  | ||||||
|  |             //message("draw "+b.x+","+b.y+" ("+b.w+","+b.h+")"); | ||||||
|  |  | ||||||
|  |             for (var i = 0; i < b.w; i++) { | ||||||
|  |                 x = b.x + i; | ||||||
|  |                 for (var j = 0; j < b.h; j++) { | ||||||
|  |                     y = b.y + j; | ||||||
|  |                     pixel = (j * b.w * 4 + i * 4); | ||||||
|  |                     data[pixel + 0] = ((x * y) / 13) % 256; | ||||||
|  |                     data[pixel + 1] = ((x * y) + 392) % 256; | ||||||
|  |                     data[pixel + 2] = ((x + y) + 256) % 256; | ||||||
|  |                     data[pixel + 3] = 255; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             //message("i: " + i + ", j: " + j + ", pixel: " + pixel); | ||||||
|  |             display.blitImage(b.x, b.y, b.w, b.h, data, 0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function toggleMove() { | ||||||
|  |             if (doMove) { | ||||||
|  |                 doMove = false; | ||||||
|  |                 $D('move-selector').style.backgroundColor = ""; | ||||||
|  |                 $D('move-selector').style.color = ""; | ||||||
|  |             } else { | ||||||
|  |                 doMove = true; | ||||||
|  |                 $D('move-selector').style.backgroundColor = "black"; | ||||||
|  |                 $D('move-selector').style.color = "lightgray"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function detectPad() { | ||||||
|  |             var c = $D('canvas'), p = c.parentNode; | ||||||
|  |             c.width = 10; | ||||||
|  |             c.height = 10; | ||||||
|  |             padW = c.offsetWidth - 10; | ||||||
|  |             padH = c.offsetHeight - 10; | ||||||
|  |             message("padW: " + padW + ", padH: " + padH); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function doResize() { | ||||||
|  |             var p = $D('canvas').parentNode; | ||||||
|  |             message("doResize1: [" + (p.offsetWidth - padW) + | ||||||
|  |                     "," + (p.offsetHeight - padH) + "]"); | ||||||
|  |             display.viewportChangeSize(p.offsetWidth - padW, p.offsetHeight - padH); | ||||||
|  |             /* | ||||||
|  |             var pos, new_w, new_h;pos | ||||||
|  |             pos = Util.getPosition($D('canvas').parentNode); | ||||||
|  |             new_w = window.innerWidth - pos.x; | ||||||
|  |             new_h = window.innerHeight - pos.y; | ||||||
|  |             display.viewportChangeSize(new_w, new_h); | ||||||
|  |             */ | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         window.onload = function() { | ||||||
|  |             detectPad(); | ||||||
|  |  | ||||||
|  |             display = new Display({'target': $D('canvas')}); | ||||||
|  |             display.resize(1600, 1024); | ||||||
|  |             display.set_viewport(true); | ||||||
|  |             ctx = display.get_context(); | ||||||
|  |  | ||||||
|  |             mouse    = new Mouse({'target': $D('canvas'), | ||||||
|  |                                 'onMouseButton': mouseButton, | ||||||
|  |                                 'onMouseMove': mouseMove}); | ||||||
|  |             mouse.grab(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             Util.addEvent(window, 'resize', doResize); | ||||||
|  |             // Shrink viewport for first resize call so that the | ||||||
|  |             // scrollbars are disabled | ||||||
|  |             display.viewportChangeSize(10, 10); | ||||||
|  |             setTimeout(doResize, 1); | ||||||
|  |             setInterval(dirtyRedraw, 50); | ||||||
|  |  | ||||||
|  |             message("Display initialized"); | ||||||
|  |         }; | ||||||
|  |     </script> | ||||||
|  | </html> | ||||||
 User Web
					User Web