Browse Source

整理目录。

oldj 9 years ago
parent
commit
8342fb81e7
100 changed files with 284 additions and 1266 deletions
  1. BIN
      app/SH3/SwitchHosts!.xcodeproj/project.xcworkspace/xcuserdata/Tim.xcuserdatad/UserInterfaceState.xcuserstate
  2. 0 23
      app/SH3/SwitchHosts!.xcodeproj/xcuserdata/Tim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
  3. 0 0
      app/build/bundle.js
  4. 0 331
      app/electron/src/agent.js
  5. 0 17
      app/electron/src/ui.js
  6. 0 5
      app/fe/.babelrc
  7. 0 9
      app/fe/.editorconfig
  8. 0 2
      app/fe/.eslintignore
  9. 0 7
      app/fe/.gitignore
  10. 0 27
      app/fe/README.md
  11. 0 35
      app/fe/build/build.js
  12. 0 9
      app/fe/build/dev-client.js
  13. 0 65
      app/fe/build/dev-server.js
  14. 0 56
      app/fe/build/utils.js
  15. 0 68
      app/fe/build/webpack.base.conf.js
  16. 0 34
      app/fe/build/webpack.dev.conf.js
  17. 0 102
      app/fe/build/webpack.prod.conf.js
  18. 0 6
      app/fe/config/dev.env.js
  19. 0 32
      app/fe/config/index.js
  20. 0 3
      app/fe/config/prod.env.js
  21. 0 6
      app/fe/config/test.env.js
  22. 0 11
      app/fe/index.html
  23. 0 47
      app/fe/package.json
  24. 0 46
      app/fe/src/App.vue
  25. BIN
      app/fe/src/assets/logo.png
  26. 0 26
      app/fe/src/components/Hello.vue
  27. 0 8
      app/fe/src/main.js
  28. 0 0
      app/fe/static/.gitkeep
  29. 1 1
      app/gulpfile.js
  30. 0 0
      app/index.html
  31. 0 0
      app/main.js
  32. 5 2
      app/package.json
  33. 253 223
      app/src/agent.js
  34. 0 0
      app/src/assets/app.icns
  35. 0 0
      app/src/assets/icon_0.pdf
  36. 0 0
      app/src/assets/icon_1.pdf
  37. 0 0
      app/src/assets/icon_2.pdf
  38. 0 0
      app/src/assets/ilogoTemplate.png
  39. 0 0
      app/src/assets/[email protected]
  40. 0 0
      app/src/assets/[email protected]
  41. 0 0
      app/src/assets/logo.png
  42. 0 0
      app/src/assets/[email protected]
  43. 0 0
      app/src/assets/[email protected]
  44. 0 0
      app/src/assets/[email protected]
  45. 0 0
      app/src/assets/logoTemplate.png
  46. 0 0
      app/src/assets/[email protected]
  47. 0 0
      app/src/assets/[email protected]
  48. 0 0
      app/src/assets/[email protected]
  49. 0 0
      app/src/assets/logo_512.png
  50. 0 0
      app/src/components/app.js
  51. 0 0
      app/src/components/app.less
  52. 0 0
      app/src/components/cfg.less
  53. 0 0
      app/src/components/content/cm_hl.js
  54. 0 0
      app/src/components/content/content.js
  55. 0 0
      app/src/components/content/content.less
  56. 0 0
      app/src/components/content/editor.js
  57. 0 0
      app/src/components/content/editor.less
  58. 0 0
      app/src/components/frame/edit.js
  59. 0 0
      app/src/components/frame/edit.less
  60. 0 0
      app/src/components/frame/frame.js
  61. 0 0
      app/src/components/frame/frame.less
  62. 0 0
      app/src/components/frame/sudo.js
  63. 0 0
      app/src/components/frame/sudo.less
  64. 0 0
      app/src/components/panel/buttons.js
  65. 0 0
      app/src/components/panel/buttons.less
  66. 0 0
      app/src/components/panel/iconfont/iconfont.css
  67. 0 0
      app/src/components/panel/iconfont/iconfont.eot
  68. 0 0
      app/src/components/panel/iconfont/iconfont.svg
  69. 0 0
      app/src/components/panel/iconfont/iconfont.ttf
  70. 0 0
      app/src/components/panel/iconfont/iconfont.woff
  71. 0 0
      app/src/components/panel/list.js
  72. 0 0
      app/src/components/panel/list.less
  73. 0 0
      app/src/components/panel/list_item.js
  74. 0 0
      app/src/components/panel/list_item.less
  75. 0 0
      app/src/components/panel/panel.js
  76. 0 0
      app/src/components/panel/panel.less
  77. 0 0
      app/src/components/panel/searchbar.js
  78. 0 0
      app/src/components/panel/searchbar.less
  79. 0 0
      app/src/configs.js
  80. 0 0
      app/src/event.js
  81. 17 3
      app/src/lang.js
  82. 0 0
      app/src/libs/default_data.js
  83. 0 0
      app/src/libs/io.js
  84. 0 0
      app/src/libs/kw.js
  85. 0 0
      app/src/libs/lang.js
  86. 0 0
      app/src/libs/paths.js
  87. 0 0
      app/src/libs/pref.js
  88. 0 0
      app/src/libs/util.js
  89. 0 0
      app/src/mock.js
  90. 0 0
      app/src/modules/mainMenu.js
  91. 0 0
      app/src/modules/stat.js
  92. 0 0
      app/src/modules/tray.js
  93. 8 62
      app/src/ui.js
  94. 0 0
      app/src/version.js
  95. 0 0
      app/webpack.config.js
  96. 0 0
      legacy/v3.1_macgap/app/SH3/.gitignore
  97. 0 0
      legacy/v3.1_macgap/app/SH3/MacGap/AppDelegate.h
  98. 0 0
      legacy/v3.1_macgap/app/SH3/MacGap/AppDelegate.m
  99. 0 0
      legacy/v3.1_macgap/app/SH3/MacGap/Base.lproj/MainMenu.xib
  100. 0 0
      legacy/v3.1_macgap/app/SH3/MacGap/Base.lproj/MainWindow.xib

BIN
app/SH3/SwitchHosts!.xcodeproj/project.xcworkspace/xcuserdata/Tim.xcuserdatad/UserInterfaceState.xcuserstate


+ 0 - 23
app/SH3/SwitchHosts!.xcodeproj/xcuserdata/Tim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Bucket
-   type = "1"
-   version = "2.0">
-   <Breakpoints>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "MG/Classes/WindowController.m"
-            timestampString = "422913472.060474"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "82"
-            endingLineNumber = "82"
-            landmarkName = "-awakeFromNib"
-            landmarkType = "5">
-         </BreakpointContent>
-      </BreakpointProxy>
-   </Breakpoints>
-</Bucket>

+ 0 - 0
app/electron/build/bundle.js → app/build/bundle.js


+ 0 - 331
app/electron/src/agent.js

