├── .gitignore
├── .gitmodules
├── CHANGES
├── CONTRIBUTING.md
├── Cakefile
├── LICENSE
├── README.md
├── args.js
├── bootstrap.min.js
├── bootstrap3-typeahead.min.js
├── css
└── bootstrap.min.css
├── fonts
├── glyphicons-halflings-regular.eot
├── glyphicons-halflings-regular.svg
├── glyphicons-halflings-regular.ttf
└── glyphicons-halflings-regular.woff
├── icon.png
├── img
├── glyphicons-halflings-white.png
└── glyphicons-halflings.png
├── index.html
├── jquery-1.9.0.min.js
├── main.js
├── menu_icon.png
├── menu_icon@2x.png
├── package.json
├── qrcode.min.js
├── screenshot.png
├── src
├── args.coffee
├── main.coffee
└── update.coffee
├── update.js
└── utils
├── Info.plist
├── build.sh
├── linux
└── start.sh
├── make_icon.sh
└── shadowsocks.icns
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node-webkit.app/
3 | node_modules/
4 | nwsnapshot
5 | gui-config.json
6 | *.exe
7 | *.dll
8 | *.pak
9 | .idea
10 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/.gitmodules
--------------------------------------------------------------------------------
/CHANGES:
--------------------------------------------------------------------------------
1 | 0.6.4 2014-11-01
2 | - Support share over LAN
3 |
4 | 0.6.2 2014-10-12
5 | - Fix IPv6 address
6 |
7 | 0.6.1 2014-10-09
8 | - Support QRCode
9 |
10 | 0.6.0 2014-09-23
11 | - Fix Linux on some latest distributions
12 |
13 | 0.5.0 2014-09-09
14 | - Add RC4-MD5 encryption
15 |
16 | 0.4.1 2014-03-28
17 | - Close button should hide the window instead of quit on OS X
18 | - Listen to 127.0.0.1 instead of 0.0.0.0
19 |
20 | 0.4 2013-07-31
21 | - Update shadowsocks-nodejs to 1.4.1
22 |
23 | 0.3.0 2013-07-21
24 | - Update shadowsocks-nodejs to 1.4.0
25 |
26 | 0.2.2 2013-07-15
27 | - Fix keyboard on OSX; disabled hiding Dock icon
28 |
29 | 0.2.1 2013-07-14
30 | - Save config to gui-config.json file on Windows
31 | - Fix incorrect window size on Windows
32 | - Hide Dock icon on OSX
33 | - Check for updates
34 | - Reduce binary size
35 | - Run gc() every 30s to reduce memory usage
36 |
37 | 0.2 2013-07-12
38 | - Support multiple profiles
39 | - Fix problems with restarting shadowsocks
40 |
41 | 0.1.5 2013-06-23
42 | - Package binary into a single exe/app
43 |
44 | 0.1.4 2013-06-22
45 | - Minimize on startup and after config is saved
46 |
47 | 0.1.3 2013-06-19
48 | - Update shadowsocks-nodejs
49 |
50 | 0.1.2 2013-06-18
51 | - Update shadowsocks-nodejs
52 |
53 | 0.1.1 2013-05-30
54 | - Auto-Complete Server IP
55 |
56 | 0.1 2013-05-29
57 | - Initial version
58 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | How to contribute
2 | =================
3 |
4 | Before you submit issues, please take a few minutes to read this guide.
5 |
6 | 在你提交问题前,请花两分钟阅读一下这份指南。
7 |
8 | Issues
9 | ------
10 |
11 | Please include the following information in your submission:
12 |
13 | 1. How did you set up your environment? (OS, version of Shadowsocks)
14 | 2. Where did you see this error, was it on local or on server?
15 | 3. What happened in your browser? Just no response, or any error message?
16 | 4. 10 lines of log on the local side of shadowsocks when the error happened.
17 | 5. 10 lines of log on the server side of shadowsocks when the error happened.
18 | 6. Any other useful information.
19 |
20 | Skip any of them if you don't know its meaning.
21 |
22 | 问题反馈
23 | -------
24 |
25 | 请提交下面的信息:
26 |
27 | 1. 你是如何搭建环境的?(操作系统,Shadowsocks 版本)
28 | 2. 错误是发生在哪里,本地还是服务器?
29 | 3. 浏览器里的现象是什么?一直转菊花,还是有提示错误?
30 | 4. 发生错误时,本地端的十行完整的日志。
31 | 5. 发生错误时,服务器端的十行完整的日志。
32 | 6. 其它你认为可能和问题有关的信息。
33 |
34 | 如果你不清楚其中某条的含义, 可以直接跳过那一条。
35 |
--------------------------------------------------------------------------------
/Cakefile:
--------------------------------------------------------------------------------
1 | {print} = require 'util'
2 | {spawn} = require 'child_process'
3 |
4 | build = () ->
5 | os = require 'os'
6 | if os.platform() == 'win32'
7 | coffeeCmd = 'coffee.cmd'
8 | else
9 | coffeeCmd = 'coffee'
10 | coffee = spawn coffeeCmd, ['-c', '-o', '.', 'src']
11 | coffee.stderr.on 'data', (data) ->
12 | process.stderr.write data.toString()
13 | coffee.stdout.on 'data', (data) ->
14 | print data.toString()
15 | coffee.on 'exit', (code) ->
16 | if code != 0
17 | process.exit code
18 |
19 | task 'build', 'Build ./ from src/', ->
20 | build()
21 |
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | shadowsocks-gui
2 |
3 | Copyright (c) 2014 clowwindy
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
13 | all 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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | shadowsocks-gui
2 | ===============
3 |
4 | #### Windows
5 |
6 | Please upgrade to [Shadowsocks for Windows](https://github.com/clowwindy/shadowsocks-csharp)
7 |
8 | #### OS X
9 |
10 | Please upgrade to [ShadowsocksX](https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help)
11 |
12 | #### Linux users
13 |
14 | Please upgrade to [shadowsocks-qt5](https://github.com/librehat/shadowsocks-qt5)
15 |
--------------------------------------------------------------------------------
/args.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.8.0
2 | (function() {
3 | var allConfigs, defaultConfig, deleteConfig, fs, guiconfigFilename, loadConfig, loadConfigs, loadFromJSON, loadIndex, localStorage, publicConfig, saveConfig, saveConfigs, saveIndex, saveToJSON, util;
4 |
5 | localStorage = window.localStorage;
6 |
7 | util = require('util');
8 |
9 | fs = require('fs');
10 |
11 | guiconfigFilename = fs.realpathSync(process.execPath + '/..') + '/gui-config.json';
12 |
13 | loadFromJSON = function() {
14 | var data, e, temp;
15 | if (process.platform === 'win32') {
16 | try {
17 | data = fs.readFileSync(guiconfigFilename);
18 | temp = JSON.parse(data.toString('utf-8'));
19 | if (temp.configs) {
20 | temp.configs = JSON.stringify(temp.configs);
21 | }
22 | localStorage = temp;
23 | return util.log('reading config file');
24 | } catch (_error) {
25 | e = _error;
26 | return console.log(e);
27 | }
28 | }
29 | };
30 |
31 | loadFromJSON();
32 |
33 | saveToJSON = function() {
34 | var data, e, temp;
35 | if (process.platform === 'win32') {
36 | util.log('saving config file');
37 | temp = JSON.parse(JSON.stringify(localStorage));
38 | if (temp.configs) {
39 | temp.configs = JSON.parse(temp.configs);
40 | }
41 | data = JSON.stringify(temp, null, 2);
42 | try {
43 | return fs.writeFileSync(guiconfigFilename, data, {
44 | 'encoding': 'utf-8'
45 | });
46 | } catch (_error) {
47 | e = _error;
48 | return util.log(e);
49 | }
50 | }
51 | };
52 |
53 | publicConfig = {
54 | server: '209.141.36.62',
55 | server_port: 8348,
56 | local_port: 1080,
57 | password: '$#HAL9000!',
58 | method: 'aes-256-cfb',
59 | timeout: 600
60 | };
61 |
62 | defaultConfig = {
63 | server_port: 8388,
64 | local_port: 1080,
65 | method: 'aes-256-cfb',
66 | timeout: 600
67 | };
68 |
69 | loadConfigs = function() {
70 | var e;
71 | try {
72 | return JSON.parse(localStorage['configs'] || '[]');
73 | } catch (_error) {
74 | e = _error;
75 | util.log(e);
76 | return [];
77 | }
78 | };
79 |
80 | allConfigs = function() {
81 | var c, configs, e, i, result;
82 | if (localStorage['configs']) {
83 | result = [];
84 | try {
85 | configs = loadConfigs();
86 | for (i in configs) {
87 | c = configs[i];
88 | result.push("" + c.server + ":" + c.server_port);
89 | }
90 | return result;
91 | } catch (_error) {
92 | e = _error;
93 | }
94 | }
95 | return [];
96 | };
97 |
98 | saveIndex = function(index) {
99 | localStorage['index'] = index;
100 | return saveToJSON();
101 | };
102 |
103 | loadIndex = function() {
104 | return +localStorage['index'];
105 | };
106 |
107 | saveConfigs = function(configs) {
108 | localStorage['configs'] = JSON.stringify(configs);
109 | return saveToJSON();
110 | };
111 |
112 | saveConfig = function(index, config) {
113 | var configs;
114 | if (index === -1) {
115 | index = NaN;
116 | }
117 | configs = loadConfigs();
118 | if (isNaN(index)) {
119 | configs.push(config);
120 | index = configs.length - 1;
121 | } else {
122 | configs[index] = config;
123 | }
124 | saveConfigs(configs);
125 | return index;
126 | };
127 |
128 | loadConfig = function(index) {
129 | var configs;
130 | if (isNaN(index)) {
131 | return defaultConfig;
132 | }
133 | if (index === -1) {
134 | return publicConfig;
135 | }
136 | configs = loadConfigs();
137 | return configs[index] || defaultConfig;
138 | };
139 |
140 | deleteConfig = function(index) {
141 | var configs;
142 | if ((!isNaN(index)) && !(index === -1)) {
143 | configs = loadConfigs();
144 | configs.splice(index, 1);
145 | return saveConfigs(configs);
146 | }
147 | };
148 |
149 | exports.allConfigs = allConfigs;
150 |
151 | exports.saveConfig = saveConfig;
152 |
153 | exports.loadConfig = loadConfig;
154 |
155 | exports.deleteConfig = deleteConfig;
156 |
157 | exports.loadIndex = loadIndex;
158 |
159 | exports.saveIndex = saveIndex;
160 |
161 | exports.publicConfig = publicConfig;
162 |
163 | }).call(this);
164 |
--------------------------------------------------------------------------------
/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.2.0 (http://getbootstrap.com)
3 | * Copyright 2011-2014 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.2.0",d.prototype.close=function(b){function c(){f.detach().trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",c).emulateTransitionEnd(150):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.2.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),d[e](null==f[b]?this.options[b]:f[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b).on("keydown.bs.carousel",a.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.2.0",c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.to=function(b){var c=this,d=this.getItemIndex(this.$active=this.$element.find(".item.active"));return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=e[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:g});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,f&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(e)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:g});return a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one("bsTransitionEnd",function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger(m)),f&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(b=!b),e||d.data("bs.collapse",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};c.VERSION="3.2.0",c.DEFAULTS={toggle:!0},c.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},c.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var c=a.Event("show.bs.collapse");if(this.$element.trigger(c),!c.isDefaultPrevented()){var d=this.$parent&&this.$parent.find("> .panel > .in");if(d&&d.length){var e=d.data("bs.collapse");if(e&&e.transitioning)return;b.call(d,"hide"),e||d.data("bs.collapse",null)}var f=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[f](0),this.transitioning=1;var g=function(){this.$element.removeClass("collapsing").addClass("collapse in")[f](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return g.call(this);var h=a.camelCase(["scroll",f].join("-"));this.$element.one("bsTransitionEnd",a.proxy(g,this)).emulateTransitionEnd(350)[f](this.$element[0][h])}}},c.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},c.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var d=a.fn.collapse;a.fn.collapse=b,a.fn.collapse.Constructor=c,a.fn.collapse.noConflict=function(){return a.fn.collapse=d,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(c){var d,e=a(this),f=e.attr("data-target")||c.preventDefault()||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),g=a(f),h=g.data("bs.collapse"),i=h?"toggle":e.data(),j=e.attr("data-parent"),k=j&&a(j);h&&h.transitioning||(k&&k.find('[data-toggle="collapse"][data-parent="'+j+'"]').not(e).addClass("collapsed"),e[g.hasClass("in")?"addClass":"removeClass"]("collapsed")),b.call(g,i)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.2.0",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('
').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.divider):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(i.filter(":focus"));38==b.keyCode&&j>0&&j--,40==b.keyCode&&j').appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;e?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(150):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var f=function(){c.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",f).emulateTransitionEnd(150):f()}else b&&b()},c.prototype.checkScrollbar=function(){document.body.clientWidth>=window.innerWidth||(this.scrollbarWidth=this.scrollbarWidth||this.measureScrollbar())},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.scrollbarWidth&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.2.0",c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show()},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var c=a.contains(document.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!c)return;var d=this,e=this.tip(),f=this.getUID(this.type);this.setContent(),e.attr("id",f),this.$element.attr("aria-describedby",f),this.options.animation&&e.addClass("fade");var g="function"==typeof this.options.placement?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,h=/\s?auto?\s?/i,i=h.test(g);i&&(g=g.replace(h,"")||"top"),e.detach().css({top:0,left:0,display:"block"}).addClass(g).data("bs."+this.type,this),this.options.container?e.appendTo(this.options.container):e.insertAfter(this.$element);var j=this.getPosition(),k=e[0].offsetWidth,l=e[0].offsetHeight;if(i){var m=g,n=this.$element.parent(),o=this.getPosition(n);g="bottom"==g&&j.top+j.height+l-o.scroll>o.height?"top":"top"==g&&j.top-o.scroll-l<0?"bottom":"right"==g&&j.right+k>o.width?"left":"left"==g&&j.left-kg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.validate=function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){clearTimeout(this.timeout),this.hide().$element.off("."+this.type).removeData("bs."+this.type)};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.2.0",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").empty()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},c.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){var e=a.proxy(this.process,this);this.$body=a("body"),this.$scrollElement=a(a(c).is("body")?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",e),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.2.0",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b="offset",c=0;a.isWindow(this.$scrollElement[0])||(b="position",c=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var d=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+c,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){d.offsets.push(this[0]),d.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.2.0",c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.closest("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},c.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one("bsTransitionEnd",e).emulateTransitionEnd(150):e(),f.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(c){c.preventDefault(),b.call(a(this),"show")})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.2.0",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=a(document).height(),d=this.$target.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=b-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){null!=this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:b-this.$element.height()-h}))}}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},d.offsetBottom&&(d.offset.bottom=d.offsetBottom),d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
--------------------------------------------------------------------------------
/bootstrap3-typeahead.min.js:
--------------------------------------------------------------------------------
1 | (function(root,factory){"use strict";if(typeof module!=="undefined"&&module.exports){module.exports=factory(require("jquery")(root))}else if(typeof define==="function"&&define.amd){define("bootstrap3-typeahead",["jquery"],function($){return factory($)})}else{factory(root.jQuery)}})(this,function($){"use strict";var Typeahead=function(element,options){this.$element=$(element);this.options=$.extend({},$.fn.typeahead.defaults,options);this.matcher=this.options.matcher||this.matcher;this.sorter=this.options.sorter||this.sorter;this.select=this.options.select||this.select;this.autoSelect=typeof this.options.autoSelect=="boolean"?this.options.autoSelect:true;this.highlighter=this.options.highlighter||this.highlighter;this.render=this.options.render||this.render;this.updater=this.options.updater||this.updater;this.source=this.options.source;this.delay=typeof this.options.delay=="number"?this.options.delay:250;this.$menu=$(this.options.menu);this.shown=false;this.listen();this.showHintOnFocus=typeof this.options.showHintOnFocus=="boolean"?this.options.showHintOnFocus:false};Typeahead.prototype={constructor:Typeahead,select:function(){var val=this.$menu.find(".active").data("value");if(this.autoSelect||val){this.$element.val(this.updater(val)).change()}return this.hide()},updater:function(item){return item},setSource:function(source){this.source=source},show:function(){var pos=$.extend({},this.$element.position(),{height:this.$element[0].offsetHeight}),scrollHeight;scrollHeight=typeof this.options.scrollHeight=="function"?this.options.scrollHeight.call():this.options.scrollHeight;this.$menu.insertAfter(this.$element).css({top:pos.top+pos.height+scrollHeight,left:pos.left}).show();this.shown=true;return this},hide:function(){this.$menu.hide();this.shown=false;return this},lookup:function(query){var items;if(typeof query!="undefined"&&query!==null){this.query=query}else{this.query=this.$element.val()||""}if(this.query.length");var query=this.query;var i=item.indexOf(query);var len,leftPart,middlePart,rightPart,strong;len=query.length;if(len==0){return html.text(item).html()}while(i>-1){leftPart=item.substr(0,i);middlePart=item.substr(i,len);rightPart=item.substr(i+len);strong=$("").text(middlePart);html.append(document.createTextNode(leftPart)).append(strong);item=rightPart;i=item.indexOf(query)}return html.append(document.createTextNode(item)).html()},render:function(items){var that=this;items=$(items).map(function(i,item){i=$(that.options.item).data("value",item);i.find("a").html(that.highlighter(item));return i[0]});if(this.autoSelect){items.first().addClass("active")}this.$menu.html(items);return this},next:function(event){var active=this.$menu.find(".active").removeClass("active"),next=active.next();if(!next.length){next=$(this.$menu.find("li")[0])}next.addClass("active")},prev:function(event){var active=this.$menu.find(".active").removeClass("active"),prev=active.prev();if(!prev.length){prev=this.$menu.find("li").last()}prev.addClass("active")},listen:function(){this.$element.on("focus",$.proxy(this.focus,this)).on("blur",$.proxy(this.blur,this)).on("keypress",$.proxy(this.keypress,this)).on("keyup",$.proxy(this.keyup,this));if(this.eventSupported("keydown")){this.$element.on("keydown",$.proxy(this.keydown,this))}this.$menu.on("click",$.proxy(this.click,this)).on("mouseenter","li",$.proxy(this.mouseenter,this)).on("mouseleave","li",$.proxy(this.mouseleave,this))},destroy:function(){this.$element.data("typeahead",null);this.$element.off("focus").off("blur").off("keypress").off("keyup");if(this.eventSupported("keydown")){this.$element.off("keydown")}this.$menu.remove()},eventSupported:function(eventName){var isSupported=eventName in this.$element;if(!isSupported){this.$element.setAttribute(eventName,"return;");isSupported=typeof this.$element[eventName]==="function"}return isSupported},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault();this.prev();break;case 40:e.preventDefault();this.next();break}e.stopPropagation()},keydown:function(e){this.suppressKeyPressRepeat=~$.inArray(e.keyCode,[40,38,9,13,27]);if(!this.shown&&e.keyCode==40){this.lookup("")}else{this.move(e)}},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation();e.preventDefault()},focus:function(e){if(!this.focused){this.focused=true;if(this.options.minLength===0&&!this.$element.val()||this.options.showHintOnFocus){this.lookup()}}},blur:function(e){this.focused=false;if(!this.mousedover&&this.shown)this.hide()},click:function(e){e.stopPropagation();e.preventDefault();this.select();this.$element.focus()},mouseenter:function(e){this.mousedover=true;this.$menu.find(".active").removeClass("active");$(e.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=false;if(!this.focused&&this.shown)this.hide()}};var old=$.fn.typeahead;$.fn.typeahead=function(option){var arg=arguments;return this.each(function(){var $this=$(this),data=$this.data("typeahead"),options=typeof option=="object"&&option;if(!data)$this.data("typeahead",data=new Typeahead(this,options));if(typeof option=="string"){if(arg.length>1){data[option].apply(data,Array.prototype.slice.call(arg,1))}else{data[option]()}}})};$.fn.typeahead.defaults={source:[],items:8,menu:'',item:'',minLength:1,scrollHeight:0,autoSelect:true};$.fn.typeahead.Constructor=Typeahead;$.fn.typeahead.noConflict=function(){$.fn.typeahead=old;return this};$(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(e){var $this=$(this);if($this.data("typeahead"))return;$this.typeahead($this.data())})});
--------------------------------------------------------------------------------
/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/fonts/glyphicons-halflings-regular.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/icon.png
--------------------------------------------------------------------------------
/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Shadowsocks GUI
5 |
6 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Please fill in the blanks and click Save.
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
136 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.8.0
2 | (function() {
3 | var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
4 |
5 | $(function() {
6 | var addConfig, addServer, args, chooseServer, deleteConfig, divWarning, divWarningShown, gui, hide, isRestarting, load, local, menu, os, publicConfig, qrcode, quit, reloadServerList, restartServer, save, serverHistory, show, tray, update, util, win;
7 | os = require('os');
8 | gui = require('nw.gui');
9 | divWarning = $('#divWarning');
10 | divWarningShown = false;
11 | serverHistory = function() {
12 | return (localStorage['server_history'] || '').split('|');
13 | };
14 | util = require('util');
15 | util.log = function(s) {
16 | console.log(new Date().toLocaleString() + (" - " + s));
17 | if (!divWarningShown) {
18 | divWarning.show();
19 | divWarningShown = true;
20 | }
21 | return divWarning.text(s);
22 | };
23 | args = require('./args');
24 | local = require('shadowsocks');
25 | update = require('./update');
26 | update.checkUpdate(function(url, version) {
27 | var divNewVersion, span;
28 | divNewVersion = $('#divNewVersion');
29 | span = $("New version " + version + " found, click here to download");
30 | span.click(function() {
31 | return gui.Shell.openExternal(url);
32 | });
33 | divNewVersion.find('.msg').append(span);
34 | return divNewVersion.fadeIn();
35 | });
36 | addServer = function(serverIP) {
37 | var newServers, server, servers, _i, _len;
38 | servers = (localStorage['server_history'] || '').split('|');
39 | servers.push(serverIP);
40 | newServers = [];
41 | for (_i = 0, _len = servers.length; _i < _len; _i++) {
42 | server = servers[_i];
43 | if (server && __indexOf.call(newServers, server) < 0) {
44 | newServers.push(server);
45 | }
46 | }
47 | return localStorage['server_history'] = newServers.join('|');
48 | };
49 | $('#inputServerIP').typeahead({
50 | source: serverHistory
51 | });
52 | chooseServer = function() {
53 | var index;
54 | index = +$(this).attr('data-key');
55 | args.saveIndex(index);
56 | load(false);
57 | return reloadServerList();
58 | };
59 | reloadServerList = function() {
60 | var configName, configs, currentIndex, divider, i, menuItem, serverMenu, _results;
61 | currentIndex = args.loadIndex();
62 | configs = args.allConfigs();
63 | divider = $('#serverIPMenu .insert-point');
64 | serverMenu = $('#serverIPMenu .divider');
65 | $('#serverIPMenu li.server').remove();
66 | i = 0;
67 | _results = [];
68 | for (configName in configs) {
69 | if (i === currentIndex) {
70 | menuItem = $(" " + configs[configName] + " ");
71 | } else {
72 | menuItem = $(" " + configs[configName] + " ");
73 | }
74 | menuItem.find('a').click(chooseServer);
75 | menuItem.insertBefore(divider, serverMenu);
76 | _results.push(i++);
77 | }
78 | return _results;
79 | };
80 | addConfig = function() {
81 | args.saveIndex(NaN);
82 | reloadServerList();
83 | return load(false);
84 | };
85 | deleteConfig = function() {
86 | args.deleteConfig(args.loadIndex());
87 | args.saveIndex(NaN);
88 | reloadServerList();
89 | return load(false);
90 | };
91 | publicConfig = function() {
92 | args.saveIndex(-1);
93 | reloadServerList();
94 | return load(false);
95 | };
96 | save = function() {
97 | var config, index;
98 | config = {};
99 | $('input,select').each(function() {
100 | var key, val;
101 | key = $(this).attr('data-key');
102 | if (this.type === 'checkbox') {
103 | val = this.checked;
104 | } else {
105 | val = $(this).val();
106 | }
107 | return config[key] = val;
108 | });
109 | index = args.saveConfig(args.loadIndex(), config);
110 | args.saveIndex(index);
111 | reloadServerList();
112 | util.log('config saved');
113 | restartServer(config);
114 | return false;
115 | };
116 | load = function(restart) {
117 | var config;
118 | config = args.loadConfig(args.loadIndex());
119 | $('input,select').each(function() {
120 | var key, val;
121 | key = $(this).attr('data-key');
122 | val = config[key] || '';
123 | if (this.type === 'checkbox') {
124 | this.checked = val;
125 | return config[key] = this.checked;
126 | } else {
127 | $(this).val(val);
128 | return config[key] = this.value;
129 | }
130 | });
131 | if (restart) {
132 | return restartServer(config);
133 | }
134 | };
135 | isRestarting = false;
136 | restartServer = function(config) {
137 | var e, start;
138 | if (config.server && +config.server_port && config.password && +config.local_port && config.method) {
139 | if (isRestarting) {
140 | util.log("Already restarting");
141 | return;
142 | }
143 | isRestarting = true;
144 | start = function() {
145 | var e;
146 | try {
147 | isRestarting = false;
148 | util.log('Starting shadowsocks...');
149 | console.log(config);
150 | window.local = local.createServer(config.server, config.server_port, config.local_port, config.password, config.method, 1000 * 600, config.share ? '0.0.0.0' : '127.0.0.1');
151 | addServer(config.server);
152 | $('#divError').fadeOut();
153 | return gui.Window.get().hide();
154 | } catch (_error) {
155 | e = _error;
156 | return util.log(e);
157 | }
158 | };
159 | if (window.local != null) {
160 | try {
161 | util.log('Restarting shadowsocks');
162 | if (window.local.address()) {
163 | window.local.close();
164 | }
165 | return setTimeout(start, 1000);
166 | } catch (_error) {
167 | e = _error;
168 | isRestarting = false;
169 | return util.log(e);
170 | }
171 | } else {
172 | return start();
173 | }
174 | } else {
175 | return $('#divError').fadeIn();
176 | }
177 | };
178 | qrcode = function(method, password, hostname, port) {
179 | return "ss://" + (btoa("" + method + ":" + password + "@" + hostname + ":" + port));
180 | };
181 | $('#buttonSave').on('click', save);
182 | $('#buttonNewProfile').on('click', addConfig);
183 | $('#buttonDeleteProfile').on('click', deleteConfig);
184 | $('#buttonPublicServer').on('click', publicConfig);
185 | $('#buttonConsole').on('click', function() {
186 | return gui.Window.get().showDevTools();
187 | });
188 | $('#buttonAbout').on('click', function() {
189 | return gui.Shell.openExternal('https://github.com/shadowsocks/shadowsocks-gui');
190 | });
191 | $("#buttonQr").on('click', function() {
192 | return window.qrCode.makeCode(qrcode($('#selectMethod').val(), $('#inputPassword').val(), $('#inputServerIP').val(), $('#inputServerPort').val()));
193 | });
194 | tray = new gui.Tray({
195 | icon: 'menu_icon@2x.png'
196 | });
197 | menu = new gui.Menu();
198 | tray.on('click', function() {
199 | return gui.Window.get().show();
200 | });
201 | show = new gui.MenuItem({
202 | type: 'normal',
203 | label: 'Show',
204 | click: function() {
205 | return gui.Window.get().show();
206 | }
207 | });
208 | quit = new gui.MenuItem({
209 | type: 'normal',
210 | label: 'Quit',
211 | click: function() {
212 | return gui.Window.get().close(true);
213 | }
214 | });
215 | hide = new gui.MenuItem({
216 | type: 'normal',
217 | label: 'Hide',
218 | click: function() {
219 | return gui.Window.get().hide();
220 | }
221 | });
222 | menu.append(show);
223 | menu.append(hide);
224 | menu.append(quit);
225 | tray.menu = menu;
226 | window.tray = tray;
227 | win = gui.Window.get();
228 | win.on('minimize', function() {
229 | return this.hide();
230 | });
231 | win.on('close', function(quit) {
232 | if (os.platform() === 'darwin' && !quit) {
233 | return this.hide();
234 | } else {
235 | return this.close(true);
236 | }
237 | });
238 | reloadServerList();
239 | return load(true);
240 | });
241 |
242 | }).call(this);
243 |
--------------------------------------------------------------------------------
/menu_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/menu_icon.png
--------------------------------------------------------------------------------
/menu_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/menu_icon@2x.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shadowsocks-gui",
3 | "main": "index.html",
4 | "description": "shadowsocks client with GUI",
5 | "version": "0.6.4",
6 | "keywords": [ "shadowsocks", "node-webkit" ],
7 | "single-instance": false,
8 | "dependencies": {
9 | "shadowsocks": "1.5.2"
10 | },
11 | "window": {
12 | "icon": "icon.png",
13 | "toolbar": false,
14 | "width": 360,
15 | "height": 420,
16 | "position": "mouse",
17 | "min_width": 360,
18 | "min_height": 420,
19 | "max_width": 360,
20 | "max_height": 420
21 | },
22 | "js-flags": "--expose-gc",
23 | "licenses": [
24 | {
25 | "type": "MIT",
26 | "url": "http://opensource.org/licenses/MIT"
27 | }
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/qrcode.min.js:
--------------------------------------------------------------------------------
1 | var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j=0?p.get(q):0}}for(var r=0,m=0;mm;m++)for(var j=0;jm;m++)for(var j=0;j=0;)b^=f.G15<=0;)b^=f.G18<>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;cf;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=[''],h=0;d>h;h++){g.push("");for(var i=0;d>i;i++)g.push(' | ');g.push("
")}g.push("
"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}();
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/screenshot.png
--------------------------------------------------------------------------------
/src/args.coffee:
--------------------------------------------------------------------------------
1 | localStorage = window.localStorage
2 | util = require 'util'
3 |
4 | fs = require 'fs'
5 | guiconfigFilename = fs.realpathSync(process.execPath + '/..') + '/gui-config.json'
6 |
7 | loadFromJSON = ->
8 | # Windows users are happy to see a config file within their shadowsocks-gui folder
9 | if process.platform == 'win32'
10 | try
11 | data = fs.readFileSync guiconfigFilename
12 | temp = JSON.parse data.toString('utf-8')
13 | # make config file easier to read
14 | if temp.configs
15 | temp.configs = JSON.stringify(temp.configs)
16 | localStorage = temp
17 | util.log 'reading config file'
18 | catch e
19 | console.log e
20 |
21 | loadFromJSON()
22 |
23 | saveToJSON = ->
24 | if process.platform == 'win32'
25 | util.log 'saving config file'
26 | # make config file easier to read
27 | temp = JSON.parse(JSON.stringify(localStorage))
28 | if temp.configs
29 | temp.configs = JSON.parse(temp.configs)
30 | data = JSON.stringify(temp, null, 2)
31 | try
32 | fs.writeFileSync guiconfigFilename, data, 'encoding': 'utf-8'
33 | catch e
34 | util.log e
35 |
36 | # This is a public server
37 | publicConfig =
38 | server: '209.141.36.62'
39 | server_port: 8348
40 | local_port: 1080
41 | password: '$#HAL9000!'
42 | method: 'aes-256-cfb'
43 | timeout: 600
44 |
45 | defaultConfig =
46 | server_port: 8388
47 | local_port: 1080
48 | method: 'aes-256-cfb'
49 | timeout: 600
50 |
51 |
52 | loadConfigs = ->
53 | try
54 | JSON.parse(localStorage['configs'] or '[]')
55 | catch e
56 | util.log e
57 | []
58 |
59 | allConfigs = ->
60 | if localStorage['configs']
61 | result = []
62 | try
63 | configs = loadConfigs()
64 | for i of configs
65 | c = configs[i]
66 | result.push "#{c.server}:#{c.server_port}"
67 | return result
68 | catch e
69 | []
70 |
71 | saveIndex = (index) ->
72 | localStorage['index'] = index
73 | saveToJSON()
74 |
75 | loadIndex = ->
76 | +localStorage['index']
77 |
78 | saveConfigs = (configs) ->
79 | localStorage['configs'] = JSON.stringify(configs)
80 | saveToJSON()
81 |
82 | saveConfig = (index, config) ->
83 | if index == -1
84 | # if modified based on public server, add a profile, not to modify public server
85 | index = NaN
86 | configs = loadConfigs()
87 | if isNaN(index)
88 | configs.push config
89 | index = configs.length - 1
90 | else
91 | configs[index] = config
92 | saveConfigs configs
93 | index
94 |
95 | loadConfig = (index) ->
96 | if isNaN(index)
97 | return defaultConfig
98 | if index == -1
99 | return publicConfig
100 | configs = loadConfigs()
101 | return configs[index] or defaultConfig
102 |
103 | deleteConfig = (index) ->
104 | if (not isNaN(index)) and not (index == -1)
105 | configs = loadConfigs()
106 | configs.splice index, 1
107 | saveConfigs configs
108 |
109 | exports.allConfigs = allConfigs
110 | exports.saveConfig = saveConfig
111 | exports.loadConfig = loadConfig
112 | exports.deleteConfig = deleteConfig
113 | exports.loadIndex = loadIndex
114 | exports.saveIndex = saveIndex
115 | exports.publicConfig = publicConfig
116 |
--------------------------------------------------------------------------------
/src/main.coffee:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2014 clowwindy
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | # SOFTWARE.
20 |
21 | $ ->
22 | os = require 'os'
23 | gui = require 'nw.gui'
24 | # hack util.log
25 |
26 | divWarning = $('#divWarning')
27 | divWarningShown = false
28 | serverHistory = ->
29 | (localStorage['server_history'] || '').split('|')
30 |
31 | util = require 'util'
32 | util.log = (s) ->
33 | console.log new Date().toLocaleString() + " - #{s}"
34 | if not divWarningShown
35 | divWarning.show()
36 | divWarningShown = true
37 | divWarning.text(s)
38 |
39 | args = require './args'
40 | local = require 'shadowsocks'
41 | update = require './update'
42 |
43 | update.checkUpdate (url, version) ->
44 | divNewVersion = $('#divNewVersion')
45 | span = $("New version #{version} found, click here to download")
46 | span.click ->
47 | gui.Shell.openExternal url
48 | divNewVersion.find('.msg').append span
49 | divNewVersion.fadeIn()
50 |
51 | addServer = (serverIP) ->
52 | servers = (localStorage['server_history'] || '').split('|')
53 | servers.push serverIP
54 | newServers = []
55 | for server in servers
56 | if server and server not in newServers
57 | newServers.push server
58 | localStorage['server_history'] = newServers.join '|'
59 |
60 | $('#inputServerIP').typeahead
61 | source: serverHistory
62 |
63 | chooseServer = ->
64 | index = +$(this).attr('data-key')
65 | args.saveIndex(index)
66 | load false
67 | reloadServerList()
68 |
69 | reloadServerList = ->
70 | currentIndex = args.loadIndex()
71 | configs = args.allConfigs()
72 | divider = $('#serverIPMenu .insert-point')
73 | serverMenu = $('#serverIPMenu .divider')
74 | $('#serverIPMenu li.server').remove()
75 | i = 0
76 | for configName of configs
77 | if i == currentIndex
78 | menuItem = $(" #{configs[configName]} ")
79 | else
80 | menuItem = $(" #{configs[configName]} ")
81 | menuItem.find('a').click chooseServer
82 | menuItem.insertBefore(divider, serverMenu)
83 | i++
84 |
85 | addConfig = ->
86 | args.saveIndex(NaN)
87 | reloadServerList()
88 | load false
89 |
90 | deleteConfig = ->
91 | args.deleteConfig(args.loadIndex())
92 | args.saveIndex(NaN)
93 | reloadServerList()
94 | load false
95 |
96 | publicConfig = ->
97 | args.saveIndex(-1)
98 | reloadServerList()
99 | load false
100 |
101 | save = ->
102 | config = {}
103 | $('input,select').each ->
104 | key = $(this).attr 'data-key'
105 | if this.type == 'checkbox'
106 | val = this.checked
107 | else
108 | val = $(this).val()
109 | config[key] = val
110 | index = args.saveConfig(args.loadIndex(), config)
111 | args.saveIndex(index)
112 | reloadServerList()
113 | util.log 'config saved'
114 | restartServer config
115 | false
116 |
117 | load = (restart)->
118 | config = args.loadConfig(args.loadIndex())
119 | $('input,select').each ->
120 | key = $(this).attr 'data-key'
121 | val = config[key] or ''
122 | if this.type == 'checkbox'
123 | this.checked = val
124 | config[key] = this.checked
125 | else
126 | $(this).val(val)
127 | config[key] = this.value
128 | if restart
129 | restartServer config
130 |
131 | isRestarting = false
132 |
133 | restartServer = (config) ->
134 | if config.server and +config.server_port and config.password and +config.local_port and config.method
135 | if isRestarting
136 | util.log "Already restarting"
137 | return
138 | isRestarting = true
139 | start = ->
140 | try
141 | isRestarting = false
142 | util.log 'Starting shadowsocks...'
143 | console.log config
144 | window.local = local.createServer config.server, config.server_port, config.local_port, config.password, config.method, 1000 * 600, if config.share then '0.0.0.0' else '127.0.0.1'
145 | addServer config.server
146 | $('#divError').fadeOut()
147 | gui.Window.get().hide()
148 | catch e
149 | util.log e
150 | if window.local?
151 | try
152 | util.log 'Restarting shadowsocks'
153 | if window.local.address()
154 | window.local.close()
155 | setTimeout start, 1000
156 | catch e
157 | isRestarting = false
158 | util.log e
159 | else
160 | start()
161 | else
162 | $('#divError').fadeIn()
163 |
164 | qrcode = (method, password, hostname, port) ->
165 | "ss://#{btoa("#{method}:#{password}@#{hostname}:#{port}")}"
166 |
167 | $('#buttonSave').on 'click', save
168 | $('#buttonNewProfile').on 'click', addConfig
169 | $('#buttonDeleteProfile').on 'click', deleteConfig
170 | $('#buttonPublicServer').on 'click', publicConfig
171 | $('#buttonConsole').on 'click', ->
172 | gui.Window.get().showDevTools()
173 | $('#buttonAbout').on 'click', ->
174 | gui.Shell.openExternal 'https://github.com/shadowsocks/shadowsocks-gui'
175 | $("#buttonQr").on 'click', ->
176 | window.qrCode.makeCode(
177 | qrcode($('#selectMethod').val(),
178 | $('#inputPassword').val(),
179 | $('#inputServerIP').val(),
180 | $('#inputServerPort').val()),
181 | )
182 |
183 | tray = new gui.Tray icon: 'menu_icon@2x.png'
184 | menu = new gui.Menu()
185 |
186 | tray.on 'click', ->
187 | gui.Window.get().show()
188 |
189 | show = new gui.MenuItem
190 | type: 'normal'
191 | label: 'Show'
192 | click: ->
193 | gui.Window.get().show()
194 |
195 | quit = new gui.MenuItem
196 | type: 'normal'
197 | label: 'Quit'
198 | click: ->
199 | gui.Window.get().close(true)
200 |
201 | hide = new gui.MenuItem
202 | type: 'normal'
203 | label: 'Hide'
204 | click: ->
205 | gui.Window.get().hide()
206 |
207 | menu.append show
208 | menu.append hide
209 | menu.append quit
210 | tray.menu = menu
211 | window.tray = tray
212 |
213 | win = gui.Window.get()
214 |
215 | win.on 'minimize', ->
216 | this.hide()
217 |
218 | win.on 'close', (quit) ->
219 | if os.platform() == 'darwin' and not quit
220 | this.hide()
221 | else
222 | this.close true
223 |
224 | reloadServerList()
225 | load true
226 |
--------------------------------------------------------------------------------
/src/update.coffee:
--------------------------------------------------------------------------------
1 | util = require 'util'
2 |
3 | platformMap =
4 | 'win32': 'win'
5 | 'darwin': 'osx'
6 | 'linux': 'linux'
7 |
8 | compareVersion = (l, r) ->
9 | # compare two version numbers
10 | ls = l.split '.'
11 | rs = r.split '.'
12 | for i in [0..Math.min(ls.length, rs.length)]
13 | lp = ls[i]
14 | rp = rs[i]
15 | if lp != rp
16 | return lp - rp
17 | return ls.length - rs.length
18 |
19 | checkUpdate = (callback) ->
20 | if callback?
21 | try
22 | packageInfo = require('./package.json')
23 | catch e
24 | util.log e
25 | return
26 | version = packageInfo.version
27 | arch = process.arch
28 | platform = platformMap[process.platform]
29 | # jQuery works well with node-webkit
30 | $ = window.$;
31 | re = /^.*shadowsocks-gui-([\d\.]+)-(\w+)-(\w+)\..*$/
32 | $.get('https://sourceforge.net/api/file/index/project-id/1817190/path/dist/mtime/desc/limit/4/rss',(data) ->
33 | results = []
34 | $(data).find('content').each ->
35 | url = $(this).attr('url')
36 | g = re.exec(url)
37 | if g?
38 | results.push g
39 | # sort versions desc
40 | results.sort (l, r) ->
41 | -compareVersion(l[1], r[1])
42 | # pick latest version
43 | for r in results
44 | if (r[2] == platform) and (r[3] == arch)
45 | if compareVersion(r[1], version) > 0
46 | callback r[0], r[1]
47 | return
48 | ).fail(->
49 | alert("error")
50 | )
51 |
52 | exports.checkUpdate = checkUpdate
53 |
--------------------------------------------------------------------------------
/update.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.8.0
2 | (function() {
3 | var checkUpdate, compareVersion, platformMap, util;
4 |
5 | util = require('util');
6 |
7 | platformMap = {
8 | 'win32': 'win',
9 | 'darwin': 'osx',
10 | 'linux': 'linux'
11 | };
12 |
13 | compareVersion = function(l, r) {
14 | var i, lp, ls, rp, rs, _i, _ref;
15 | ls = l.split('.');
16 | rs = r.split('.');
17 | for (i = _i = 0, _ref = Math.min(ls.length, rs.length); 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
18 | lp = ls[i];
19 | rp = rs[i];
20 | if (lp !== rp) {
21 | return lp - rp;
22 | }
23 | }
24 | return ls.length - rs.length;
25 | };
26 |
27 | checkUpdate = function(callback) {
28 | var $, arch, e, packageInfo, platform, re, version;
29 | if (callback != null) {
30 | try {
31 | packageInfo = require('./package.json');
32 | } catch (_error) {
33 | e = _error;
34 | util.log(e);
35 | return;
36 | }
37 | version = packageInfo.version;
38 | arch = process.arch;
39 | platform = platformMap[process.platform];
40 | $ = window.$;
41 | re = /^.*shadowsocks-gui-([\d\.]+)-(\w+)-(\w+)\..*$/;
42 | return $.get('https://sourceforge.net/api/file/index/project-id/1817190/path/dist/mtime/desc/limit/4/rss', function(data) {
43 | var r, results, _i, _len;
44 | results = [];
45 | $(data).find('content').each(function() {
46 | var g, url;
47 | url = $(this).attr('url');
48 | g = re.exec(url);
49 | if (g != null) {
50 | return results.push(g);
51 | }
52 | });
53 | results.sort(function(l, r) {
54 | return -compareVersion(l[1], r[1]);
55 | });
56 | for (_i = 0, _len = results.length; _i < _len; _i++) {
57 | r = results[_i];
58 | if ((r[2] === platform) && (r[3] === arch)) {
59 | if (compareVersion(r[1], version) > 0) {
60 | callback(r[0], r[1]);
61 | return;
62 | }
63 | }
64 | }
65 | }).fail(function() {
66 | return alert("error");
67 | });
68 | }
69 | };
70 |
71 | exports.checkUpdate = checkUpdate;
72 |
73 | }).call(this);
74 |
--------------------------------------------------------------------------------
/utils/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | shadowsocks
9 | CFBundleDocumentTypes
10 |
11 | CFBundleExecutable
12 | node-webkit
13 | CFBundleIconFile
14 | shadowsocks.icns
15 | CFBundleIdentifier
16 | org.shadowsocks
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundleName
20 | shadowsocks
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | 1.0
25 | CFBundleVersion
26 | 1.0
27 | LSFileQuarantineEnabled
28 |
29 | LSMinimumSystemVersion
30 | 10.6.0
31 | NSPrincipalClass
32 | NSApplication
33 | NSSupportsAutomaticGraphicsSwitching
34 |
35 | SCMRevision
36 | 199640
37 | UTExportedTypeDeclarations
38 |
39 |
40 | UTTypeConformsTo
41 |
42 | com.pkware.zip-archive
43 |
44 | UTTypeDescription
45 | node-webkit App
46 | UTTypeIconFile
47 | nw.icns
48 | UTTypeIdentifier
49 | com.intel.nw.app
50 | UTTypeReferenceURL
51 | https://github.com/rogerwang/node-webkit/wiki/How-to-package-and-distribute-your-apps
52 | UTTypeTagSpecification
53 |
54 | com.apple.ostype
55 | node-webkit
56 | public.filename-extension
57 |
58 | nw
59 |
60 | public.mime-type
61 | application/x-node-webkit-app
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/utils/build.sh:
--------------------------------------------------------------------------------
1 | NW_VERSION=v0.8.6
2 | if [ $# == 0 ]; then
3 | echo 'usage: build.sh version'
4 | exit 1
5 | fi
6 | pushd `dirname $0`
7 | cd ..
8 | mkdir -p dist
9 | cd dist
10 | rm -rf app
11 | mkdir app
12 | pushd app && \
13 | cp ../../*.js . && \
14 | cp -r ../../css . && \
15 | cp -r ../../img . && \
16 | cp ../../*.json . && \
17 | cp ../../*.htm* . && \
18 | cp ../../*.png . && \
19 | cp -r ../../fonts . && \
20 | cp -r ../../node_modules . || \
21 | exit 1
22 | rm ../app.nw
23 | zip -r ../app.nw * && \
24 | popd && \
25 | rm -rf app || \
26 | exit 1
27 | for platform in linux-x64 win-ia32
28 | do
29 | if [ -f shadowsocks-gui-$1-$platform.tar.xz ]; then
30 | continue
31 | fi
32 | if [ ! -f node-webkit-$NW_VERSION-$platform.zip ] ; then
33 | if [ ! -f node-webkit-$NW_VERSION-$platform.tar.gz ] ; then
34 | axel http://dl.node-webkit.org/$NW_VERSION/node-webkit-$NW_VERSION-$platform.zip || \
35 | axel http://dl.node-webkit.org/$NW_VERSION/node-webkit-$NW_VERSION-$platform.tar.gz || \
36 | exit 1
37 | fi
38 | fi
39 | mkdir shadowsocks-gui-$1-$platform && \
40 | pushd shadowsocks-gui-$1-$platform && \
41 | unzip ../node-webkit-$NW_VERSION-$platform.zip || \
42 | tar xf ../node-webkit-$NW_VERSION-$platform.tar.gz || \
43 | exit 1
44 | if [ -d node-webkit-$NW_VERSION-$platform ]; then
45 | mv node-webkit-$NW_VERSION-$platform/* ./ && \
46 | rm -r node-webkit-$NW_VERSION-$platform || \
47 | exit 1
48 | fi
49 | if [ $platform == win-ia32 ]; then
50 | cat nw.exe ../app.nw > shadowsocks.exe && \
51 | rm nwsnapshot.exe && \
52 | rm ffmpegsumo.dll && \
53 | rm libEGL.dll && \
54 | rm libGLESv2.dll && \
55 | rm nw.exe || \
56 | exit 1
57 | fi
58 | if [ $platform == osx-ia32 ]; then
59 | rm nwsnapshot && \
60 | cp ../app.nw node-webkit.app/Contents/Resources/ && \
61 | cp ../../utils/Info.plist node-webkit.app/Contents/ && \
62 | cp ../../utils/*.icns node-webkit.app/Contents/Resources/ && \
63 | /usr/libexec/PlistBuddy -c "Set CFBundleVersion $1" node-webkit.app/Contents/Info.plist && \
64 | /usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $1" node-webkit.app/Contents/Info.plist && \
65 | mv node-webkit.app shadowsocks.app || \
66 | exit 1
67 | fi
68 | if [ $platform == linux-x64 ]; then
69 | rm nwsnapshot && \
70 | cp ../app.nw . && \
71 | cp ../../utils/linux/start.sh . && \
72 | rm libffmpegsumo.so || \
73 | exit 1
74 | fi
75 | popd && \
76 | tar Jcf shadowsocks-gui-$1-$platform.tar.xz shadowsocks-gui-$1-$platform && \
77 | rm -r shadowsocks-gui-$1-$platform && \
78 | rsync --progress -e ssh shadowsocks-gui-$1-$platform.tar.xz frs.sourceforge.net:/home/frs/project/shadowsocksgui/dist/shadowsocks-gui-$1-$platform.tar.xz || \
79 | exit 1
80 | done
81 | popd
82 |
--------------------------------------------------------------------------------
/utils/linux/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd "`dirname $0`"
4 |
5 | paths=(
6 | "/lib/x86_64-linux-gnu/libudev.so.1" # Ubuntu, Xubuntu, Mint
7 | "/usr/lib64/libudev.so.1" # SUSE, Fedora
8 | "/usr/lib/libudev.so.1" # Arch, Fedora 32bit
9 | "/lib/i386-linux-gnu/libudev.so.1" # Ubuntu 32bit
10 | )
11 | mkdir -p /tmp/shadowsocks-gui
12 | for i in "${paths[@]}"
13 | do
14 | if [ -f $i ]
15 | then
16 | ln -sf "$i" /tmp/shadowsocks-gui/libudev.so.0
17 | break
18 | fi
19 | done
20 | LD_LIBRARY_PATH=/tmp/shadowsocks-gui/ $PWD/nw app.nw
21 |
--------------------------------------------------------------------------------
/utils/make_icon.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | LAST=`pwd`
4 | pushd /tmp
5 | mkdir icon.iconset
6 | sips -z 16 16 $LAST/icon.png --out icon.iconset/icon_16x16.png
7 | sips -z 32 32 $LAST/icon.png --out icon.iconset/icon_16x16@2x.png
8 | sips -z 32 32 $LAST/icon.png --out icon.iconset/icon_32x32.png
9 | sips -z 64 64 $LAST/icon.png --out icon.iconset/icon_32x32@2x.png
10 | sips -z 128 128 $LAST/icon.png --out icon.iconset/icon_128x128.png
11 | sips -z 256 256 $LAST/icon.png --out icon.iconset/icon_128x128@2x.png
12 | sips -z 256 256 $LAST/icon.png --out icon.iconset/icon_256x256.png
13 | sips -z 512 512 $LAST/icon.png --out icon.iconset/icon_256x256@2x.png
14 | sips -z 512 512 $LAST/icon.png --out icon.iconset/icon_512x512.png
15 | cp $LAST/icon.png icon.iconset/icon_512x512@2x.png
16 | iconutil -c icns icon.iconset
17 | rm -R icon.iconset
18 | popd
19 | mv /tmp/icon.icns utils/shadowsocks.icns
--------------------------------------------------------------------------------
/utils/shadowsocks.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Long-live-shadowsocks/shadowsocks-gui/069dd20c00d9f188bd1994b4c4b4f6aa177b8492/utils/shadowsocks.icns
--------------------------------------------------------------------------------