├── .gitignore ├── LICENSE ├── README.asc ├── pom.xml ├── src └── main │ ├── java │ └── com │ │ └── kodcu │ │ ├── ApplicationStarter.java │ │ ├── ServletInitializer.java │ │ └── ShellSocket.java │ └── webapp │ ├── index.html │ ├── jquery.min.js │ ├── jquery.terminal │ ├── css │ │ └── jquery.terminal.css │ └── js │ │ ├── dterm.js │ │ ├── jquery-1.7.1.min.js │ │ ├── jquery.mousewheel-min.js │ │ ├── jquery.terminal-0.8.8.js │ │ ├── jquery.terminal-0.8.8.min.js │ │ ├── jquery.terminal-min.js │ │ └── jquery.terminal-src.js │ └── sockjs.js └── ssh-on-web.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | *.class 4 | 5 | # Mobile Tools for Java (J2ME) 6 | .mtj.tmp/ 7 | 8 | # Package Files # 9 | *.jar 10 | *.war 11 | *.ear 12 | 13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 14 | hs_err_pid* 15 | 16 | 17 | ### JetBrains template 18 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 19 | 20 | *.iml 21 | 22 | ## Directory-based project format: 23 | .idea/ 24 | # if you remove the above rule, at least ignore the following: 25 | 26 | # User-specific stuff: 27 | # .idea/workspace.xml 28 | # .idea/tasks.xml 29 | # .idea/dictionaries 30 | 31 | # Sensitive or high-churn files: 32 | # .idea/dataSources.ids 33 | # .idea/dataSources.xml 34 | # .idea/sqlDataSources.xml 35 | # .idea/dynamic.xml 36 | # .idea/uiDesigner.xml 37 | 38 | # Gradle: 39 | # .idea/gradle.xml 40 | # .idea/libraries 41 | 42 | # Mongo Explorer plugin: 43 | # .idea/mongoSettings.xml 44 | 45 | ## File-based project format: 46 | *.ipr 47 | *.iws 48 | 49 | ## Plugin-specific files: 50 | 51 | # IntelliJ 52 | out/ 53 | 54 | # mpeltonen/sbt-idea plugin 55 | .idea_modules/ 56 | 57 | # JIRA plugin 58 | atlassian-ide-plugin.xml 59 | 60 | # Crashlytics plugin (for Android Studio and IntelliJ) 61 | com_crashlytics_export_strings.xml 62 | crashlytics.properties 63 | crashlytics-build.properties 64 | 65 | 66 | ### Maven template 67 | target/ 68 | pom.xml.tag 69 | pom.xml.releaseBackup 70 | pom.xml.versionsBackup 71 | pom.xml.next 72 | release.properties 73 | 74 | 75 | 76 | 77 | hopbiriki.p12 78 | src/main/resources/application.properties -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Rahman Usta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.asc: -------------------------------------------------------------------------------- 1 | = SSH Web Client written in Java 2 | 3 | Experimental SSH Web Client written in Java. 4 | 5 | image::ssh-on-web.png[] 6 | 7 | == Usage 8 | 9 | [source,bash] 10 | ---- 11 | $ mvn clean install 12 | $ mvn spring-boot:run 13 | ---- 14 | 15 | == Run 16 | 17 | http://localhost:8080 18 | 19 | == Licence 20 | 21 | MIT -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | war 7 | 8 | 9 | org.springframework.boot 10 | spring-boot-starter-parent 11 | 1.2.0.RELEASE 12 | 13 | 14 | 15 | 16 | ssh-on-web 17 | ssh-on-web 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 1.8 22 | 1.8 23 | UTF-8 24 | 25 | 26 | 27 | 28 | com.hierynomus 29 | sshj 30 | 0.11.0 31 | 32 | 33 | net.sf.expectit 34 | expectit-core 35 | 0.6.1 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-web 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-websocket 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-tomcat 48 | 49 | 50 | 51 | javax.el 52 | javax.el-api 53 | 2.2.4 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/main/java/com/kodcu/ApplicationStarter.java: -------------------------------------------------------------------------------- 1 | package com.kodcu; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.web.socket.WebSocketHandler; 7 | import org.springframework.web.socket.config.annotation.EnableWebSocket; 8 | import org.springframework.web.socket.config.annotation.WebSocketConfigurer; 9 | import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; 10 | import org.springframework.web.socket.handler.PerConnectionWebSocketHandler; 11 | 12 | @SpringBootApplication 13 | @EnableWebSocket 14 | public class ApplicationStarter implements WebSocketConfigurer { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(ApplicationStarter.class, args); 18 | } 19 | 20 | @Override 21 | public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { 22 | registry.addHandler(myHandler(), "/shell").withSockJS(); 23 | } 24 | 25 | @Bean 26 | public WebSocketHandler myHandler() { 27 | return new PerConnectionWebSocketHandler(ShellSocket.class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/kodcu/ServletInitializer.java: -------------------------------------------------------------------------------- 1 | package com.kodcu; 2 | 3 | import org.springframework.boot.builder.SpringApplicationBuilder; 4 | import org.springframework.boot.context.web.SpringBootServletInitializer; 5 | 6 | public class ServletInitializer extends SpringBootServletInitializer { 7 | 8 | @Override 9 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 10 | return application.sources(ApplicationStarter.class); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/kodcu/ShellSocket.java: -------------------------------------------------------------------------------- 1 | package com.kodcu; 2 | 3 | import net.schmizz.sshj.SSHClient; 4 | import net.schmizz.sshj.connection.channel.direct.Session; 5 | import net.sf.expectit.Expect; 6 | import net.sf.expectit.ExpectBuilder; 7 | import net.sf.expectit.Result; 8 | import net.sf.expectit.matcher.Matchers; 9 | import org.springframework.web.socket.CloseStatus; 10 | import org.springframework.web.socket.TextMessage; 11 | import org.springframework.web.socket.WebSocketSession; 12 | import org.springframework.web.socket.handler.TextWebSocketHandler; 13 | 14 | import java.io.IOException; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Executors; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | /** 20 | * Created by usta on 20.02.2015. 21 | */ 22 | public class ShellSocket extends TextWebSocketHandler { 23 | 24 | private final SSHClient ssh = new SSHClient(); 25 | private static final ExecutorService executorService = Executors.newCachedThreadPool(); 26 | 27 | private WebSocketSession session; 28 | private Session.Shell shell; 29 | private Expect expect; 30 | 31 | private boolean sshConnected() { 32 | return ssh.isConnected() && ssh.isAuthenticated(); 33 | } 34 | 35 | @Override 36 | public void afterConnectionEstablished(WebSocketSession session) throws Exception { 37 | this.session = session; 38 | 39 | } 40 | 41 | @Override 42 | protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { 43 | 44 | if (!sshConnected()) { 45 | checkSshString(message); 46 | return; 47 | } 48 | 49 | executorService.submit(() -> { 50 | try { 51 | expect.sendLine(message.getPayload()); 52 | session.sendMessage(new TextMessage("> OK.\n")); 53 | Result result = expect.expect(Matchers.anyString()); 54 | if (result.isSuccessful()) 55 | session.sendMessage(new TextMessage(result.getInput())); 56 | else 57 | session.sendMessage(new TextMessage(message.getPayload() + " was unsuccessful..")); 58 | } catch (IOException e) { 59 | e.printStackTrace(); 60 | } 61 | 62 | }); 63 | 64 | } 65 | 66 | private void checkSshString(TextMessage message) throws Exception { 67 | String trim = message.getPayload(); 68 | if (trim.startsWith("connect::")) { 69 | 70 | String replace = trim.replace("connect::", ""); 71 | String[] split = replace.split("\\|"); 72 | String user = split[0]; 73 | String host = split[1]; 74 | String port = split[2]; 75 | String password = split[3]; 76 | 77 | connectToSshServer(user, host, port, password); 78 | 79 | } 80 | } 81 | 82 | private void connectToSshServer(String user, String host, String port, String password) throws Exception { 83 | 84 | ssh.addHostKeyVerifier((hostname, p, key) -> true); 85 | ssh.connect(host, Integer.parseInt(port)); 86 | ssh.authPassword(user, password); 87 | shell = ssh.startSession().startShell(); 88 | expect = new ExpectBuilder() 89 | .withOutput(shell.getOutputStream()) 90 | .withInputs(shell.getInputStream(), shell.getErrorStream()) 91 | .withTimeout(5, TimeUnit.SECONDS) 92 | .build(); 93 | session.sendMessage(new TextMessage("connected::true")); 94 | 95 | } 96 | 97 | @Override 98 | public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { 99 | System.out.println(status.getCode()); 100 | System.out.println(status.getReason()); 101 | // if (Objects.nonNull(ses)) 102 | // ses.disconnect(); 103 | // if (Objects.nonNull(channel)) 104 | // channel.disconnect(); 105 | } 106 | 107 | @Override 108 | public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { 109 | exception.printStackTrace(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 |