@@ -1,331 +0,0 @@
-/**
- * @author oldj
- * @blog http://oldj.net
- *
- * 和系统、平台相关的方法
- */
-
-'use strict';
-
-const fs = require('fs');
-const path = require('path');
-const request = require('request');
-const moment = require('moment');
-const notifier = require('node-notifier');
-const util = require('./libs/util');
-const platform = process.platform;
-
-const paths = require('./libs/paths');
-const pref = require('./libs/pref');
-const sys_host_path = paths.sys_host_path;
-const work_path = paths.work_path;
-const data_path = paths.data_path;
-const preference_path = paths.preference_path;
-
-const exec = require('child_process').exec;
-const stat = require('./modules/stat');
-stat.init();
-
-const crypto = require('crypto');
-function md5 (text) {
-    return crypto.createHash('md5').update(text).digest('hex');
-}
-
-
-const m_lang = require('./lang');
-let sudo_pswd = '';
-
-function getUserLang() {
-    let user_lang;
-
-    user_lang = pref.get('user_language') || navigator.language || navigator.userLanguage;
-    if (user_lang === 'zh_CN') {
-        user_lang = 'cn';
-    } else {
-        user_lang = 'en';
-    }
-
-    return user_lang;
-}
-
-const lang = m_lang.getLang(getUserLang());
-
-
-function getSysHosts() {
-    let cnt = '';
-
-    try {
-        cnt = fs.readFileSync(sys_host_path, 'utf-8');
-    } catch (e) {
-        console.log(e.message);
-    }
-
-    return cnt;
-}
-
-function tryToCreateWorkDir() {
-    if (util.isDirectory((work_path))) {
-        console.log('work dir exists.');
-        return;
-    }
-
-    console.log(`try to create work directory: ${work_path}`);
-    try {
-        fs.mkdirSync(work_path);
-        console.log('work directory created.');
-    } catch (e) {
-        alert('Fail to create work directory!');
-    }
-}
-
-function saveData(content) {
-
-    let txt = JSON.stringify({
-        list: content
-    });
-
-    fs.writeFile(data_path, txt, 'utf-8', (error) => {
-        if (error) {
-            alert(error.message);
-        }
-    });
-}
-
-
-function apply_UNIX(content, success) {
-    let tmp_fn = path.join(work_path, 'tmp.txt');
-    if (content) {
-        fs.writeFileSync(tmp_fn, content, 'utf-8');
-    }
-
-    let cmd;
-    if (!sudo_pswd) {
-        cmd = [
-            'cat "' + tmp_fn + '" > ' + sys_host_path
-            , 'rm -rf ' + tmp_fn
-        ].join(' && ');
-    } else {
-        sudo_pswd = sudo_pswd.replace(/'/g, '\\x27');
-        cmd = [
-            'echo \'' + sudo_pswd + '\' | sudo -S chmod 777 ' + sys_host_path
-            , 'cat "' + tmp_fn + '" > ' + sys_host_path
-            , 'echo \'' + sudo_pswd + '\' | sudo -S chmod 644 ' + sys_host_path
-            // , 'rm -rf ' + tmp_fn
-        ].join(' && ');
-    }
-
-    exec(cmd, function(error, stdout, stderr) {
-        // command output is in stdout
-        if (error) {
-            if (!sudo_pswd) {
-                // 尝试让用户输入管理密码
-                SH_event.emit('sudo_prompt', (pswd) => {
-                    sudo_pswd = pswd;
-                    tryToApply(null, success);
-                });
-            } else {
-                alert(stderr);
-            }
-            return;
-        }
-
-        if (!error) {
-            after_apply(success);
-        }
-    });
-}
-
-function _after_apply_unix(callback) {
-    let cmd_fn = path.join(work_path, '_restart_mDNSResponder.sh');
-
-    let cmd = [
-        'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
-        , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
-        , 'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
-        , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
-        , 'sudo killall -HUP mDNSResponder'
-    ].join('\n');
-
-    fs.writeFileSync(cmd_fn, cmd, 'utf-8');
-
-    exec(`/bin/sh ${cmd_fn}`, function(error, stdout, stderr) {
-        // command output is in stdout
-        if (error) {
-            console.log(error);
-        }
-        console.log(stdout, stderr);
-
-        callback();
-    });
-}
-
-function after_apply(callback) {
-
-    SH_event.emit('after_apply');
-
-    if (!sudo_pswd) {
-        callback();
-        return;
-    }
-
-    if (platform === 'darwin') {
-        _after_apply_unix(callback);
-        return;
-    }
-
-    callback();
-}
-
-function apply_Win32(content, success) {
-    // todo 判断写入权限
-    try {
-        fs.writeFileSync(sys_host_path, content, 'utf-8');
-    } catch (e) {
-        console.log(e);
-        let msg = e.message;
-        if (platform === 'win32') {
-            msg = `${msg}\n\n${lang.please_run_as_admin}`;
-        }
-        alert(msg);
-        return;
-    }
-    success && success();
-
-    // todo 更新 DNS 缓存
-}
-
-
-function tryToApply(content, success) {
-
-    if (platform !== 'win32') {
-        apply_UNIX(content, success);
-    } else {
-        apply_Win32(content, success);
-    }
-}
-
-
-// init
-tryToCreateWorkDir();
-
-SH_event.on('test', () => {
-    console.log('ttt');
-});
-
-SH_event.on('apply', (content, success) => {
-    success = success || function () {};
-    tryToApply(content, success);
-});
-
-SH_event.on('sudo_pswd', (pswd) => {
-    sudo_pswd = pswd;
-});
-
-SH_event.on('save_data', (content) => {
-    saveData(content);
-    ipcRenderer.send('send_host_list', content);
-});
-
-SH_event.on('check_host_refresh', (host, force=false) => {
-    if (host.where !== 'remote' || !host.url || !host.refresh_interval) return;
-
-    let last_refresh = host.last_refresh;
-    let refresh_interval = parseInt(host.refresh_interval) || 0;
-    if (last_refresh && !force) {
-        last_refresh = new Date(last_refresh);
-        let delta = (new Date()).getTime() - (last_refresh.getTime() || 0) / (1000 * 3600);
-        if (delta < refresh_interval) {
-            return;
-        }
-    }
-
-    // refresh
-    console.log(`getting '${host.url}' ..`);
-    SH_event.emit('loading', host, true);
-    host.is_loading = true;
-    request(host.url, (err, res, body) => {
-        console.log(err, res.statusCode);
-        SH_event.emit('loading', host, false);
-        host.is_loading = false;
-        if (!err && res.statusCode === 200) {
-            // console.log(body);
-            host.content = body;
-            host.last_refresh = moment().format('YYYY-MM-DD HH:mm:ss');
-
-            SH_event.emit('change');
-        } else {
-            console.log(err, res.statusCode);
-        }
-    });
-});
-
-/**
- * 如果本地没有 data 文件,认为是第一次运行
- */
-function initGet() {
-    let dd = require('./libs/default_data');
-    let data = dd.make();
-
-    data.sys.content = getSysHosts();
-    data.list.push({
-        title: 'backup',
-        content: data.sys.content
-    });
-
-    return data;
-}
-
-module.exports = {
-    md5: md5,
-    getHosts: function () {
-        let data = null;
-
-        if (!util.isFile(data_path)) {
-            return initGet();
-        }
-
-        try {
-            let cnt = fs.readFileSync(data_path, 'utf-8');
-            data = JSON.parse(cnt);
-        } catch (e) {
-            console.log(e);
-            alert('bad data file.. :(');
-            return initGet();
-        }
-
-        return {
-            sys: {
-                is_sys: true
-                , content: getSysHosts()
-            },
-            list: data.list.map((i) => {
-                return {
-                    title: i.title || ''
-                    , content: i.content || ''
-                    , on: !!i.on
-                    , where: i.where || 'local'
-                    , url: i.url || ''
-                    , last_refresh: i.last_refresh || null
-                    , refresh_interval: i.refresh_interval || 0
-                }
-            })
-        };
-    },
-    getSysHosts: function () {
-        return {
-            is_sys: true
-            , content: getSysHosts()
-        }
-    },
-    readFile: function (fn, callback) {
-        fs.readFile(fn, 'utf-8', callback);
-    },
-    notify: (options) => {
-        notifier.notify(Object.assign({
-            title: 'SwitchHosts!',
-            message: '',
-            icon: path.join(__dirname, 'assets', 'logo_512.png')
-        }, options));
-    },
-    lang: lang
-};

+ 0 - 17
app/electron/src/ui.js

@@ -1,17 +0,0 @@
-/**
- * @author oldj
- * @blog http://oldj.net
- */
-
-'use strict';
-
-import React from 'react';
-import ReactDom from 'react-dom';
-import App from './components/app';
-
-ipcRenderer.setMaxListeners(20);
-
-ReactDom.render(
-    <App/>
-    , document.getElementById('app')
-);

+ 0 - 5
app/fe/.babelrc

@@ -1,5 +0,0 @@
-{
-  "presets": ["es2015", "stage-2"],
-  "plugins": ["transform-runtime"],
-  "comments": false
-}

+ 0 - 9
app/fe/.editorconfig

@@ -1,9 +0,0 @@
-root = true
-
-[*]
-charset = utf-8
-indent_style = space
-indent_size = 2
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true

+ 0 - 2
app/fe/.eslintignore

@@ -1,2 +0,0 @@
-build/*.js
-config/*.js

+ 0 - 7
app/fe/.gitignore

@@ -1,7 +0,0 @@
-.DS_Store
-node_modules/
-dist/
-npm-debug.log
-selenium-debug.log
-test/unit/coverage
-test/e2e/reports

+ 0 - 27
app/fe/README.md

@@ -1,27 +0,0 @@
-# switchhosts
-
-> Switch hosts quickly!
-
-## Build Setup
-
-``` bash
-# install dependencies
-npm install
-
-# serve with hot reload at localhost:8080
-npm run dev
-
-# build for production with minification
-npm run build
-
-# run unit tests
-npm run unit
-
-# run e2e tests
-npm run e2e
-
-# run all tests
-npm test
-```
-
-For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).

+ 0 - 35
app/fe/build/build.js

@@ -1,35 +0,0 @@
-// https://github.com/shelljs/shelljs
-require('shelljs/global')
-env.NODE_ENV = 'production'
-
-var path = require('path')
-var config = require('../config')
-var ora = require('ora')
-var webpack = require('webpack')
-var webpackConfig = require('./webpack.prod.conf')
-
-console.log(
-  '  Tip:\n' +
-  '  Built files are meant to be served over an HTTP server.\n' +
-  '  Opening index.html over file:// won\'t work.\n'
-)
-
-var spinner = ora('building for production...')
-spinner.start()
-
-var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
-rm('-rf', assetsPath)
-mkdir('-p', assetsPath)
-cp('-R', 'static/', assetsPath)
-
-webpack(webpackConfig, function (err, stats) {
-  spinner.stop()
-  if (err) throw err
-  process.stdout.write(stats.toString({
-    colors: true,
-    modules: false,
-    children: false,
-    chunks: false,
-    chunkModules: false
-  }) + '\n')
-})

+ 0 - 9
app/fe/build/dev-client.js

@@ -1,9 +0,0 @@
-/* eslint-disable */
-require('eventsource-polyfill')
-var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
-
-hotClient.subscribe(function (event) {
-  if (event.action === 'reload') {
-    window.location.reload()
-  }
-})

+ 0 - 65
app/fe/build/dev-server.js

@@ -1,65 +0,0 @@
-var path = require('path')
-var express = require('express')
-var webpack = require('webpack')
-var config = require('../config')
-var proxyMiddleware = require('http-proxy-middleware')
-var webpackConfig = process.env.NODE_ENV === 'testing'
-  ? require('./webpack.prod.conf')
-  : require('./webpack.dev.conf')
-
-// default port where dev server listens for incoming traffic
-var port = process.env.PORT || config.dev.port
-// Define HTTP proxies to your custom API backend
-// https://github.com/chimurai/http-proxy-middleware
-var proxyTable = config.dev.proxyTable
-
-var app = express()
-var compiler = webpack(webpackConfig)
-
-var devMiddleware = require('webpack-dev-middleware')(compiler, {
-  publicPath: webpackConfig.output.publicPath,
-  stats: {
-    colors: true,
-    chunks: false
-  }
-})
-
-var hotMiddleware = require('webpack-hot-middleware')(compiler)
-// force page reload when html-webpack-plugin template changes
-compiler.plugin('compilation', function (compilation) {
-  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
-    hotMiddleware.publish({ action: 'reload' })
-    cb()
-  })
-})
-
-// proxy api requests
-Object.keys(proxyTable).forEach(function (context) {
-  var options = proxyTable[context]
-  if (typeof options === 'string') {
-    options = { target: options }
-  }
-  app.use(proxyMiddleware(context, options))
-})
-
-// handle fallback for HTML5 history API
-app.use(require('connect-history-api-fallback')())
-
-// serve webpack bundle output
-app.use(devMiddleware)
-
-// enable hot-reload and state-preserving
-// compilation error display
-app.use(hotMiddleware)
-
-// serve pure static assets
-var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
-app.use(staticPath, express.static('./static'))
-
-module.exports = app.listen(port, function (err) {
-  if (err) {
-    console.log(err)
-    return
-  }
-  console.log('Listening at http://localhost:' + port + '\n')
-})

