Web版Telnet工具设计与实现

C/S架构Telnet工具实现原理

C/S架构的Telnet工具实现的主要思想是:界面组件捕获键盘事件,经过过滤、转换将消息发送到服务端,服务通过Telnet第三方开发包,将消息发送给Telnet服务端(多是支持Telnet的设备),然后将设备返回的信息返回到客户端,客户端进行处理后显示在界面。

Termlib组件

同样在B/S中要实现Telnet/SSH功能,也需要实现类似C/S界面组件功能,这个组件不仅要过滤输入字符,而且要根据Telnet协议要求实现了键盘输入字符和发送字符命令之间的转换以及设备返回内容的处理(格式处理,字符替换)

通过相关资料的搜索,发现网上有一个叫termlibjavascript组件(http://www.masswerk.at/termlib),这个js文件就是用javascript实现了一个命令行终端组件,虽然代码仅仅几千行,但是几乎实现了命令行终端的所有功能,提供了相关的控制API。因此可以使用该组件来实现Web版的Telnet工具

Termlib可以使用在命令终端、小游戏界面等相关的应用场景,其中著名的PC机模拟项目——Javascript实现(http://www.bellard.org/jslinux/)模拟PC,界面的实现过程也参考了termlibtermlib的最简单demo可以看termlib网站的demo和帮助。

       Termlib使用分几种模式:默认模式、字符模式、Raw模式、文件模式。默认的是命令模式,是以回车键标志命令事件,捕获事件后回调使用者handler。其构造函数可以定制term相关的参数。

       分析网站中的demo发现,使用默认模式来实现Web版本的Telnet是最好的办法。但是还需要解决如下几个关键问题:

1  C/STelnent工具实现来看,我们必须捕获每个键盘事件,并且发命令给设备,即发送命令不是以回车键为标识。

2  即使捕获键盘事件,但是每个键盘(尤其是控制键)点击时间要发送什么样的命令需要我们特殊实现,termlib不提供这种处理,因为termlib仅仅做界面处理,并不是针对Telnet来实现的。

3  对于设备返回的信息需要进行处理后再让termlib显示。

4  由于Telnet连接过程中有可能超时和异常,因此Web服务端需要向浏览器push这些信息。

5 Telnet连接需要进行额外的管理。

B/S架构Telnet工具实现思路

1  如何捕获原始的键盘敲击事件?

通过覆盖prototype方法keyHandler,将我们的事件处理替换原有的keyHandler事件处理,从而捕获原始的键盘点击事件:

function customTerm(term){

   Terminal.prototype.globals.keyHandler=function(e) {

              。。。。。。

   };

}

2  如何处理键盘事件?

通过分析Telnet的协议处理,将键盘事件归类如下

键盘

给设备的命令字节

回显处理

Entry

键盘Code13

需要处理“删除”字符

ESC

键盘Code

直接关闭窗口

LEFT

27,91,68

根据telnet协议特殊处理

RIGHT

27,91,67

根据telnet协议特殊处理

UP

27,91,65

根据telnet协议特殊处理

DOWN

27,91,66

根据telnet协议特殊处理

BS

8

根据telnet协议特殊处理

DEL

127

根据telnet协议特殊处理

TAB

键盘Code

根据telnet协议特殊处理

空格

键盘Code

需要处理“删除”字符

可打印字符

字符Code

需要处理“删除”字符

Ctrl+Z

字符Code26

根据telnet协议特殊处理

Ctrl+V

字符Code22

根据telnet协议特殊处理

其他特殊字符

不发送返回

不支持

3  对于设备返回的信息如何处理?

将命令分类,分析规律,根据需要处理回显字符,然后再根据需要现实,回显处理可以参考上表,具体还需要在实现的时候进一步分析

4  服务端的信息的push方法? jquery + cometd

5  如何进行telnet链接管理?

根据http sessionTelnet服务端(设备)关键字,在业务层提供统一的Telnent连接管理,实现过程要考虑并发性问题。

B/S架构Telnet工具实现Demo

这个demo中实现了webtelnet工具的基本功能,很多特殊处理还需要在实际实现中完善,界面组织、用户接入等等问题也都需要在实际问题中解决。

1、演示效果如下:

2demo中的web层代码如下:

 html层

<html>
<head>
<title>termlib Socket Sample</title>
<script language="JavaScript" type="text/javascript" src="termlib.js"></script>
<script language="JavaScript" type="text/javascript" src="easycrthelper.js"></script>
<script language="JavaScript" type="text/javascript" src="easycrt.js"></script>
<style type="text/css">
body,p,a,td,li {
font-family: courier,fixed,swiss,sans-serif;
font-size: 12px;
color: #cccccc;
}
.lh15 {
line-height: 15px;
}
.term {
font-family: "Courier New",courier,fixed,monospace;
font-size: 12px;
color: #94aad6;
background: none;
letter-spacing: 1px;
}
.term .termReverse {
color: #232e45;
background: #95a9d5;
}
a,a:link,a:visited {
text-decoration: none;
color: #77dd11;
}
a:hover {
text-decoration: underline;
color: #77dd11;
}
a:active {
text-decoration: underline;
color: #eeeeee;
}
a.termopen,a.termopen:link,a.termopen:visited {
text-decoration: none;
color: #77dd11;
background: none;
}
a.termopen:hover {
text-decoration: none;
color: #222222;
background: #77dd11;
}
a.termopen:active {
text-decoration: none;
color: #222222;
background: #eeeeee;
}
table.inventory td {
padding-bottom: 20px !important;
}
tt,pre {
font-family: courier,fixed,monospace;
color: #ccffaa;
font-size: 12px;
line-height: 15px;
}
li {
line-height: 15px;
margin-bottom: 8px !important;
}
.dimmed,.dimmed *,.dimmed * * {
background-color: #222222 !important;
color: #333333 !important;
}
@media print {
body { background-color: #ffffff; }
body,p,a,td,li,tt {
color: #000000;
}
pre,.prop {
color: #000000;
}
h1 {
color: #000000;
}
a,a:link,a:visited {
color: #000000;
}
a:hover {
color: #000000;
}
a:active {
color: #000000;
}
table.inventory {
display: none;
}
}
</style>
</head>
<body bgcolor="#222222" link="#77dd11" text="#cccccc" alink="#eeeeee" vlink="#77dd11" topmargin="0" bottommargin="0" leftmargin="0" rightmargin="0" marginheight="0" marginwidth="0" οnlοad=termOpen()>

<div id="termDiv" style="position:absolute; visibility: hidden; z-index:1;"></div>
</body>
</html>

js 层

var term;
var help = [];
function termOpen()
{
  if ((!term) || (term.closed))
  {
    term = new Terminal(
    {
	  cols:100,
	  rows:36,
      x: 220,
      y: 70,
	  blinkDelay:1000,
	  crsrBlinkMode:true,
	  ps:'',
      termDiv: 'termDiv',
      bgColor: '#232e45',
      greeting: help.join('\n'),
      handler: termHandler,
      exitHandler: myExitHandler
    }
    );
	customTerm(term);
    term.open();
	term.currentEvent=KeyEventDetail.prototype.getNomalDetail(10000);
	term.handler();
  }
}

function customTerm(term){
   Terminal.prototype.globals.keyHandler=function(e) {
		var tg=Terminal.prototype.globals;
		var term=tg.activeTerm;
		if (tg.keylock || term.lock || term.isMac && e && e.metaKey) return true;
		if (window.event) {
			if (window.event.preventDefault) window.event.preventDefault();
			if (window.event.stopPropagation) window.event.stopPropagation();
		}
		else if (e) {
			if (e.preventDefault) e.preventDefault();
			if (e.stopPropagation) e.stopPropagation();
		}
		var ch;
		var ctrl=false;
		var shft=false;
		var remapped=false;
		var termKey=term.termKey;
		var keyRepeat=0;
		if (e) {
			ch=e.which;
			ctrl=((e.ctrlKey && !e.altKey) || e.modifiers==2);
			shft=(e.shiftKey || e.modifiers==4);
			if (e._remapped) {
				remapped=true;
				if (window.event) {
					//ctrl=(ctrl || window.event.ctrlKey);
					ctrl=(ctrl || (window.event.ctrlKey && !window.event.altKey));
					shft=(shft || window.event.shiftKey);
				}
			}
			if (e._repeated) {
				keyRepeat=2;
			}
			else if (e._repeat) {
				keyRepeat=1;
			}
		}
		else if (window.event) {
			ch=window.event.keyCode;
			//ctrl=(window.event.ctrlKey);
			ctrl=(window.event.ctrlKey && !window.event.altKey); // allow alt gr == ctrl alt
			shft=(window.event.shiftKey);
			if (window.event._repeated) {
				keyRepeat=2;
			}
			else if (window.event._repeat) {
				keyRepeat=1;
			}
		}
		else {
			return true;
		}
		if (ch=='' && remapped==false) {
			// map specials
			if (e==null) e=window.event;
			if (e.charCode==0 && e.keyCode) {
				if (e.DOM_VK_UP) {
					var dkr=tg.termDomKeyRef;
					for (var i in dkr) {
						if (e[i] && e.keyCode == e[i]) {
							ch=dkr[i];
							break;
						}
					}
				}
				else {
					// NS4
					if (e.keyCode==28) { ch=termKey.LEFT; }
					else if (e.keyCode==29) { ch=termKey.RIGHT; }
					else if (e.keyCode==30) { ch=termKey.UP; }
					else if (e.keyCode==31) { ch=termKey.DOWN; }
					// Mozilla alike but no DOM support
					else if (e.keyCode==37) { ch=termKey.LEFT; }
					else if (e.keyCode==39) { ch=termKey.RIGHT; }
					else if (e.keyCode==38) { ch=termKey.UP; }
					else if (e.keyCode==40) { ch=termKey.DOWN; }
					// just to have the TAB mapping here too
					else if (e.keyCode==9) { ch=termKey.TAB; }
				}
			}
		}
		// leave on unicode private use area (might be function key etc)
		if ((ch>=0xE000) && (ch<= 0xF8FF)) return;
		if (keyRepeat) {
			tg.clearRepeatTimer();
			tg.keyRepeatTimer = window.setTimeout(
				'Terminal.prototype.globals.doKeyRepeat('+ch+')',
				(keyRepeat==1)? tg.keyRepeatDelay1:tg.keyRepeatDelay2
			);
		}
		if (!ctrl) {
			// special keys
			if (ch==termKey.CR) {//entry*********************************************************************************************************************
				term.lock=true;
				term.cursorOff();
				term.insert=false;
				term.lineBuffer=term._getLine(true);
				if (
					term.lineBuffer!='' &&
					(!term.historyUnique || term.history.length==0 ||
					term.lineBuffer!=term.history[term.history.length-1])
					) {
					term.history[term.history.length]=term.lineBuffer;
				}
				term.histPtr=term.history.length;
				term.lastLine='';
				term.inputChar=0;
				term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(ch);
				term.handler();
				term.lock=false;
				term.cursorOn();
				term.insert=true;
				if (window.event) window.event.cancelBubble=true;
				return false;
			}
			else if (ch==termKey.ESC && term.conf.closeOnESC) {//ESC********************************************************************************
				term.close();
				if (window.event) window.event.cancelBubble=true;
				return false;
			}

			{
				if (ch==termKey.LEFT) {//LEFT*****************************************************************************************************************
					//term.cursorLeft();
					term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(ch);
					term.handler();
					//no need  if add follow ,curcor move becaome slow
					//term.lock=false;
					//term.cursorOn();
					//term.insert=true;
					if (window.event) window.event.cancelBubble=true;
					return false;
				}
				else if (ch==termKey.RIGHT) {
					//term.cursorRight();
					term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(ch);
					term.handler();
					if (window.event) window.event.cancelBubble=true;
					return false;
				}
				else if (ch==termKey.UP) {
					term.cursorOff();
					/**
					if (term.histPtr==term.history.length) term.lastLine=term._getLine();
					term._clearLine();
					if (term.history.length && term.histPtr>=0) {
						if (term.histPtr>0) term.histPtr--;
						term.type(term.history[term.histPtr]);
					}
					else if (term.lastLine) {
						term.type(term.lastLine);
					}
					*/
					term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(ch);
					term.handler();
					term.cursorOn();
					if (window.event) window.event.cancelBubble=true;
					return false;
				}
				else if (ch==termKey.DOWN) {
					term.cursorOff();
					/**
					if (term.histPtr==term.history.length) term.lastLine=term._getLine();
					term._clearLine();
					if (term.history.length && term.histPtr<=term.history.length) {
						if (term.histPtr<term.history.length) term.histPtr++;
						if (term.histPtr<term.history.length) {
							term.type(term.history[term.histPtr]);
						}
						else if (term.lastLine) {
							term.type(term.lastLine);
						}
					}
					else if (term.lastLine) {
						term.type(term.lastLine);
					}
					*/
					term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(ch);
					term.handler();
					term.cursorOn();
					if (window.event) window.event.cancelBubble=true;
					return false;
				}
				else if (ch==termKey.BS) {
					//term.backspace();
					term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(ch);
					term.handler();
					if (window.event) window.event.cancelBubble=true;
					return false;
				}
				else if (ch==termKey.DEL) {
					/**
					if (term.DELisBS) {
						term.backspace();
					}
					else {
						term.fwdDelete();
					}
					*/
					term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(ch);
					term.handler();
					if (window.event) window.event.cancelBubble=true;
					return false;
				}
			}
		}
		
		{
			if (term.conf.catchCtrlH && (ch==termKey.BS || (ctrl && ch==72))) {
				// catch ^H
				//term.backspace();
				term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(0);
				term.handler();
				if (window.event) window.event.cancelBubble=true;
				return false;
			}
			else if (term.ctrlHandler && (ch<32 || (ctrl && term.isPrintable(ch,true)))) {//Ctrl unprintable key **************************************************
				if ((ch>=65 && ch<=96) || ch==63) {
					// remap canonical
					if (ch==63) {
						ch=31;
					}
					else {
						ch-=64;
					}
				}
				term.inputChar=ch;
				//term.ctrlHandler();
				term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(0);
				term.handler();
				if (window.event) window.event.cancelBubble=true;
				return false;
			}
			else if (ctrl || !term.isPrintable(ch,true)) {//Ctrl printable key ************************************************************************************************
				if(ctrl && ch==122||ctrl && ch==90){//Ctrl z
					term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(26);
				}else if(ctrl && ch==118||ctrl && ch==86){//Ctrl v
					term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(22);
				}else{
					term.currentEvent=KeyEventDetail.prototype.getSpeciclDetail(0);
				}
				term.handler();
				if (window.event) window.event.cancelBubble=true;
				return false;
			}
			else if (term.isPrintable(ch,true)) {//printable key*****************************************************************************************************************
				if (term.blinkTimer) clearTimeout(term.blinkTimer);
				/**
				if (term.insert) {
					term.cursorOff();
					term._scrollRight(term.r,term.c);
				}
				term._charOut(ch);
				term.cursorOn();
				*/
				if (ch==32 && window.event) {
					window.event.cancelBubble=true;
				}
				else if (window.opera && window.event) {
					window.event.cancelBubble=true;
				}
				term.currentEvent=KeyEventDetail.prototype.getNomalDetail(ch);
				term.handler();
				term.lock=false;
				term.cursorOn();
				term.insert=true;
				return false;
			}
		}
		return true;
   };
}

function termHandler() {
		var keyEvent=this.currentEvent;
		var termpCharCode=0;
		var sendStr="0";
		if(keyEvent){
			termpCharCode=keyEvent.inputchar;
			sendStr=String(termpCharCode);
		}else{
			return;
		}
		if(keyEvent.isSpecial){
			if(keyEvent.isSupport){
				sendStr=keyEvent.getSendCmd();
			}else{
				return ;
			}
		}
		var myDataObject = {
              charCode: sendStr,
              ipaddress: "10.46.60.69"
        };
	    this.send(
        {
          url: "demoApp/easyCRT.action",
          method: 'get',
          callback: myServerCallback,
		  data: myDataObject
        }
     );
}


function myServerCallback()
{
  var response=this.socket;
  if (response.success)
  {
    var func=null;
    try{
      func=eval(response.responseText);
    }catch (e){
    }
    if (typeof func=='function'){
      try{
        func.apply(this);
      }catch(e){
        this.write('An error occured within the imported function: '+e);
      }
    }else{
	  var respText=response.responseText;
	  var temptg=Terminal.prototype.globals;
	  var tempterm=temptg.activeTerm;
	  var keyEvent=tempterm.currentEvent;
	  if(keyEvent.isSpecial&&keyEvent.isSupport){
			var flag=keyEvent.inputchar;
			switch (flag){
				case Terminal.prototype.globals.termKey.CR:
					break;
				case Terminal.prototype.globals.termKey.LEFT:
					leftkey(tempterm,respText);
					return;
					break;
				case Terminal.prototype.globals.termKey.RIGHT:
					rightkey(tempterm,respText);
					return;
					break;
				case Terminal.prototype.globals.termKey.UP:
					break;
				case Terminal.prototype.globals.termKey.DOWN:
					break;
				case Terminal.prototype.globals.termKey.BS:
					break;
				case KeyEventDetail.prototype.specialKeysSupported.CTRLZ:
					break;
				case KeyEventDetail.prototype.specialKeysSupported.CTRLV:
					break;
				default:
					;
			}
	 }
	  respText=keyEvent.preResponText(respText,tempterm);
	  var text=HelperUtil.prototype.removeCharbyDelIndex(respText);
	  this.write(respText);
    }
  }
  else
  {
    var s='Request failed: ' + response.status + ' ' + response.statusText;
    if (response.errno) s += '\n' + response.errstring;
    this.write(s);
  }
}

function leftkey(tempterm,respText){
	if(respText){
		tempterm.cursorLeft();
	}
}
function rightkey(tempterm,respText){
	if(respText){
		tempterm.cursorRight();
	}
}


function myExitHandler()
{
  var mainPane = (document.getElementById)?
  document.getElementById('mainPane') : document.all.mainPane;
  if (mainPane) mainPane.className = 'lh15';
}
function KeyEventDetail(){
    this.inputchar='';
	this.isSpecial=false;
	this.isSupport=true;
	this.isControl=false;
	this.isShift=false;
	this.getSendCmd=function(){
		var sendStrTemp=String(this.inputchar);
		if(this.isSpecial){
			var flag=this.inputchar;
			switch (flag){
				case Terminal.prototype.globals.termKey.CR:
					sendStrTemp=String(this.inputchar);
					break;
				case Terminal.prototype.globals.termKey.LEFT:
					sendStrTemp="27,91,68";
					break;
				case Terminal.prototype.globals.termKey.RIGHT:
					sendStrTemp="27,91,67";
					break;
				case Terminal.prototype.globals.termKey.UP:
					sendStrTemp="27,91,65";
					break;
				case Terminal.prototype.globals.termKey.DOWN:
					sendStrTemp="27,91,66";
					break;
				case Terminal.prototype.globals.termKey.BS:
					sendStrTemp="8";
					break;
				case Terminal.prototype.globals.termKey.DEL:
					sendStrTemp="127";
					break;
				case KeyEventDetail.prototype.specialKeysSupported.CTRLZ:
					sendStrTemp="26";
					break;
				case KeyEventDetail.prototype.specialKeysSupported.CTRLV:
					sendStrTemp="22";
					break;
				default:
					;
			}
		}
		return sendStrTemp;
	};
	this.preResponText=function(respText,term){
		if(this.isSpecial&&this.isSupport){
			var flag=this.inputchar;
			switch (flag){
				case Terminal.prototype.globals.termKey.CR:
					break;
				case Terminal.prototype.globals.termKey.LEFT:
					break;
				case Terminal.prototype.globals.termKey.RIGHT:
					break;
				case Terminal.prototype.globals.termKey.UP:
					break;
				case Terminal.prototype.globals.termKey.DOWN:
					break;
				case Terminal.prototype.globals.termKey.BS:
					break;
				case Terminal.prototype.globals.termKey.DEL:
					break;
				case KeyEventDetail.prototype.specialKeysSupported.CTRLZ:
					break;
				case KeyEventDetail.prototype.specialKeysSupported.CTRLV:
					break;
				default:
					;
			}
		}
		return respText;
	};
}

KeyEventDetail.prototype={

    specialKeysSupported:{
		'CR': Terminal.prototype.globals.termKey.CR,
		'LEFT': Terminal.prototype.globals.termKey.LEFT,
		'RIGHT': Terminal.prototype.globals.termKey.RIGHT,
		'UP': Terminal.prototype.globals.termKey.UP,
		'DOWN': Terminal.prototype.globals.termKey.DOWN,
		'BS': Terminal.prototype.globals.termKey.BS,
		'DEL': Terminal.prototype.globals.termKey.DEL,
		'CTRLZ': 26,
		'CTRLV': 22
	},
	getNomalDetail:function(charValue){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=charValue;
		return currentEvent;
	},
	getSpeciclDetail:function(charValue){
	    var detail= KeyEventDetail.prototype.getDefaultSpecialDetail(charValue);
		switch (charValue){
				case Terminal.prototype.globals.termKey.CR:
					detail=KeyEventDetail.prototype.getEnterDetail();
					break;
				case Terminal.prototype.globals.termKey.LEFT:
					detail=KeyEventDetail.prototype.getLeftDetail();
					break;
				case Terminal.prototype.globals.termKey.RIGHT:
					detail=KeyEventDetail.prototype.getRightDetail();
					break;
				case Terminal.prototype.globals.termKey.UP:
					detail=KeyEventDetail.prototype.getUpDetail();
					break;
				case Terminal.prototype.globals.termKey.DOWN:
					detail=KeyEventDetail.prototype.getDownDetail();
					break;
				case Terminal.prototype.globals.termKey.BS:
					detail=KeyEventDetail.prototype.getBsDetail();
					break;
				case Terminal.prototype.globals.termKey.DEL:
					detail=KeyEventDetail.prototype.getDelDetail();
					break;
				case KeyEventDetail.prototype.specialKeysSupported.CTRLZ:
					detail=KeyEventDetail.prototype.getCtrlzDetail();
					break;
				case KeyEventDetail.prototype.specialKeysSupported.CTRLV:
					detail=KeyEventDetail.prototype.getCtrlvDetail();
					break;
				default:
					;
		}
		return detail;
	},
	getDefaultSpecialDetail:function(chatValue){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=chatValue;
		currentEvent.isSpecial=true;
		currentEvent.isSupport=false;
		return currentEvent;
	},
	getEnterDetail:function(){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=Terminal.prototype.globals.termKey.CR;
		currentEvent.isSpecial=true;
		return currentEvent;
	},
	getLeftDetail:function(){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=Terminal.prototype.globals.termKey.LEFT;
		currentEvent.isSpecial=true;
		return currentEvent;
	},
	getRightDetail:function(){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=Terminal.prototype.globals.termKey.RIGHT;
		currentEvent.isSpecial=true;
		return currentEvent;
	},
	getUpDetail:function(){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=Terminal.prototype.globals.termKey.UP;
		currentEvent.isSpecial=true;
		return currentEvent;
	},
	getDownDetail:function(){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=Terminal.prototype.globals.termKey.DOWN;
		currentEvent.isSpecial=true;
		return currentEvent;
	},
	getBsDetail:function(){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=Terminal.prototype.globals.termKey.BS;
		currentEvent.isSpecial=true;
		return currentEvent;
	},
	getDelDetail:function(){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=Terminal.prototype.globals.termKey.DEL;
		currentEvent.isSpecial=true;
		return currentEvent;
	},
	getCtrlzDetail:function(){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=KeyEventDetail.prototype.specialKeysSupported.CTRLZ;
		currentEvent.isSpecial=true;
		return currentEvent;
	},
	getCtrlvDetail:function(){
		var currentEvent=new KeyEventDetail();
		currentEvent.inputchar=KeyEventDetail.prototype.specialKeysSupported.CTRLZ;
		currentEvent.isSpecial=true;
		return currentEvent;
	}

	
}

function HelperUtil(){
}
HelperUtil.prototype={

	removeCharbyDelIndex:function(text){
	         return text;
	}
	
}

3demo中的服务端代码:略

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值