SSH on WEB

23 | 24 |
25 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/main/webapp/jquery.terminal/css/jquery.terminal.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This css file is part of jquery terminal 3 | * 4 | * Licensed under GNU LGPL Version 3 license 5 | * Copyright (c) 2011-2013 Jakub Jankiewicz 6 | * 7 | */ 8 | .terminal .terminal-output .format, .cmd .format, 9 | .cmd .prompt, .cmd .prompt div, .terminal .terminal-output div div{ 10 | display: inline-block; 11 | } 12 | .cmd .clipboard { 13 | position: absolute; 14 | bottom: 0; 15 | left: 0; 16 | opacity: 0.01; 17 | filter: alpha(opacity = 0.01); 18 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0.01); 19 | width: 2px; 20 | } 21 | .cmd > .clipboard { 22 | position: fixed; 23 | } 24 | .terminal { 25 | padding: 10px; 26 | position: relative; 27 | overflow: hidden; 28 | } 29 | .cmd { 30 | padding: 0; 31 | margin: 0; 32 | height: 1.3em; 33 | /*margin-top: 3px; */ 34 | } 35 | .cmd .cursor.blink { 36 | -webkit-animation: blink 1s infinite steps(1, start); 37 | -moz-animation: blink 1s infinite steps(1, start); 38 | -ms-animation: blink 1s infinite steps(1, start); 39 | animation: blink 1s infinite steps(1, start); 40 | } 41 | @keyframes blink { 42 | 0%, 100% { 43 | background-color: #000; 44 | color: #aaa; 45 | } 46 | 50% { 47 | background-color: #bbb; /* not #aaa because it's seem there is Google Chrome bug */ 48 | color: #000; 49 | } 50 | } 51 | @-webkit-keyframes blink { 52 | 0%, 100% { 53 | background-color: #000; 54 | color: #aaa; 55 | } 56 | 50% { 57 | background-color: #bbb; 58 | color: #000; 59 | } 60 | } 61 | @-ms-keyframes blink { 62 | 0%, 100% { 63 | background-color: #000; 64 | color: #aaa; 65 | } 66 | 50% { 67 | background-color: #bbb; 68 | color: #000; 69 | } 70 | } 71 | @-moz-keyframes blink { 72 | 0%, 100% { 73 | background-color: #000; 74 | color: #aaa; 75 | } 76 | 50% { 77 | background-color: #bbb; 78 | color: #000; 79 | } 80 | } 81 | .terminal .terminal-output div div, .cmd .prompt { 82 | display: block; 83 | line-height: 14px; 84 | height: 80%; 85 | } 86 | .cmd .prompt { 87 | float: left; 88 | } 89 | .terminal, .cmd { 90 | font-family: FreeMono, monospace; 91 | color: #aaa; 92 | background-color: #000; 93 | font-size: 12px; 94 | line-height: 14px; 95 | } 96 | .terminal-output > div { 97 | /*padding-top: 3px;*/ 98 | min-height: 14px; 99 | } 100 | .terminal .terminal-output div span { 101 | display: inline-block; 102 | } 103 | .cmd span { 104 | float: left; 105 | /*display: inline-block; */ 106 | } 107 | .terminal .inverted, .cmd .inverted, .cmd .cursor.blink { 108 | background-color: #aaa; 109 | color: #000; 110 | } 111 | .terminal .terminal-output div div::-moz-selection, 112 | .terminal .terminal-output div span::-moz-selection, 113 | .terminal .terminal-output div div a::-moz-selection { 114 | background-color: #aaa; 115 | color: #000; 116 | } 117 | .terminal .terminal-output div div::selection, 118 | .terminal .terminal-output div div a::selection, 119 | .terminal .terminal-output div span::selection, 120 | .cmd > span::selection, 121 | .cmd .prompt span::selection { 122 | background-color: #aaa; 123 | color: #000; 124 | } 125 | .terminal .terminal-output div.error, .terminal .terminal-output div.error div { 126 | color: red; 127 | } 128 | .tilda { 129 | position: fixed; 130 | top: 0; 131 | left: 0; 132 | width: 100%; 133 | z-index: 1100; 134 | } 135 | .clear { 136 | clear: both; 137 | } 138 | .terminal a { 139 | color: #0F60FF; 140 | } 141 | .terminal a:hover { 142 | color: red; 143 | } 144 | -------------------------------------------------------------------------------- /src/main/webapp/jquery.terminal/js/dterm.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Example plugin using JQuery Terminal Emulator 3 | * Copyright (C) 2010 Jakub Jankiewicz 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this program. If not, see . 17 | */ 18 | (function($) { 19 | $.extend_if_has = function(desc, source, array) { 20 | for (var i=array.length;i--;) { 21 | if (typeof source[array[i]] != 'undefined') { 22 | desc[array[i]] = source[array[i]]; 23 | } 24 | } 25 | return desc; 26 | }; 27 | $.fn.dterm = function(eval, options) { 28 | var op = $.extend_if_has({}, options, 29 | ['greetings', 'prompt', 'onInit', 30 | 'onExit', 'clear', 31 | 'login', 'name', 'exit']); 32 | op.enabled = false; 33 | var terminal = this.terminal(eval, op).css('overflow', 'hidden'); 34 | if (!options.title) { 35 | options.title = 'JQuery Terminal Emulator'; 36 | } 37 | if (options.logoutOnClose) { 38 | options.close = function(e, ui) { 39 | terminal.logout(); 40 | terminal.clear(); 41 | }; 42 | } else { 43 | options.close = function(e, ui) { 44 | terminal.disable(); 45 | }; 46 | } 47 | var self = this; 48 | this.dialog($.extend(options, { 49 | resizeStop: function(e, ui) { 50 | var content = self.find('.ui-dialog-content'); 51 | terminal.resize(content.width(), content.height()); 52 | }, 53 | open: function(e, ui) { 54 | terminal.focus(); 55 | terminal.resize(); 56 | }, 57 | show: 'fade', 58 | closeOnEscape: false 59 | })); 60 | self.terminal = terminal; 61 | return self; 62 | }; 63 | })(jQuery); 64 | -------------------------------------------------------------------------------- /src/main/webapp/jquery.terminal/js/jquery.mousewheel-min.js: -------------------------------------------------------------------------------- 1 | (function(c){function g(a){var b=a||window.event,i=[].slice.call(arguments,1),e=0,h=0,f=0;a=c.event.fix(b);a.type="mousewheel";if(b.wheelDelta)e=b.wheelDelta/120;if(b.detail)e=-b.detail/3;f=e;if(b.axis!==undefined&&b.axis===b.HORIZONTAL_AXIS){f=0;h=-1*e}if(b.wheelDeltaY!==undefined)f=b.wheelDeltaY/120;if(b.wheelDeltaX!==undefined)h=-1*b.wheelDeltaX/120;i.unshift(a,e,h,f);return(c.event.dispatch||c.event.handle).apply(this,i)}var d=["DOMMouseScroll","mousewheel"];if(c.event.fixHooks)for(var j=d.length;j;)c.event.fixHooks[d[--j]]= 2 | c.event.mouseHooks;c.event.special.mousewheel={setup:function(){if(this.addEventListener)for(var a=d.length;a;)this.addEventListener(d[--a],g,false);else this.onmousewheel=g},teardown:function(){if(this.removeEventListener)for(var a=d.length;a;)this.removeEventListener(d[--a],g,false);else this.onmousewheel=null}};c.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})})(jQuery); 3 | -------------------------------------------------------------------------------- /src/main/webapp/jquery.terminal/js/jquery.terminal-0.8.8.min.js: -------------------------------------------------------------------------------- 1 | (function(ctx){var sprintf=function(){if(!sprintf.cache.hasOwnProperty(arguments[0])){sprintf.cache[arguments[0]]=sprintf.parse(arguments[0])}return sprintf.format.call(null,sprintf.cache[arguments[0]],arguments)};sprintf.format=function(parse_tree,argv){var cursor=1,tree_length=parse_tree.length,node_type="",arg,output=[],i,k,match,pad,pad_character,pad_length;for(i=0;i>>0;break;case"x":arg=arg.toString(16);break;case"X":arg=arg.toString(16).toUpperCase();break}arg=/[def]/.test(match[8])&&match[3]&&arg>=0?"+"+arg:arg;pad_character=match[4]?match[4]=="0"?"0":match[4].charAt(1):" ";pad_length=match[6]-String(arg).length;pad=match[6]?str_repeat(pad_character,pad_length):"";output.push(match[5]?arg+pad:pad+arg)}}return output.join("")};sprintf.cache={};sprintf.parse=function(fmt){var _fmt=fmt,match=[],parse_tree=[],arg_names=0;while(_fmt){if((match=/^[^\x25]+/.exec(_fmt))!==null){parse_tree.push(match[0])}else if((match=/^\x25{2}/.exec(_fmt))!==null){parse_tree.push("%")}else if((match=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt))!==null){if(match[2]){arg_names|=1;var field_list=[],replacement_field=match[2],field_match=[];if((field_match=/^([a-z_][a-z_\d]*)/i.exec(replacement_field))!==null){field_list.push(field_match[1]);while((replacement_field=replacement_field.substring(field_match[0].length))!==""){if((field_match=/^\.([a-z_][a-z_\d]*)/i.exec(replacement_field))!==null){field_list.push(field_match[1])}else if((field_match=/^\[(\d+)\]/.exec(replacement_field))!==null){field_list.push(field_match[1])}else{throw"[sprintf] huh?"}}}else{throw"[sprintf] huh?"}match[2]=field_list}else{arg_names|=2}if(arg_names===3){throw"[sprintf] mixing positional and named placeholders is not (yet) supported"}parse_tree.push(match)}else{throw"[sprintf] huh?"}_fmt=_fmt.substring(match[0].length)}return parse_tree};var vsprintf=function(fmt,argv,_argv){_argv=argv.slice(0);_argv.splice(0,0,fmt);return sprintf.apply(null,_argv)};function get_type(variable){return Object.prototype.toString.call(variable).slice(8,-1).toLowerCase()}function str_repeat(input,multiplier){for(var output=[];multiplier>0;output[--multiplier]=input){}return output.join("")}ctx.sprintf=sprintf;ctx.vsprintf=vsprintf})(typeof exports!="undefined"?exports:window);(function($,undefined){"use strict";$.omap=function(o,fn){var result={};$.each(o,function(k,v){result[k]=fn.call(o,k,v)});return result};var isLS=typeof window.localStorage!=="undefined";function wls(n,v){var c;if(typeof n==="string"&&typeof v==="string"){localStorage[n]=v;return true}else if(typeof n==="object"&&typeof v==="undefined"){for(c in n){if(n.hasOwnProperty(c)){localStorage[c]=n[c]}}return true}return false}function wc(n,v){var dt,e,c;dt=new Date;dt.setTime(dt.getTime()+31536e6);e="; expires="+dt.toGMTString();if(typeof n==="string"&&typeof v==="string"){document.cookie=n+"="+v+e+"; path=/";return true}else if(typeof n==="object"&&typeof v==="undefined"){for(c in n){if(n.hasOwnProperty(c)){document.cookie=c+"="+n[c]+e+"; path=/"}}return true}return false}function rls(n){return localStorage[n]}function rc(n){var nn,ca,i,c;nn=n+"=";ca=document.cookie.split(";");for(i=0;itimes&×!==0||fn.call(element,counter)===false){jQuery.timer.remove(element,label,fn)}handler.inProgress=false};handler.$timerID=fn.$timerID;if(!element.$timers[label][fn.$timerID]){element.$timers[label][fn.$timerID]=window.setInterval(handler,interval)}if(!this.global[label]){this.global[label]=[]}this.global[label].push(element)},remove:function(element,label,fn){var timers=element.$timers,ret;if(timers){if(!label){for(var lab in timers){if(timers.hasOwnProperty(lab)){this.remove(element,lab,fn)}}}else if(timers[label]){if(fn){if(fn.$timerID){window.clearInterval(timers[label][fn.$timerID]);delete timers[label][fn.$timerID]}}else{for(var _fn in timers[label]){if(timers[label].hasOwnProperty(_fn)){window.clearInterval(timers[label][_fn]);delete timers[label][_fn]}}}for(ret in timers[label]){if(timers[label].hasOwnProperty(ret)){break}}if(!ret){ret=null;delete timers[label]}}for(ret in timers){if(timers.hasOwnProperty(ret)){break}}if(!ret){element.$timers=null}}}}});if(/(msie) ([\w.]+)/.exec(navigator.userAgent.toLowerCase())){jQuery(window).one("unload",function(){var global=jQuery.timer.global;for(var label in global){if(global.hasOwnProperty(label)){var els=global[label],i=els.length;while(--i){jQuery.timer.remove(els[i],label)}}}})}(function(undef){if(!String.prototype.split.toString().match(/\[native/)){return}var nativeSplit=String.prototype.split,compliantExecNpcg=/()??/.exec("")[1]===undef,self;self=function(str,separator,limit){if(Object.prototype.toString.call(separator)!=="[object RegExp]"){return nativeSplit.call(str,separator,limit)}var output=[],flags=(separator.ignoreCase?"i":"")+(separator.multiline?"m":"")+(separator.extended?"x":"")+(separator.sticky?"y":""),lastLastIndex=0,separator2,match,lastIndex,lastLength;separator=new RegExp(separator.source,flags+"g");str+="";if(!compliantExecNpcg){separator2=new RegExp("^"+separator.source+"$(?!\\s)",flags)}limit=limit===undef?-1>>>0:limit>>>0;while(match=separator.exec(str)){lastIndex=match.index+match[0].length;if(lastIndex>lastLastIndex){output.push(str.slice(lastLastIndex,match.index));if(!compliantExecNpcg&&match.length>1){match[0].replace(separator2,function(){for(var i=1;i1&&match.index=limit){break}}if(separator.lastIndex===match.index){separator.lastIndex++}}if(lastLastIndex===str.length){if(lastLength||!separator.test("")){output.push("")}}else{output.push(str.slice(lastLastIndex))}return output.length>limit?output.slice(0,limit):output};String.prototype.split=function(separator,limit){return self(this,separator,limit)};return self})();function str_parts(str,length){var result=[];var len=str.length;if(len0?data[data.length-1]:null}})}$.json_stringify=function(object,level){var result="",i;level=level===undefined?1:level;var type=typeof object;switch(type){case"function":result+=object;break;case"boolean":result+=object?"true":"false";break;case"object":if(object===null){result+="null"}else if(object instanceof Array){result+="[";var len=object.length;for(i=0;i1?",":"";if(level===1){result=result.replace(/,([\]}])/g,"$1")}return result.replace(/([\[{]),/g,"$1")};function History(name,size){var enabled=true;var storage_key="";if(typeof name==="string"&&name!==""){storage_key=name+"_"}storage_key+="commands";var data=$.Storage.get(storage_key);data=data?$.parseJSON(data):[];var pos=data.length-1;$.extend(this,{append:function(item){if(enabled){if(data[data.length-1]!==item){data.push(item);if(size&&data.length>size){data=data.slice(-size)}pos=data.length-1;$.Storage.set(storage_key,$.json_stringify(data))}}},data:function(){return data},reset:function(){pos=data.length-1},last:function(){return data[length-1]},end:function(){return pos===data.length-1},position:function(){return pos},current:function(){return data[pos]},next:function(){if(pos0){--pos}if(old!==-1){return data[pos]}},clear:function(){data=[];this.purge()},enabled:function(){return enabled},enable:function(){enabled=true},purge:function(){$.Storage.remove(storage_key)},disable:function(){enabled=false}})}$.fn.cmd=function(options){var self=this;var maybe_data=self.data("cmd");if(maybe_data){return maybe_data}self.addClass("cmd");self.append(''+' ');var clip=$("