+ 0 - 56
app/fe/build/utils.js

@@ -1,56 +0,0 @@
-var path = require('path')
-var config = require('../config')
-var ExtractTextPlugin = require('extract-text-webpack-plugin')
-
-exports.assetsPath = function (_path) {
-  return path.posix.join(config.build.assetsSubDirectory, _path)
-}
-
-exports.cssLoaders = function (options) {
-  options = options || {}
-  // generate loader string to be used with extract text plugin
-  function generateLoaders (loaders) {
-    var sourceLoader = loaders.map(function (loader) {
-      var extraParamChar
-      if (/\?/.test(loader)) {
-        loader = loader.replace(/\?/, '-loader?')
-        extraParamChar = '&'
-      } else {
-        loader = loader + '-loader'
-        extraParamChar = '?'
-      }
-      return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
-    }).join('!')
-
-    if (options.extract) {
-      return ExtractTextPlugin.extract('vue-style-loader', sourceLoader)
-    } else {
-      return ['vue-style-loader', sourceLoader].join('!')
-    }
-  }
-
-  // http://vuejs.github.io/vue-loader/configurations/extract-css.html
-  return {
-    css: generateLoaders(['css']),
-    postcss: generateLoaders(['css']),
-    less: generateLoaders(['css', 'less']),
-    sass: generateLoaders(['css', 'sass?indentedSyntax']),
-    scss: generateLoaders(['css', 'sass']),
-    stylus: generateLoaders(['css', 'stylus']),
-    styl: generateLoaders(['css', 'stylus'])
-  }
-}
-
-// Generate loaders for standalone style files (outside of .vue)
-exports.styleLoaders = function (options) {
-  var output = []
-  var loaders = exports.cssLoaders(options)
-  for (var extension in loaders) {
-    var loader = loaders[extension]
-    output.push({
-      test: new RegExp('\\.' + extension + '$'),
-      loader: loader
-    })
-  }
-  return output
-}

+ 0 - 68
app/fe/build/webpack.base.conf.js

@@ -1,68 +0,0 @@
-var path = require('path')
-var config = require('../config')
-var utils = require('./utils')
-var projectRoot = path.resolve(__dirname, '../')
-
-module.exports = {
-  entry: {
-    app: './src/main.js'
-  },
-  output: {
-    path: config.build.assetsRoot,
-    publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
-    filename: '[name].js'
-  },
-  resolve: {
-    extensions: ['', '.js', '.vue'],
-    fallback: [path.join(__dirname, '../node_modules')],
-    alias: {
-      'src': path.resolve(__dirname, '../src'),
-      'assets': path.resolve(__dirname, '../src/assets'),
-      'components': path.resolve(__dirname, '../src/components')
-    }
-  },
-  resolveLoader: {
-    fallback: [path.join(__dirname, '../node_modules')]
-  },
-  module: {
-    loaders: [
-      {
-        test: /\.vue$/,
-        loader: 'vue'
-      },
-      {
-        test: /\.js$/,
-        loader: 'babel',
-        include: projectRoot,
-        exclude: /node_modules/
-      },
-      {
-        test: /\.json$/,
-        loader: 'json'
-      },
-      {
-        test: /\.html$/,
-        loader: 'vue-html'
-      },
-      {
-        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
-        loader: 'url',
-        query: {
-          limit: 10000,
-          name: utils.assetsPath('img/[name].[hash:7].[ext]')
-        }
-      },
-      {
-        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
-        loader: 'url',
-        query: {
-          limit: 10000,
-          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
-        }
-      }
-    ]
-  },
-  vue: {
-    loaders: utils.cssLoaders()
-  }
-}

+ 0 - 34
app/fe/build/webpack.dev.conf.js

@@ -1,34 +0,0 @@
-var config = require('../config')
-var webpack = require('webpack')
-var merge = require('webpack-merge')
-var utils = require('./utils')
-var baseWebpackConfig = require('./webpack.base.conf')
-var HtmlWebpackPlugin = require('html-webpack-plugin')
-
-// add hot-reload related code to entry chunks
-Object.keys(baseWebpackConfig.entry).forEach(function (name) {
-  baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
-})
-
-module.exports = merge(baseWebpackConfig, {
-  module: {
-    loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
-  },
-  // eval-source-map is faster for development
-  devtool: '#eval-source-map',
-  plugins: [
-    new webpack.DefinePlugin({
-      'process.env': config.dev.env
-    }),
-    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
-    new webpack.optimize.OccurenceOrderPlugin(),
-    new webpack.HotModuleReplacementPlugin(),
-    new webpack.NoErrorsPlugin(),
-    // https://github.com/ampedandwired/html-webpack-plugin
-    new HtmlWebpackPlugin({
-      filename: 'index.html',
-      template: 'index.html',
-      inject: true
-    })
-  ]
-})

+ 0 - 102
app/fe/build/webpack.prod.conf.js

@@ -1,102 +0,0 @@
-var path = require('path')
-var config = require('../config')
-var utils = require('./utils')
-var webpack = require('webpack')
-var merge = require('webpack-merge')
-var baseWebpackConfig = require('./webpack.base.conf')
-var ExtractTextPlugin = require('extract-text-webpack-plugin')
-var HtmlWebpackPlugin = require('html-webpack-plugin')
-var env = process.env.NODE_ENV === 'testing'
-  ? require('../config/test.env')
-  : config.build.env
-
-var webpackConfig = merge(baseWebpackConfig, {
-  module: {
-    loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true })
-  },
-  devtool: config.build.productionSourceMap ? '#source-map' : false,
-  output: {
-    path: config.build.assetsRoot,
-    filename: utils.assetsPath('js/[name].[chunkhash].js'),
-    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
-  },
-  vue: {
-    loaders: utils.cssLoaders({
-      sourceMap: config.build.productionSourceMap,
-      extract: true
-    })
-  },
-  plugins: [
-    // http://vuejs.github.io/vue-loader/workflow/production.html
-    new webpack.DefinePlugin({
-      'process.env': env
-    }),
-    new webpack.optimize.UglifyJsPlugin({
-      compress: {
-        warnings: false
-      }
-    }),
-    new webpack.optimize.OccurenceOrderPlugin(),
-    // extract css into its own file
-    new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
-    // generate dist index.html with correct asset hash for caching.
-    // you can customize output by editing /index.html
-    // see https://github.com/ampedandwired/html-webpack-plugin
-    new HtmlWebpackPlugin({
-      filename: process.env.NODE_ENV === 'testing'
-        ? 'index.html'
-        : config.build.index,
-      template: 'index.html',
-      inject: true,
-      minify: {
-        removeComments: true,
-        collapseWhitespace: true,
-        removeAttributeQuotes: true
-        // more options:
-        // https://github.com/kangax/html-minifier#options-quick-reference
-      },
-      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
-      chunksSortMode: 'dependency'
-    }),
-    // split vendor js into its own file
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'vendor',
-      minChunks: function (module, count) {
-        // any required modules inside node_modules are extracted to vendor
-        return (
-          module.resource &&
-          /\.js$/.test(module.resource) &&
-          module.resource.indexOf(
-            path.join(__dirname, '../node_modules')
-          ) === 0
-        )
-      }
-    }),
-    // extract webpack runtime and module manifest to its own file in order to
-    // prevent vendor hash from being updated whenever app bundle is updated
-    new webpack.optimize.CommonsChunkPlugin({
-      name: 'manifest',
-      chunks: ['vendor']
-    })
-  ]
-})
-
-if (config.build.productionGzip) {
-  var CompressionWebpackPlugin = require('compression-webpack-plugin')
-
-  webpackConfig.plugins.push(
-    new CompressionWebpackPlugin({
-      asset: '[path].gz[query]',
-      algorithm: 'gzip',
-      test: new RegExp(
-        '\\.(' +
-        config.build.productionGzipExtensions.join('|') +
-        ')$'
-      ),
-      threshold: 10240,
-      minRatio: 0.8
-    })
-  )
-}
-
-module.exports = webpackConfig

+ 0 - 6
app/fe/config/dev.env.js

@@ -1,6 +0,0 @@
-var merge = require('webpack-merge')
-var prodEnv = require('./prod.env')
-
-module.exports = merge(prodEnv, {
-  NODE_ENV: '"development"'
-})

+ 0 - 32
app/fe/config/index.js

@@ -1,32 +0,0 @@
-// see http://vuejs-templates.github.io/webpack for documentation.
-var path = require('path')
-
-module.exports = {
-  build: {
-    env: require('./prod.env'),
-    index: path.resolve(__dirname, '../dist/index.html'),
-    assetsRoot: path.resolve(__dirname, '../dist'),
-    assetsSubDirectory: 'static',
-    assetsPublicPath: '/',
-    productionSourceMap: true,
-    // Gzip off by default as many popular static hosts such as
-    // Surge or Netlify already gzip all static assets for you.
-    // Before setting to `true`, make sure to:
-    // npm install --save-dev compression-webpack-plugin
-    productionGzip: false,
-    productionGzipExtensions: ['js', 'css']
-  },
-  dev: {
-    env: require('./dev.env'),
-    port: 8080,
-    assetsSubDirectory: 'static',
-    assetsPublicPath: '/',
-    proxyTable: {},
-    // CSS Sourcemaps off by default because relative paths are "buggy"
-    // with this option, according to the CSS-Loader README
-    // (https://github.com/webpack/css-loader#sourcemaps)
-    // In our experience, they generally work as expected,
-    // just be aware of this issue when enabling this option.
-    cssSourceMap: false,
-  }
-}

+ 0 - 3
app/fe/config/prod.env.js

@@ -1,3 +0,0 @@
-module.exports = {
-  NODE_ENV: '"production"'
-}

+ 0 - 6
app/fe/config/test.env.js

@@ -1,6 +0,0 @@
-var merge = require('webpack-merge')
-var devEnv = require('./dev.env')
-
-module.exports = merge(devEnv, {
-  NODE_ENV: '"testing"'
-})

+ 0 - 11
app/fe/index.html

@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <title>SwitchHosts!</title>
-  </head>
-  <body>
-    <app></app>
-    <!-- built files will be auto injected -->
-  </body>
-</html>

+ 0 - 47
app/fe/package.json

@@ -1,47 +0,0 @@
-{
-  "name": "switchhosts",
-  "version": "1.0.0",
-  "description": "Switch hosts quickly!",
-  "author": "oldj <[email protected]>",
-  "private": true,
-  "scripts": {
-    "dev": "node build/dev-server.js",
-    "build": "node build/build.js",
-    "test": ""
-  },
-  "dependencies": {
-    "vue": "^1.0.21",
-    "babel-runtime": "^6.0.0"
-  },
-  "devDependencies": {
-    "babel-core": "^6.0.0",
-    "babel-loader": "^6.0.0",
-    "babel-plugin-transform-runtime": "^6.0.0",
-    "babel-preset-es2015": "^6.0.0",
-    "babel-preset-stage-2": "^6.0.0",
-    "babel-register": "^6.0.0",
-    "connect-history-api-fallback": "^1.1.0",
-    "css-loader": "^0.23.0",
-    "eventsource-polyfill": "^0.9.6",
-    "express": "^4.13.3",
-    "extract-text-webpack-plugin": "^1.0.1",
-    "file-loader": "^0.8.4",
-    "function-bind": "^1.0.2",
-    "html-webpack-plugin": "^2.8.1",
-    "http-proxy-middleware": "^0.12.0",
-    "json-loader": "^0.5.4",
-    "less": "^2.7.1",
-    "less-loader": "^2.2.3",
-    "ora": "^0.2.0",
-    "shelljs": "^0.6.0",
-    "url-loader": "^0.5.7",
-    "vue-hot-reload-api": "^1.2.0",
-    "vue-html-loader": "^1.0.0",
-    "vue-loader": "^8.3.0",
-    "vue-style-loader": "^1.0.0",
-    "webpack": "^1.12.2",
-    "webpack-dev-middleware": "^1.4.0",
-    "webpack-hot-middleware": "^2.6.0",
-    "webpack-merge": "^0.8.3"
-  }
-}

+ 0 - 46
app/fe/src/App.vue

@@ -1,46 +0,0 @@
-<template>
-  <div id="app">
-    <hello></hello>
-  </div>
-</template>
-
-<script>
-  import Hello from './components/Hello'
-
-  export default {
-    components: {
-      Hello
-    }
-  }
-</script>
-
-<style lang="less">
-  html {
-    height: 100%;
-  }
-
-  body {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    height: 100%;
-  }
-
-  #app {
-    color: #2c3e50;
-    margin-top: -100px;
-    max-width: 600px;
-    font-family: Source Sans Pro, Helvetica, sans-serif;
-    text-align: center;
-  }
-
-  #app a {
-    color: #42b983;
-    text-decoration: none;
-  }
-
-  .logo {
-    width: 100px;
-    height: 100px
-  }
-</style>

BIN
app/fe/src/assets/logo.png


+ 0 - 26
app/fe/src/components/Hello.vue

@@ -1,26 +0,0 @@
-<template>
-  <div class="hello">
-    <h1>{{ msg }}</h1>
-  </div>
-</template>
-
-<script>
-  export default {
-    data () {
-      return {
-        // note: changing this line won't causes changes
-        // with hot-reload because the reloaded component
-        // preserves its current state and we are modifying
-        // its initial state.
-        msg: 'Hello World!'
-      }
-    }
-  }
-</script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style lang="less" scoped>
-  h1 {
-    color: #42b983;
-  }
-</style>

+ 0 - 8
app/fe/src/main.js

@@ -1,8 +0,0 @@
-import Vue from 'vue'
-import App from './App'
-
-/* eslint-disable no-new */
-new Vue({
-  el: 'body',
-  components: { App }
-})

+ 0 - 0
app/fe/static/.gitkeep


+ 1 - 1
app/electron/gulpfile.js → app/gulpfile.js

@@ -17,7 +17,7 @@ gulp.task('ver', () => {
 
     console.log(`version -> ${version.join('.')}`);
 
-    let cnt = `exports.version = ${JSON.stringify(version)};`
+    let cnt = `exports.version = ${JSON.stringify(version)};`;
     fs.writeFileSync(fn, cnt, 'utf-8');
 
     // update package.json

+ 0 - 0
app/electron/index.html → app/index.html


+ 0 - 0
app/electron/main.js → app/main.js


+ 5 - 2
app/electron/package.json → app/package.json

@@ -11,7 +11,10 @@
     "type": "git",
     "url": "git+https://github.com/oldj/SwitchHosts.git"
   },
-  "keywords": ["SwitchHosts!", "host"],
+  "keywords": [
+    "SwitchHosts!",
+    "host"
+  ],
   "author": "oldj",
   "license": "MIT",
   "bugs": {
@@ -36,7 +39,7 @@
     "babel-preset-react": "^6.11.1",
     "css-loader": "^0.23.1",
     "electron-packager": "^7.7.0",
-    "electron-prebuilt": "^1.2.0",
+    "electron-prebuilt": "^1.3.4",
     "file-loader": "^0.9.0",
     "gulp": "^3.9.1",
     "js-beautify": "^1.6.3",

+ 253 - 223
app/src/agent.js

@@ -7,295 +7,325 @@
 
 'use strict';
 
-var sys_host_path = '/etc/hosts';
-var work_path = MacGap.homePath + '/.SwitchHosts';
-var data_path = work_path + '/data.json';
-var preference_path = work_path + '/preferences.json';
-var is_work_path_made;
-var _preferences;
-
-
-function mixObj(a, b) {
-    var k;
-    for (k in b) {
-        if (b.hasOwnProperty(k)) {
-            a[k] = b[k];
-        }
-    }
-    return a;
+const fs = require('fs');
+const path = require('path');
+const request = require('request');
+const moment = require('moment');
+const notifier = require('node-notifier');
+const util = require('./libs/util');
+const platform = process.platform;
+
+const paths = require('./libs/paths');
+const pref = require('./libs/pref');
+const sys_host_path = paths.sys_host_path;
+const work_path = paths.work_path;
+const data_path = paths.data_path;
+const preference_path = paths.preference_path;
+
+const exec = require('child_process').exec;
+const stat = require('./modules/stat');
+stat.init();
+
+const crypto = require('crypto');
+function md5 (text) {
+    return crypto.createHash('md5').update(text).digest('hex');
 }
 
-function writeFile(path, content) {
-    MacGap.File.write(path, content, 'string');
-}
 
-function readFile(path) {
-    return MacGap.File.read(path, 'string');
-}
+const m_lang = require('./lang');
+let sudo_pswd = '';
 
-function existPath(path) {
-    return MacGap.File.exists(path);
-}
+function getUserLang() {
+    let user_lang;
 
-function makeBackupHosts() {
-    return {
-        title: 'backup',
-        on: true,
-        content: getSysHosts()
-    };
+    user_lang = pref.get('user_language') || navigator.language || navigator.userLanguage;
+    if (user_lang === 'zh_CN') {
+        user_lang = 'cn';
+    } else {
+        user_lang = 'en';
+    }
+
+    return user_lang;
 }
 
-function tryToCreateWorkDir() {
-    if (existPath(work_path)) return;
+const lang = m_lang.getLang(getUserLang());
 
-    var cmd = 'mkdir -p \'' + work_path + '\'';
-    var my_task = MacGap.Task.create('/bin/sh', function (result) {
-        if (result.status == 0) {
-            //MacGap.File.write(sys_host_path, val, 'string');
-        } else {
-            alert('Fail to create work directory!\n\npath: ' + work_path);
-        }
-    });
-    my_task['arguments'] = ['-c', cmd];
-    my_task.launch();
-}
 
 function getSysHosts() {
-    var s;
+    let cnt = '';
+
+    try {
+        cnt = fs.readFileSync(sys_host_path, 'utf-8');
+    } catch (e) {
+        console.log(e.message);
+    }
+
+    return cnt;
+}
+
+function tryToCreateWorkDir() {
+    if (util.isDirectory((work_path))) {
+        console.log('work dir exists.');
+        return;
+    }
+
+    console.log(`try to create work directory: ${work_path}`);
     try {
-        s = readFile(sys_host_path);
+        fs.mkdirSync(work_path);
+        console.log('work directory created.');
     } catch (e) {
-        alert(e.message);
+        alert('Fail to create work directory!');
     }
-    return s || '';
 }
 
-function setSysHosts(val, sudo_pswd, callback) {
-    var tmp_f = work_path + '/tmp.txt';
-    //var cmd_f = work_path + '/cmd.sh';
+function saveData(content) {
 
-    sudo_pswd = sudo_pswd || '';
-    writeFile(tmp_f, val);
+    let txt = JSON.stringify({
+        list: content
+    });
 
-    var cmd;
-    //var cmd2;
+    fs.writeFile(data_path, txt, 'utf-8', (error) => {
+        if (error) {
+            alert(error.message);
+        }
+    });
+}
+
+
+function apply_UNIX(content, success) {
+    let tmp_fn = path.join(work_path, 'tmp.txt');
+    if (content) {
+        fs.writeFileSync(tmp_fn, content, 'utf-8');
+    }
+
+    let cmd;
     if (!sudo_pswd) {
         cmd = [
-            'cat "' + tmp_f + '" > ' + sys_host_path
-            , 'rm -rf ' + tmp_f
+            'cat "' + tmp_fn + '" > ' + sys_host_path
+            , 'rm -rf ' + tmp_fn
         ].join(' && ');
     } else {
         sudo_pswd = sudo_pswd.replace(/'/g, '\\x27');
         cmd = [
             'echo \'' + sudo_pswd + '\' | sudo -S chmod 777 ' + sys_host_path
-            , 'cat "' + tmp_f + '" > ' + sys_host_path
+            , 'cat "' + tmp_fn + '" > ' + sys_host_path
             , 'echo \'' + sudo_pswd + '\' | sudo -S chmod 644 ' + sys_host_path
-            , 'rm -rf ' + tmp_f
+            // , 'rm -rf ' + tmp_fn
         ].join(' && ');
-
-        //cmd2 = [
-        //    'echo \'' + sudo_pswd + '\' | sudo -S launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
-        //    , 'echo \'' + sudo_pswd + '\' | sudo -S launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
-        //    , 'echo \'' + sudo_pswd + '\' | sudo -S launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
-        //    , 'echo \'' + sudo_pswd + '\' | sudo -S launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
-        //].join(' ; ');
-        //cmd = cmd + ';' + cmd2;
-        //cmd = "$'" + cmd.replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'";
     }
 
-    var task = MacGap.Task.create('/bin/sh', function (result) {
-        if (result.status == 0) {
-            setTimeout(function () {
-                afterSetHosts(sudo_pswd);
-            }, 10);
-            callback && callback();
-        } else {
-            //alert('An error occurred!');
-            callback && callback(result);
+    exec(cmd, function(error, stdout, stderr) {
+        // command output is in stdout
+        if (error) {
+            if (!sudo_pswd) {
+                // 尝试让用户输入管理密码
+                SH_event.emit('sudo_prompt', (pswd) => {
+                    sudo_pswd = pswd;
+                    tryToApply(null, success);
+                });
+            } else {
+                alert(stderr);
+            }
+            return;
+        }
+
+        if (!error) {
+            after_apply(success);
         }
     });
-    task['arguments'] = ['-c', cmd];
-    task.launch();
 }
 
-function getData(config) {
-    if (!is_work_path_made) {
-        tryToCreateWorkDir();
-        is_work_path_made = true;
-    }
+function _after_apply_unix(callback) {
+    let cmd_fn = path.join(work_path, '_restart_mDNSResponder.sh');
 
-    var default_hosts = {
-        title: 'My Hosts',
-        on: false,
-        content: '# My Hosts\n'
-    };
-    var default_vals = {
-        sys: getSysHosts(),
-        list: [default_hosts, makeBackupHosts()]
-    };
-    if (!existPath(data_path)) {
-        return default_vals;
-    }
+    let cmd = [
+        'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
+        , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
+        , 'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
+        , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
+        , 'sudo killall -HUP mDNSResponder'
+    ].join('\n');
 
-    var vals = {};
-    mixObj(vals, config);
+    fs.writeFileSync(cmd_fn, cmd, 'utf-8');
 
-    var s;
-    try {
-        s = readFile(data_path);
-    } catch (e) {
-        alert(e.message);
-        return default_hosts;
+    exec(`/bin/sh ${cmd_fn}`, function(error, stdout, stderr) {
+        // command output is in stdout
+        if (error) {
+            console.log(error);
+        }
+        console.log(stdout, stderr);
+
+        callback();
+    });
+}
+
+function after_apply(callback) {
+
+    SH_event.emit('after_apply');
+
+    if (!sudo_pswd) {
+        callback();
+        return;
     }
 
-    try {
-        s = JSON.parse(s);
-    } catch (e) {
-        alert(e.message);
-        return default_hosts;
+    if (platform === 'darwin') {
+        _after_apply_unix(callback);
+        return;
     }
-    mixObj(vals, s);
 
-    return vals;
+    callback();
 }
 
-function setData(data) {
+function apply_Win32(content, success) {
+    // todo 判断写入权限
     try {
-        writeFile(data_path, JSON.stringify(data));
+        fs.writeFileSync(sys_host_path, content, 'utf-8');
     } catch (e) {
-        alert(e);
-    }
-}
-
-function getAllPreferences() {
-    if (!_preferences) {
-        var c = readFile(preference_path);
-        try {
-            c = JSON.parse(c);
-        } catch (e) {
-            c = {};
+        console.log(e);
+        let msg = e.message;
+        if (platform === 'win32') {
+            msg = `${msg}\n\n${lang.please_run_as_admin}`;
         }
-        _preferences = c;
+        alert(msg);
+        return;
     }
+    success && success();
 
-    return _preferences;
+    // todo 更新 DNS 缓存
 }
 
-function getPreference(key) {
-    var p = getAllPreferences();
-    return p[key];
-}
 
-function setPreference(key, value) {
-    var p = getAllPreferences();
-    p[key] = value;
+function tryToApply(content, success) {
 
-    writeFile(preference_path, JSON.stringify(p));
+    if (platform !== 'win32') {
+        apply_UNIX(content, success);
+    } else {
+        apply_Win32(content, success);
+    }
 }
 
-function getURL(url, data, success, fail) {
-    data = data || {};
-    if (!data._r) {
-        data._r = Math.random();
-    }
 
-    $.ajax({
-        url: url,
-        data: data,
-        //async: false,
-        success: function (s) {
-            success && success(s);
-        },
-        error: function (e) {
-            fail && fail(e);
-        }
-    });
-}
+// init
+tryToCreateWorkDir();
 
-function openURL(url) {
-    MacGap.openURL(url);
-}
+SH_event.on('test', () => {
+    console.log('ttt');
+});
 
-function activate() {
-    setTimeout(function () {
-        MacGap.activate();
-    }, 0);
-}
+SH_event.on('apply', (content, success) => {
+    success = success || function () {};
+    tryToApply(content, success);
+});
 
-function notify(type, title, content) {
-    MacGap.notify({
-        type: type,
-        title: title,
-        content: content
-    });
-}
+SH_event.on('sudo_pswd', (pswd) => {
+    sudo_pswd = pswd;
+});
 
-function afterSetHosts(sudo_pswd, callback) {
-    // sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
-    // sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
-    // sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist
-    // sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist
+SH_event.on('save_data', (content) => {
+    saveData(content);
+    ipcRenderer.send('send_host_list', content);
+});
 
-    if (!sudo_pswd) {
-        callback && callback();
-        return;
+SH_event.on('check_host_refresh', (host, force=false) => {
+    if (host.where !== 'remote' || !host.url || !host.refresh_interval) return;
+
+    let last_refresh = host.last_refresh;
+    let refresh_interval = parseInt(host.refresh_interval) || 0;
+    if (last_refresh && !force) {
+        last_refresh = new Date(last_refresh);
+        let delta = (new Date()).getTime() - (last_refresh.getTime() || 0) / (1000 * 3600);
+        if (delta < refresh_interval) {
+            return;
+        }
     }
 
-    var cmd;
-    //sudo_pswd = sudo_pswd.replace(/'/g, '\\x27');
-    cmd = [
-        //'echo \'' + sudo_pswd + '\' | sudo -S launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
-        //, 'echo \'' + sudo_pswd + '\' | sudo -S launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
-        //, 'echo \'' + sudo_pswd + '\' | sudo -S launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
-        //, 'echo \'' + sudo_pswd + '\' | sudo -S launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
-        'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
-        , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
-        , 'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
-        , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
-        , 'sudo killall -HUP mDNSResponder'
-    ].join('\n');
-    //cmd = "$'" + cmd.replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'";
-    //alert(cmd);
-    var path = work_path + '/_restart_mDNSResponder.sh';
-    MacGap.File.write(path, cmd, 'string');
-
-    var task = MacGap.Task.create('/bin/sh', function (result) {
-        if (result.status == 0) {
-            callback && callback();
+    // refresh
+    console.log(`getting '${host.url}' ..`);
+    SH_event.emit('loading', host, true);
+    host.is_loading = true;
+    request(host.url, (err, res, body) => {
+        console.log(err, res.statusCode);
+        SH_event.emit('loading', host, false);
+        host.is_loading = false;
+        if (!err && res.statusCode === 200) {
+            // console.log(body);
+            host.content = body;
+            host.last_refresh = moment().format('YYYY-MM-DD HH:mm:ss');
+
+            SH_event.emit('change');
         } else {
-            callback && callback(result);
+            console.log(err, res.statusCode);
         }
     });
+});
 
-    cmd = [
-        '/bin/sh ' + path
-        , 'rm -rf \'' + path + '\''
-    ].join(';');
-    task['arguments'] = ['-c', cmd];
-    //task['arguments'] = [path];
-    task.launch();
-}
+/**
+ * 如果本地没有 data 文件,认为是第一次运行
+ */
+function initGet() {
+    let dd = require('./libs/default_data');
+    let data = dd.make();
+
+    data.sys.content = getSysHosts();
+    data.list.push({
+        title: 'backup',
+        content: data.sys.content
+    });
 
-function log(msg) {
-    /*eslint no-console: "error"*/
-    // console.log(msg);
-    MacGap.log(msg.toString());
+    return data;
 }
 
 module.exports = {
-    log: log,
-    readFile: readFile,
-    writeFile: writeFile,
-    existPath: existPath,
-    getSysHosts: getSysHosts,
-    setSysHosts: setSysHosts,
-    getData: getData,
-    setData: setData,
-    getAllPreferences: getAllPreferences,
-    getPreference: getPreference,
-    setPreference: setPreference,
-    getURL: getURL,
-    openURL: openURL,
-    activate: activate,
-    notify: notify
+    md5: md5,
+    getHosts: function () {
+        let data = null;
+
+        if (!util.isFile(data_path)) {
+            return initGet();
+        }
+
+        try {
+            let cnt = fs.readFileSync(data_path, 'utf-8');
+            data = JSON.parse(cnt);
+        } catch (e) {
+            console.log(e);
+            alert('bad data file.. :(');
+            return initGet();
+        }
+
+        return {
+            sys: {
+                is_sys: true
+                , content: getSysHosts()
+            },
+            list: data.list.map((i) => {
+                return {
+                    title: i.title || ''
+                    , content: i.content || ''
+                    , on: !!i.on
+                    , where: i.where || 'local'
+                    , url: i.url || ''
+                    , last_refresh: i.last_refresh || null
+                    , refresh_interval: i.refresh_interval || 0
+                }
+            })
+        };
+    },
+    getSysHosts: function () {
+        return {
+            is_sys: true
+            , content: getSysHosts()
+        }
+    },
+    readFile: function (fn, callback) {
+        fs.readFile(fn, 'utf-8', callback);
+    },
+    notify: (options) => {
+        notifier.notify(Object.assign({
+            title: 'SwitchHosts!',
+            message: '',
+            icon: path.join(__dirname, 'assets', 'logo_512.png')
+        }, options));
+    },
+    lang: lang
 };

+ 0 - 0
app/SH3/public/images/app.icns → app/src/assets/app.icns


+ 0 - 0
app/SH3/public/images/icon_0.pdf → app/src/assets/icon_0.pdf


+ 0 - 0
app/SH3/public/images/icon_1.pdf → app/src/assets/icon_1.pdf


+ 0 - 0
app/SH3/public/images/icon_2.pdf → app/src/assets/icon_2.pdf


+ 0 - 0
app/electron/src/assets/ilogoTemplate.png → app/src/assets/ilogoTemplate.png


+ 0 - 0
app/electron/src/assets/[email protected] → app/src/assets/[email protected]


+ 0 - 0
app/electron/src/assets/[email protected] → app/src/assets/[email protected]


+ 0 - 0
app/electron/src/assets/logo.png → app/src/assets/logo.png


+ 0 - 0
app/electron/src/assets/[email protected] → app/src/assets/[email protected]


+ 0 - 0
app/electron/src/assets/[email protected] → app/src/assets/[email protected]


+ 0 - 0
app/electron/src/assets/[email protected] → app/src/assets/[email protected]


+ 0 - 0
app/electron/src/assets/logoTemplate.png → app/src/assets/logoTemplate.png


+ 0 - 0
app/electron/src/assets/[email protected] → app/src/assets/[email protected]


+ 0 - 0
app/electron/src/assets/[email protected] → app/src/assets/[email protected]


+ 0 - 0
app/electron/src/assets/[email protected] → app/src/assets/[email protected]


+ 0 - 0
app/electron/src/assets/logo_512.png → app/src/assets/logo_512.png


+ 0 - 0
app/electron/src/components/app.js → app/src/components/app.js


+ 0 - 0
app/electron/src/components/app.less → app/src/components/app.less


+ 0 - 0
app/electron/src/components/cfg.less → app/src/components/cfg.less


+ 0 - 0
app/electron/src/components/content/cm_hl.js → app/src/components/content/cm_hl.js


+ 0 - 0
app/electron/src/components/content/content.js → app/src/components/content/content.js


+ 0 - 0
app/electron/src/components/content/content.less → app/src/components/content/content.less


+ 0 - 0
app/electron/src/components/content/editor.js → app/src/components/content/editor.js


+ 0 - 0
app/electron/src/components/content/editor.less → app/src/components/content/editor.less


+ 0 - 0
app/electron/src/components/frame/edit.js → app/src/components/frame/edit.js


+ 0 - 0
app/electron/src/components/frame/edit.less → app/src/components/frame/edit.less


+ 0 - 0
app/electron/src/components/frame/frame.js → app/src/components/frame/frame.js


+ 0 - 0
app/electron/src/components/frame/frame.less → app/src/components/frame/frame.less


+ 0 - 0
app/electron/src/components/frame/sudo.js → app/src/components/frame/sudo.js


+ 0 - 0
app/electron/src/components/frame/sudo.less → app/src/components/frame/sudo.less


+ 0 - 0
app/electron/src/components/panel/buttons.js → app/src/components/panel/buttons.js


+ 0 - 0
app/electron/src/components/panel/buttons.less → app/src/components/panel/buttons.less


+ 0 - 0
app/SH3/public/css/iconfont/iconfont.css → app/src/components/panel/iconfont/iconfont.css


+ 0 - 0
app/SH3/public/css/iconfont/iconfont.eot → app/src/components/panel/iconfont/iconfont.eot


+ 0 - 0
app/SH3/public/css/iconfont/iconfont.svg → app/src/components/panel/iconfont/iconfont.svg


+ 0 - 0
app/SH3/public/css/iconfont/iconfont.ttf → app/src/components/panel/iconfont/iconfont.ttf


+ 0 - 0
app/SH3/public/css/iconfont/iconfont.woff → app/src/components/panel/iconfont/iconfont.woff


+ 0 - 0
app/electron/src/components/panel/list.js → app/src/components/panel/list.js


+ 0 - 0
app/electron/src/components/panel/list.less → app/src/components/panel/list.less


+ 0 - 0
app/electron/src/components/panel/list_item.js → app/src/components/panel/list_item.js


+ 0 - 0
app/electron/src/components/panel/list_item.less → app/src/components/panel/list_item.less


+ 0 - 0
app/electron/src/components/panel/panel.js → app/src/components/panel/panel.js


+ 0 - 0
app/electron/src/components/panel/panel.less → app/src/components/panel/panel.less


+ 0 - 0
app/electron/src/components/panel/searchbar.js → app/src/components/panel/searchbar.js


+ 0 - 0
app/electron/src/components/panel/searchbar.less → app/src/components/panel/searchbar.less


+ 0 - 0
app/electron/src/configs.js → app/src/configs.js


+ 0 - 0
app/electron/src/event.js → app/src/event.js


+ 17 - 3
app/src/lang.js

@@ -8,10 +8,14 @@
 var languages = {
     'en': {
         add: 'Add'
+        , new: 'New'
+        , quit: 'Quit'
         , cancel: 'Cancel'
         , ok: 'OK'
         , add_host: 'Add new rules.'
         , edit_host: 'Edit host'
+        , import: 'Import'
+        , export: 'Export'
         , host_title: 'Host title'
         , host_title_cant_be_empty: 'Host title could not be empty!'
         , sys_host_title: 'System Hosts'
@@ -31,29 +35,36 @@ var languages = {
         , url: 'URL'
         , bad_url: 'URL is not valid.'
         , auto_refresh: 'Auto refresh'
-        , last_refresh: 'Last refresh'
+        , last_refresh: 'Last refresh: '
         , refresh: 'Refresh'
         , never: 'never'
         , hour: 'hour'
         , hours: 'hours'
         , day: 'day'
         , days: 'days'
+        , feedback: 'Feedback'
+        , homepage: 'Homepage'
         , hide_dock_icon: 'Hide Dock Icon'
         , show_dock_icon: 'Show Dock Icon'
         , toggle_dock_icon: 'Toggle Dock Icon'
         , no_valid_host_found: 'There is no valid host in the file.'
         , confirm_import: 'You sure you want to import it? The original rules will be overwriten, this operation can not be undone.'
+        , please_run_as_admin: 'Please run SwitchHosts! as an Administrator.'
     },
     'cn': {
         add: '添加'
+        , new: '新建'
+        , quit: '退出'
         , cancel: '取消'
         , ok: '确定'
         , add_host: '添加 host 规则'
         , edit_host: '修改 host'
+        , import: '导入'
+        , export: '导出'
         , host_title: 'host 方案名'
         , host_title_cant_be_empty: 'Host 方案名不能为空!'
         , sys_host_title: '系统 Hosts'
-        , input_sudo_pswd: '请输入您的开机密码'
+        , input_sudo_pswd: '请输入您的开机密码(sudo 密码)'
         , sudo_pswd: '密码'
         , del_host: '删除当前 host'
         , confirm_del: '确定要删除此 host 吗?'
@@ -69,18 +80,21 @@ var languages = {
         , url: 'URL 地址'
         , bad_url: 'URL 地址有误。'
         , auto_refresh: '自动更新'
-        , last_refresh: '上次更新'
+        , last_refresh: '上次更新'
         , refresh: '刷新'
         , never: '从不'
         , hour: '小时'
         , hours: '小时'
         , day: '天'
         , days: '天'
+        , feedback: '反馈'
+        , homepage: '主页'
         , hide_dock_icon: '隐藏 Dock 图标'
         , show_dock_icon: '显示 Dock 图标'
         , toggle_dock_icon: '显示/隐藏 Dock 图标'
         , no_valid_host_found: '所指定的文件中未找到合法的 host 配置'
         , confirm_import: '确定要导入吗?原方案列表将被覆盖,此操作不可撤销。'
+        , please_run_as_admin: '请以管理员身份运行 SwitchHosts!'
     }
 };
 

+ 0 - 0
app/electron/src/libs/default_data.js → app/src/libs/default_data.js


+ 0 - 0
app/electron/src/libs/io.js → app/src/libs/io.js


+ 0 - 0
app/electron/src/libs/kw.js → app/src/libs/kw.js


+ 0 - 0
app/electron/src/libs/lang.js → app/src/libs/lang.js


+ 0 - 0
app/electron/src/libs/paths.js → app/src/libs/paths.js


+ 0 - 0
app/electron/src/libs/pref.js → app/src/libs/pref.js


+ 0 - 0
app/electron/src/libs/util.js → app/src/libs/util.js


+ 0 - 0
app/electron/src/mock.js → app/src/mock.js


+ 0 - 0
app/electron/src/modules/mainMenu.js → app/src/modules/mainMenu.js


+ 0 - 0
app/electron/src/modules/stat.js → app/src/modules/stat.js


+ 0 - 0
app/electron/src/modules/tray.js → app/src/modules/tray.js


+ 8 - 62
app/src/ui.js

@@ -5,67 +5,13 @@
 
 'use strict';
 
-var CodeMirror = require('codemirror');
-require('codemirror/mode/shell/shell');
-//require('codemirror/addon/mode/overlay');
+import React from 'react';
+import ReactDom from 'react-dom';
+import App from './components/app';
 
-var my_codemirror;
+ipcRenderer.setMaxListeners(20);
 
-function resize() {
-    var wh = window.innerHeight;
-    var oh = $('#left').find('.operations').height();
-    var h = wh - $('#sys-list').height() - oh;
-    $('#custom-list').css('height', h);
-    my_codemirror && my_codemirror.setSize('100%', wh);
-}
-
-function init(app) {
-
-    require('./cm_hl').init(app);
-
-    $(document).ready(function () {
-        var el_textarea = $('#host-code');
-        //el_textarea.css('height', window.innerHeight - 8);
-
-        my_codemirror = CodeMirror.fromTextArea(el_textarea[0], {
-            lineNumbers: true,
-            readOnly: true,
-            mode: 'host'
-        });
-        app.codemirror = my_codemirror;
-
-        my_codemirror.on('change', function (a) {
-            app.onCurrentHostBeChanged(a.getDoc().getValue());
-        });
-
-        my_codemirror.on('gutterClick', function (cm, n) {
-            if (app.current_host.is_editable === false) return;
-
-            var info = cm.lineInfo(n);
-            //cm.setGutterMarker(n, "breakpoints", info.gutterMarkers ? null : makeMarker());
-            var ln = info.text;
-            if (/^\s*$/.test(ln)) return;
-
-            var new_ln;
-            if (/^#/.test(ln)) {
-                new_ln = ln.replace(/^#\s*/, '');
-            } else {
-                new_ln = '# ' + ln;
-            }
-            my_codemirror.getDoc().replaceRange(new_ln, {line: info.line, ch: 0}, {line: info.line, ch: ln.length});
-            //app.caculateHosts();
-        });
-
-        $(document).keydown(function (e) {
-            if (e.which === 27) {
-                app.onESC();
-            }
-        });
-
-        resize();
-        $(window).resize(resize);
-    });
-}
-
-exports.init = init;
-exports.resize = resize;
+ReactDom.render(
+    <App/>
+    , document.getElementById('app')
+);

+ 0 - 0
app/electron/src/version.js → app/src/version.js


+ 0 - 0
app/electron/webpack.config.js → app/webpack.config.js


+ 0 - 0
app/SH3/.gitignore → legacy/v3.1_macgap/app/SH3/.gitignore


+ 0 - 0
app/SH3/MacGap/AppDelegate.h → legacy/v3.1_macgap/app/SH3/MacGap/AppDelegate.h


+ 0 - 0
app/SH3/MacGap/AppDelegate.m → legacy/v3.1_macgap/app/SH3/MacGap/AppDelegate.m


+ 0 - 0
app/SH3/MacGap/Base.lproj/MainMenu.xib → legacy/v3.1_macgap/app/SH3/MacGap/Base.lproj/MainMenu.xib


+ 0 - 0
app/SH3/MacGap/Base.lproj/MainWindow.xib → legacy/v3.1_macgap/app/SH3/MacGap/Base.lproj/MainWindow.xib


Some files were not shown because too many files changed in this diff