├── .gitignore
├── less
├── Show.less
├── About.less
├── JsonFormat.less
├── index.less
├── TxtDiff.less
└── sections.css
├── img
├── logo128x128.ico
├── logo128x128.icns
└── logoAlien16x16.png
├── app
├── assets
│ ├── img
│ │ ├── logoAlien.png
│ │ ├── logo128x128.icns
│ │ ├── logo128x128.ico
│ │ ├── logoAlien16x16.png
│ │ ├── fontawesome-webfont-674f50d287a8c48dc19ba404d20fe713.eot
│ │ ├── fontawesome-webfont-b06871f281fee6b241d60582ae9369b9.ttf
│ │ ├── fontawesome-webfont-fee66e712a8a08eef5805a46892932ad.woff
│ │ ├── fontawesome-webfont-af7ae505a9eed503f8b8e6982036873e.woff2
│ │ └── jsoneditor-icons-bfab7b16cb24ac5e2856e2b172f47fe8.svg
│ └── css
│ │ └── tools.min.css
├── package.json
├── js
│ ├── ext
│ │ └── Link.js
│ ├── tray
│ │ └── Tray.js
│ └── menu
│ │ └── Menu.js
├── main.js
└── index.html
├── sections
├── JsonFormat.html
├── About.html
├── svg
│ ├── IconHistory.svg
│ ├── TxtDiffSvg.svg
│ ├── LoadingSvg.svg
│ ├── AboutSvg.svg
│ ├── SwitchSvg.svg
│ └── JsonFormatSvg.svg
└── TxtDiff.html
├── README.md
├── js
├── ext
│ └── Link.js
├── index.js
├── JsonFormat.js
├── TxtDiff.js
└── Tools.js
├── package.json
├── index.html
├── LICENSE
├── webpack.config.js
├── 3rd
└── jsdifflib
│ ├── diffview.css
│ ├── diffview.js
│ └── difflib.js
└── gulpfile.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .DS_Store?
4 | dist
5 |
--------------------------------------------------------------------------------
/less/Show.less:
--------------------------------------------------------------------------------
1 | #show {
2 | #what {
3 | height: 100%;
4 | }
5 | }
--------------------------------------------------------------------------------
/img/logo128x128.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/img/logo128x128.ico
--------------------------------------------------------------------------------
/img/logo128x128.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/img/logo128x128.icns
--------------------------------------------------------------------------------
/img/logoAlien16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/img/logoAlien16x16.png
--------------------------------------------------------------------------------
/app/assets/img/logoAlien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/app/assets/img/logoAlien.png
--------------------------------------------------------------------------------
/app/assets/img/logo128x128.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/app/assets/img/logo128x128.icns
--------------------------------------------------------------------------------
/app/assets/img/logo128x128.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/app/assets/img/logo128x128.ico
--------------------------------------------------------------------------------
/app/assets/img/logoAlien16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/app/assets/img/logoAlien16x16.png
--------------------------------------------------------------------------------
/sections/JsonFormat.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/app/assets/img/fontawesome-webfont-674f50d287a8c48dc19ba404d20fe713.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/app/assets/img/fontawesome-webfont-674f50d287a8c48dc19ba404d20fe713.eot
--------------------------------------------------------------------------------
/app/assets/img/fontawesome-webfont-b06871f281fee6b241d60582ae9369b9.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/app/assets/img/fontawesome-webfont-b06871f281fee6b241d60582ae9369b9.ttf
--------------------------------------------------------------------------------
/app/assets/img/fontawesome-webfont-fee66e712a8a08eef5805a46892932ad.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/app/assets/img/fontawesome-webfont-fee66e712a8a08eef5805a46892932ad.woff
--------------------------------------------------------------------------------
/app/assets/img/fontawesome-webfont-af7ae505a9eed503f8b8e6982036873e.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aizuyan/gram-tools/HEAD/app/assets/img/fontawesome-webfont-af7ae505a9eed503f8b8e6982036873e.woff2
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tools",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC"
11 | }
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gram-tools
2 |
3 | > 依赖于electron,跨平台
4 |
5 | > 收集整理各种常用工具
6 |
7 | > GramTools是一个工具箱的集锦,志在提高工作效率。缩写刚好是GT,就是用了外星人GT作为图标,欢迎大家一起贡献
8 |
9 | 
10 |
11 | 
12 |
13 |
14 |
--------------------------------------------------------------------------------
/less/About.less:
--------------------------------------------------------------------------------
1 | #about{
2 | .container {
3 | .logo {
4 | text-align: center;
5 | margin: 20px 0;
6 | }
7 | .name {
8 | text-align: center;
9 | font-size: 20px;
10 | }
11 | .version {
12 | text-align: center;
13 | color: #C1C1C1;
14 | font-size: 18px;
15 | }
16 | .about-me {
17 | font-size: 20px;
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/app/js/ext/Link.js:
--------------------------------------------------------------------------------
1 | const shell = require('electron').shell
2 |
3 | const links = document.querySelectorAll('a[href]')
4 |
5 | function InitLink() {
6 | Array.prototype.forEach.call(links, (link) => {
7 | const url = link.getAttribute('href')
8 | if (url.indexOf('http') === 0) {
9 | link.addEventListener('click', (e) => {
10 | e.preventDefault()
11 | shell.openExternal(url)
12 | })
13 | }
14 | })
15 | }
16 |
17 | InitLink();
--------------------------------------------------------------------------------
/js/ext/Link.js:
--------------------------------------------------------------------------------
1 | const {shell} = require("electron");
2 |
3 | const links = document.querySelectorAll('a[href]')
4 |
5 | function InitLink() {
6 | Array.prototype.forEach.call(links, (link) => {
7 | const url = link.getAttribute('href')
8 | if (url.indexOf('http') === 0) {
9 | link.addEventListener('click', (e) => {
10 | e.preventDefault()
11 | shell.openExternal(url)
12 | })
13 | }
14 | })
15 | }
16 |
17 | $(function(){
18 | InitLink();
19 | });
--------------------------------------------------------------------------------
/sections/About.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
6 |
7 | Gram Tools
8 |
9 |
10 | V1.0.0
11 |
12 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sections/svg/IconHistory.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sections/svg/TxtDiffSvg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gram-tools",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "bootstrap": "^4.0.0",
14 | "css-loader": "^0.28.9",
15 | "diff": "^3.4.0",
16 | "electron": "^1.8.2",
17 | "file-loader": "^1.1.6",
18 | "gulp": "^3.9.1",
19 | "gulp-clean-css": "^3.9.2",
20 | "gulp-concat": "^2.6.1",
21 | "gulp-htmlmin": "^4.0.0",
22 | "gulp-less": "^3.4.0",
23 | "gulp-minify-css": "^1.2.4",
24 | "gulp-rename": "^1.2.2",
25 | "gulp-replace": "^0.6.1",
26 | "jquery": "^3.3.1",
27 | "jsoneditor": "^5.13.2",
28 | "layui-layer": "^1.0.9",
29 | "popper.js": "^1.12.9",
30 | "style-loader": "^0.20.1",
31 | "uglifyjs-webpack-plugin": "^1.2.2",
32 | "url-loader": "^0.6.2",
33 | "velocity-animate": "^1.5.1",
34 | "webpack": "^3.10.0"
35 | },
36 | "dependencies": {
37 | "antd": "^3.2.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 工具箱
5 |
6 |
7 |
8 |
9 |
10 | {LoadingSvg}
11 |
12 |
13 |
14 |
15 |
19 |
20 | {JsonFormatSvg}
21 | json转换
22 |
23 |
24 | {TxtDiffSvg}
25 | 文本DIFF
26 |
27 |
31 |
32 | {AboutSvg}
33 | 关于
34 |
35 |
36 |
37 | {JsonFormat}
38 | {TxtDiff}
39 | {About}
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 一颗小白菜
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path'),
2 | webpack = require('webpack'),
3 | uglify = require('uglifyjs-webpack-plugin');
4 | /**
5 | * 常用变量
6 | */
7 | const projectPath = __dirname;
8 | const appPath = path.join(projectPath, "app");
9 | const assetsPath = path.join(appPath, "assets");
10 |
11 | module.exports = {
12 | entry: path.join(projectPath, "js", "index.js"),
13 | output: {
14 | path: assetsPath,
15 | filename: "js/tools.js"
16 | },
17 |
18 | module: {
19 | rules: [
20 | {
21 | test: /\.css$/,
22 | use: ['style-loader', 'css-loader']
23 | }, {
24 | test: /\.(eot|woff|woff2|ttf|svg|png|jpg)$/,
25 | use: 'url-loader?limit=8000&name=[name]-[hash].[ext]&outputPath=img/&publicPath=assets/'
26 | },
27 | ]
28 | },
29 | plugins: [
30 | // 打包内容里面全局jquery
31 | new webpack.ProvidePlugin({
32 | "$": "jquery",
33 | "jQuery": "jquery",
34 | "window.jQuery": 'jquery'
35 | }),
36 | // 压缩
37 | new uglify()
38 | ]
39 | };
--------------------------------------------------------------------------------
/app/js/tray/Tray.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var path = require('path');
4 | const {Menu, Tray} = require('electron')
5 |
6 | function InitTray(app, win) {
7 | var trayIcon = null;
8 |
9 | if (process.platform === 'darwin') {
10 | trayIcon = new Tray(path.join(__dirname, "../..", 'assets/img/logoAlien16x16.png'));
11 | }
12 | else {
13 | trayIcon = new Tray(path.join(__dirname, "../..", 'assets/img/logoAlien16x16.png'));
14 | }
15 | trayIcon.setToolTip("GramTools")
16 |
17 | var trayMenuTemplate = [
18 | {
19 | label: "Gram Tools",
20 | click: () => {
21 | app.emit('show')
22 | }
23 | },
24 | {
25 | type: "separator"
26 | },
27 | {
28 | label: "退出",
29 | accelerator: 'CommandOrControl+Q',
30 | click: () => {
31 | app.quit()
32 | }
33 | }
34 | ];
35 | var trayMenu = Menu.buildFromTemplate(trayMenuTemplate);
36 | trayIcon.setContextMenu(trayMenu);
37 |
38 | trayIcon.on('click', () => {
39 | if (process.platform === 'win32') {
40 | app.emit('show')
41 | }
42 | });
43 | };
44 |
45 | exports.InitTray = InitTray;
--------------------------------------------------------------------------------
/less/JsonFormat.less:
--------------------------------------------------------------------------------
1 | #json-format {
2 | #json-format-container {
3 | height: 100%;
4 | background-color: #FFF;
5 | .jsoneditor {
6 | &.jsoneditor-mode-tree {
7 | div.jsoneditor-readonly, div.jsoneditor-field, div.jsoneditor-value, div.jsoneditor td, div.jsoneditor th, div.jsoneditor textarea, .jsoneditor-schema-error {
8 | font-size: 15px !important;
9 | line-height: 1.2em !important;
10 | }
11 | }
12 | .ace_editor.ace-jsoneditor {
13 | font-size: 15px !important;
14 | }
15 | .jsoneditor-menu {
16 | button[type=button] {
17 | cursor: pointer;
18 | outline: none;
19 | }
20 | .jsoneditor-menu {
21 | button[type=button].jsoneditor-selected {
22 | background-color: #3883fa;
23 | }
24 | }
25 | .jsoneditor-history {
26 | background-image: url("../sections/IconHistory.svg");
27 | }
28 | ul.jsoneditor-menu {
29 | li {
30 | button {
31 | &.jsoneditor-type-modes {
32 | div.jsoneditor-text {
33 | padding-top: 10px;
34 | padding-bottom: 10px;
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 | .jsoneditor-navigation-bar {
42 | div.jsoneditor-contextmenu ul li button.jsoneditor-selected, div.jsoneditor-contextmenu ul li button.jsoneditor-selected:hover, div.jsoneditor-contextmenu ul li button.jsoneditor-selected:focus {
43 | background-color: #3883fa;
44 | }
45 | }
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/sections/TxtDiff.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | Tips diff工具,为了更好的体验,请使用全屏。
7 |
8 |
21 |
22 |
38 |
39 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/sections/svg/LoadingSvg.svg:
--------------------------------------------------------------------------------
1 |
2 |
19 |
--------------------------------------------------------------------------------
/app/js/menu/Menu.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {Menu, Tray} = require('electron');
4 | const os = process.platform
5 |
6 | function InitMenu(app, win) {
7 | let template = [
8 | {
9 | label: "编辑",
10 | submenu: [
11 | {
12 | label: "撤销",
13 | role: "undo"
14 | },
15 | {
16 | label: "重做",
17 | role: "redo"
18 | },
19 | {
20 | type: "separator"
21 | },
22 | {
23 | label: "剪切",
24 | role: "cut"
25 | },
26 | {
27 | label: "复制",
28 | role: "copy"
29 | },
30 | {
31 | label: "粘贴",
32 | role: "paste"
33 | },
34 | {
35 | label: "删除",
36 | role: "delete"
37 | },
38 | {
39 | label: "全选",
40 | role: "selectall"
41 | }
42 | ]
43 | }
44 | ];
45 |
46 | if (os === 'darwin') {
47 | template.unshift({
48 | label: "GramTools",
49 | submenu: [
50 | { label: "关于 GramTools"},
51 | { type: "separator" },
52 | {
53 | label: "退出",
54 | accelerator: 'CommandOrControl+Q',
55 | click: () => {
56 | app.quit()
57 | }
58 | }
59 | ]
60 | });
61 | }
62 |
63 |
64 | //注册菜单
65 | Menu.setApplicationMenu(Menu.buildFromTemplate(template));
66 | }
67 | exports.InitMenu = InitMenu;
--------------------------------------------------------------------------------
/sections/svg/AboutSvg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sections/svg/SwitchSvg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/js/index.js:
--------------------------------------------------------------------------------
1 | import "velocity-animate";
2 | import "../node_modules/velocity-animate/velocity.ui.js";
3 | //import "bootstrap";
4 | import "bootstrap";
5 | import "../node_modules/bootstrap/dist/css/bootstrap.css";
6 |
7 | // import layer
8 | import "../node_modules/layui-layer/dist/layer.js";
9 |
10 | import JsonFromat from "./JsonFormat.js";
11 | import TxtDiff from "./TxtDiff.js";
12 |
13 |
14 | var pageObj = {
15 | system: {}, // app系统级别的变量
16 | sectionIndex: {}, // 各个section的index
17 | nowSectionIndex: 1, // 当前的section索引
18 | sectionCache: {}, // 各个section内容的缓存
19 | };
20 |
21 | pageObj.initLoading = function() {
22 | $("#loading").velocity("transition.whirlOut", { duration: 650 });
23 | $("#loading").remove();
24 | };
25 |
26 | pageObj.initMenu = function() {
27 | let zIndexStart = 1000;
28 | $("#index>.right>.section:eq(0)").css("z-index", zIndexStart++);
29 | $("#index>.right>.section:gt(0)").css("top", "-100%");
30 | $("#index>.left").on("click", ".item", function(e){
31 | let me = $(this);
32 | let index = $("#index>.left>.item").index(this);
33 | let lastIndex = $("#index>.left>.item").index($("#index>.left>.item.active"));
34 | // 判断是否当前元素
35 | if (index == lastIndex) {
36 | return true;
37 | }
38 | let topGap = lastIndex > index ? "-100%" : "100%";
39 | let lastTopGap = lastIndex > index ? "100%" : "-100%";
40 |
41 | let section = $("#index>.right>.section:eq("+index+")");
42 | let lastSection = $("#index>.right>.section:eq("+lastIndex+")");
43 | me.siblings(".item").removeClass("active");
44 | section.css({
45 | "top": topGap,
46 | "z-index": zIndexStart++
47 | });
48 | setTimeout(function (argument) {
49 | lastSection.velocity(
50 | {
51 | top: lastTopGap
52 | },
53 | {
54 | duration: 300,
55 | easing: "ease-in"
56 | }
57 | );
58 | section.velocity(
59 | {
60 | top: 0
61 | },
62 | {
63 | duration: 300,
64 | queue: false,
65 | easing: "ease-in"
66 | }
67 | );
68 | me.addClass("active");
69 | }, 100);
70 | });
71 | };
72 |
73 | pageObj.init = function(){
74 | // 初始化各个模块
75 | $(function() {
76 | pageObj.initLoading();
77 | pageObj.initMenu();
78 | JsonFromat();
79 | TxtDiff();
80 | });
81 | };
82 | pageObj.init();
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/less/index.less:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 | html {
6 | height: 100%;
7 | body {
8 | height: 100%;
9 | }
10 | font-size: 10px;
11 | }
12 | #loading {
13 | position: fixed;
14 | width: 100%;
15 | height: 100%;
16 | top: 0;
17 | left: 0;
18 | z-index: 10000;
19 | background: black;
20 | opacity: .8;
21 | >.loading-svg {
22 | text-align: center;
23 | top: 50%;
24 | position: relative;
25 | margin: 0 auto;
26 | }
27 | }
28 | #index {
29 | width: 100%;
30 | height: 100%;
31 | box-sizing: border-box;
32 | -webkit-box-sizing: border-box;
33 | overflow: hidden;
34 | display: flex;
35 | >.left{
36 | -webkit-touch-callout: none;
37 | -webkit-user-select: none;
38 | touch-callout: none;
39 | user-select: none;
40 | width: 20%;
41 | max-width: 200px;
42 | min-width: 150px;
43 | height: 100%;
44 | box-sizing: border-box;
45 | -webkit-box-sizing: border-box;
46 | background-color: rgb(34, 32, 39);
47 | font-size: 2rem;
48 | overflow-y: auto;
49 | >.item{
50 | font-size: 1.6rem;
51 | box-sizing: border-box;
52 | -webkit-box-sizing: border-box;
53 | padding: 7px 20px 10px;
54 | color: rgb(255, 255, 255);
55 | >.icon {
56 | margin-right: 5px;
57 | }
58 | &.active {
59 | background-color: rgb(37, 135, 247);
60 | }
61 | cursor: pointer;
62 | }
63 | >.division{
64 | display: flex;
65 | box-sizing: border-box;
66 | -webkit-box-sizing: border-box;
67 | color: rgb(143, 143, 143);
68 | font-size: 1.6rem;
69 | padding: 5px 10px;
70 | position: relative;
71 | >.txt {
72 | }
73 | >.line{
74 | flex:1;
75 | position: relative;
76 | margin-left: 1rem;
77 | &:after {
78 | content: "";
79 | display: block;
80 | width: 100%;
81 | background-color: rgb(77, 77, 77);
82 | height: 0.1rem;
83 | position: absolute;
84 | top: 1rem;
85 | }
86 | }
87 | }
88 | }
89 | >.right {
90 | flex:1;
91 | height: 100%;
92 | box-sizing: border-box;
93 | -webkit-box-sizing: border-box;
94 | position: relative;
95 | >.section {
96 | width: 100%;
97 | height: 100%;
98 | overflow-y: scroll;
99 | position: absolute;
100 | left: 0;
101 | right: 0;
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/3rd/jsdifflib/diffview.css:
--------------------------------------------------------------------------------
1 | /*
2 | This is part of jsdifflib v1.0.
3 |
4 | Copyright 2007 - 2011 Chas Emerick . All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without modification, are
7 | permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this list of
10 | conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 | of conditions and the following disclaimer in the documentation and/or other materials
14 | provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY Chas Emerick ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
18 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Chas Emerick OR
19 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
26 | The views and conclusions contained in the software and documentation are those of the
27 | authors and should not be interpreted as representing official policies, either expressed
28 | or implied, of Chas Emerick.
29 | */
30 | table.diff {
31 | border-collapse:collapse;
32 | border:1px solid darkgray;
33 | white-space:pre-wrap
34 | }
35 | table.diff tbody {
36 | font-family:Courier, monospace
37 | }
38 | table.diff tbody th {
39 | font-family:verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif;
40 | background:#EED;
41 | font-size:11px;
42 | font-weight:normal;
43 | border:1px solid #BBC;
44 | color:#886;
45 | padding:.3em .5em .1em 2em;
46 | text-align:right;
47 | vertical-align:top
48 | }
49 | table.diff thead {
50 | border-bottom:1px solid #BBC;
51 | background:#EFEFEF;
52 | font-family:Verdana
53 | }
54 | table.diff thead th.texttitle {
55 | text-align:left
56 | }
57 | table.diff tbody td {
58 | padding:0px .4em;
59 | padding-top:.4em;
60 | vertical-align:top;
61 | }
62 | table.diff .empty {
63 | background-color:#DDD;
64 | }
65 | table.diff .replace {
66 | background-color:#FD8
67 | }
68 | table.diff .delete {
69 | background-color:#E99;
70 | }
71 | table.diff .skip {
72 | background-color:#EFEFEF;
73 | border:1px solid #AAA;
74 | border-right:1px solid #BBC;
75 | }
76 | table.diff .insert {
77 | background-color:#9E9
78 | }
79 | table.diff th.author {
80 | text-align:right;
81 | border-top:1px solid #BBC;
82 | background:#EFEFEF
83 | }
--------------------------------------------------------------------------------
/js/JsonFormat.js:
--------------------------------------------------------------------------------
1 | import JSONEditor from "JSONEditor";
2 | import "../node_modules/jsoneditor/dist/jsoneditor.css";
3 | import utilMine from "../node_modules/jsoneditor/src/js/util";
4 | JSONEditor.prototype.setMode = function (mode) {
5 | var container = this.container;
6 | var options = utilMine.extend({}, this.options);
7 | var oldMode = options.mode;
8 | var data;
9 | var name;
10 |
11 | options.mode = mode;
12 | var config = JSONEditor.modes[mode];
13 | if (config) {
14 | try {
15 | var asText = (config.data == 'text');
16 | name = this.getName();
17 | data = this[asText ? 'getText' : 'get'](); // get text or json
18 |
19 | this.destroy();
20 | utilMine.clear(this);
21 | utilMine.extend(this, config.mixin);
22 | this.create(container, options);
23 |
24 | this.setName(name);
25 | this[asText ? 'setText' : 'set'](data); // set text or json
26 |
27 | if (typeof config.load === 'function') {
28 | try {
29 | config.load.call(this);
30 | }
31 | catch (err) {
32 | console.error(err);
33 | }
34 | }
35 |
36 | if (typeof options.onModeChange === 'function') {
37 | try {
38 | options.onModeChange(mode, oldMode);
39 | }
40 | catch (err) {
41 | console.error(err);
42 | }
43 | }
44 | }
45 | catch (err) {
46 | this._onError(err);
47 | }
48 | }
49 | else {
50 | throw new Error('Unknown mode "' + options.mode + '"');
51 | }
52 | };
53 |
54 | export default () => {
55 | let container, options, json,
56 | nowFontSize = 13,
57 | containerId = "json-format-container";
58 |
59 | function changeEditorFontSize(fontSize, duration) {
60 | duration = typeof(duration) != "undefined" ? duration : 0;
61 | $("#json-format #json-format-container .jsoneditor .ace_editor.ace-jsoneditor").velocity({
62 | "font-size": fontSize
63 | }, {
64 | duration: duration,
65 | });
66 | }
67 |
68 | function formatMenuInfo() {
69 | $("button[type=button].jsoneditor-repair").remove();
70 | $("a.jsoneditor-poweredBy").remove();
71 |
72 | $("button.jsoneditor-type-modes.jsoneditor-selected").attr("disabled", "disabled");
73 |
74 | // 增加历史记录按钮
75 | //$(".jsoneditor-menu > .jsoneditor-modes").after('');
76 | };
77 |
78 | container = document.getElementById(containerId);
79 |
80 | options = {
81 | mode: 'code',
82 | modes: ['code', 'tree'], // allowed modes
83 | onError: function (err) {
84 | var error = err.toString();
85 | layer.msg(
86 | "出现错误:" + error, {
87 | time: 2000, //2s后自动关闭
88 | });
89 | },
90 | onModeChange: function(newMode, oldMode) {
91 | formatMenuInfo();
92 | }
93 | };
94 |
95 | json = {
96 | "weatherinfo": {
97 | "city": "北京",
98 | "city_en": "beijing",
99 | "date_y": "2013年9月24日",
100 | "index": "较冷",
101 | "index_d": "建议着大衣、呢外套加毛衣、卫衣等服装。体弱者宜着厚外套、厚毛衣。因昼夜温差较大,注意增减衣服。",
102 | "index48": "较舒适",
103 | "index_co": "舒",
104 | "st1": "21",
105 | "index_ag": "极易发"
106 | }
107 | };
108 | var editor = new JSONEditor(container, options, json);
109 |
110 |
111 |
112 | (function() {
113 | formatMenuInfo();
114 | })();
115 | };
--------------------------------------------------------------------------------
/less/TxtDiff.less:
--------------------------------------------------------------------------------
1 | #txt-diff {
2 | .sm-alert {
3 | padding: 1rem;
4 | padding-right: 6rem;
5 | font-size: 1.4rem;
6 | .sm-colse {
7 | padding: 1rem;
8 | font-size: 2.2rem;
9 | outline: none;
10 | }
11 | }
12 | .diff-condtion {
13 | margin: 1.6rem 1.6rem 0;
14 | font-size: 1.6rem;
15 | label {
16 | &.btn {
17 | font-size: 1.4rem;
18 | }
19 | }
20 | input[type=radio][name=diffLevel] {
21 | cursor: pointer;
22 | }
23 | .do-diff {
24 | button[type=button] {
25 | outline: none;
26 | &:focus {
27 | outline: none;
28 | box-shadow: none;
29 | }
30 | }
31 | display: inline-block;
32 | margin-left: 2rem;
33 | }
34 | .switch-icon {
35 |
36 | }
37 | }
38 | .txt-wrap {
39 | margin: 0 0;
40 | textarea {
41 | &.code-diff-left, &.code-diff-right {
42 | font-size: 1.4rem;
43 | }
44 | }
45 | }
46 | .txt-diff-result {
47 | margin: 0 1rem 1rem;
48 | border: 1px solid #F0F0F0;
49 | font-size: 2rem;
50 | .level-char {
51 | div {
52 | display: inline-block;
53 | }
54 | background-color: #FFF;
55 | padding-left: 2em;
56 | position: relative;
57 | &:hover {
58 | .char-header {
59 | .char-num {
60 | background-color: #DED7FC;
61 | border-color: #CABFFA;
62 | }
63 | }
64 | }
65 | .char-info {
66 | display: block;
67 | word-wrap: break-word;
68 | word-break: break-all;
69 | line-height: 3rem;
70 | font-size: 2rem;
71 | padding-left: 2em;
72 | height: 3rem;
73 | &:has(.changed-delete) {
74 | color: red;
75 | }
76 | &.changed {
77 | background-color: #fafbfc;
78 | }
79 | &.changed-delete {
80 | background-color: #ffeef0;
81 | }
82 | &.changed-add {
83 | background-color: #e6ffed;
84 | }
85 | .char-add {
86 | background-color: #acf2bd;
87 | }
88 | .char-delete {
89 | background-color: #fdb8c0;
90 | }
91 | }
92 | .char-header {
93 | margin-left: -2em;
94 | position: absolute;
95 | height: 100%;
96 | .char-num {
97 | display: inline-block;
98 | width: 4rem;
99 | height: 100%;
100 | background-color: #fafafa;
101 | border-right: 1px solid;
102 | border-color: #f0f0f0;
103 | text-align: right;
104 | padding: 0 .2em 0 0;
105 | vertical-align: top;
106 | color: rgba(0,0,0,0.3);
107 | font-size: .8em;
108 | }
109 | .char-flag {
110 | width: 2em;
111 | height: 100%;
112 | text-align: center;
113 | color: #9bb0a1
114 | }
115 | }
116 | }
117 | .level-line {
118 | background-color: #FFF;
119 | padding-left: 5em;
120 | position: relative;
121 | &:hover {
122 | .line-header {
123 | .line-num {
124 | background-color: #DED7FC;
125 | border-color: #CABFFA;
126 | }
127 | }
128 | }
129 | div {
130 | display: inline-block;
131 | }
132 | .line-info {
133 | word-wrap: break-word;
134 | word-break: break-all;
135 | line-height: 3rem;
136 | font-size: 2rem;
137 | padding-left: 1rem;
138 | }
139 | .line-header {
140 | margin-left: -5em;
141 | position: absolute;
142 | height: 100%;
143 | .line-num {
144 | display: inline-block;
145 | width: 4rem;
146 | height: 100%;
147 | background-color: #fafafa;
148 | border-right: 1px solid;
149 | border-color: #f0f0f0;
150 | text-align: right;
151 | padding: 0 .2em 0 0;
152 | vertical-align: top;
153 | color: rgba(0,0,0,0.3);
154 | font-size: .8em;
155 | }
156 | .line-flag {
157 | width: 1em;
158 | height: 100%;
159 | text-align: center;
160 | color: #9bb0a1
161 | }
162 | }
163 | &.line-add {
164 | background-color: #ecfdf0;
165 | .line-num {
166 | background-color: #ddfbe6;
167 | border-color: #c7f0d2;
168 | }
169 | }
170 | &.line-delete {
171 | background-color: #fbe9eb;
172 | .line-num {
173 | background-color: #f9d7dc;
174 | border-color: #fac5cd;
175 | }
176 | }
177 | }
178 | }
179 | }
--------------------------------------------------------------------------------
/app/main.js:
--------------------------------------------------------------------------------
1 | const {app, BrowserWindow} = require('electron')
2 | const path = require('path')
3 | const url = require('url')
4 | const electron = require('electron');
5 |
6 | const Tray = require("./js/tray/Tray"); // 托盘图标
7 | const Menu = require("./js/menu/Menu"); // 复制粘贴
8 |
9 |
10 | let win = null;
11 | let contents = null;
12 | let isTrayInit = false;
13 | let willQuitApp = false;
14 |
15 | // 判断是不是第二个实例,第二个实例执行到这里显示之前的窗口
16 | const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
17 | if (win) {
18 | if (win.isMinimized()) win.restore()
19 | win.focus()
20 | }
21 | })
22 | if (isSecondInstance) {
23 | app.quit()
24 | }
25 |
26 | // 创建窗口
27 | function createWin () {
28 | // 创建主窗口
29 | win = new BrowserWindow({
30 | width: 960,
31 | height: 600,
32 | minWidth: 768,
33 | minHeight: 480,
34 | fullscreenable: true,
35 | autoHideMenuBar: true
36 | })
37 |
38 | contents = win.webContents;
39 |
40 | // 内容加在完毕之后,设置托盘图标
41 | contents.on('did-finish-load', () => {
42 | Menu.InitMenu(app, win);
43 | if (!isTrayInit) {
44 | Tray.InitTray(app, win);
45 | isTrayInit = true
46 | }
47 | })
48 |
49 | // 加载app首页面
50 | win.loadURL(url.format({
51 | pathname: path.join(__dirname, 'index.html'),
52 | protocol: 'file:',
53 | }))
54 |
55 | // 关闭的时候可能只是想最小化
56 | win.on('close', (e) => {
57 | console.log();
58 | if (willQuitApp) {
59 | win = null;
60 | } else {
61 | if (process.platform == 'darwin') {
62 | if (win.isMaximized()) {
63 | win.setFullScreen(false);
64 | let times = 0;
65 | let timer = setInterval(function() {
66 | win.hide();
67 | if (times++ > 6) {
68 | clearInterval(timer);
69 | }
70 | }, 200);
71 | } else {
72 | win.hide();
73 | }
74 | } else {
75 | win.hide();
76 | }
77 | e.preventDefault();
78 | }
79 | });
80 |
81 | // 窗口已经关闭的时候销毁
82 | win.on('closed', () => {
83 | win = null
84 | })
85 |
86 | // Open the DevTools.
87 | //contents.openDevTools()
88 | }
89 | app.on('ready', function() {
90 | createWin();
91 | });
92 |
93 | /**
94 | * 添加一个展示时间,通过emit调用
95 | */
96 | app.on('show', function () {
97 | if (win) {
98 | if (win.isMinimized()) {
99 | win.restore()
100 | }
101 | win.show()
102 | } else {
103 | createWin()
104 | }
105 | })
106 |
107 | // mac下面点击关闭,最小化到docker中,再次点击恢复
108 | app.on('activate', function () {
109 | if (!win) {
110 | createWin()
111 | } else if (win.isMinimized()) {
112 | win.restore()
113 | } else {
114 | win.show()
115 | }
116 | })
117 |
118 | // 真正的退出
119 | app.on('before-quit', () => willQuitApp = true)
120 |
121 | app.on('window-all-closed', function () {
122 | // On OS X it is common for applications and their menu bar
123 | // to stay active until the user quits explicitly with Cmd + Q
124 | if (process.platform !== 'darwin') {
125 | app.quit()
126 | }
127 | })
128 |
129 |
130 |
131 | const ipc = require('electron').ipcMain
132 | const dialog = require('electron').dialog
133 |
134 | ipc.on('select-file-dialog', function (event) {
135 | dialog.showOpenDialog({
136 | properties: ['openFile', 'multiSelections']
137 | }, function (files) {
138 | if (files) event.sender.send('select-file-paths', files)
139 | })
140 | })
141 |
142 | ipc.on('select-single-file-dialog', function (event) {
143 | dialog.showOpenDialog({
144 | properties: ['openFile']
145 | }, function (files) {
146 | if (files) event.sender.send('select-file-path', files)
147 | })
148 | })
149 |
150 | ipc.on('save-file-dialog', function (event) {
151 | dialog.showSaveDialog({
152 | "title": "保存合并文件位置",
153 | }, function (fullPath) {
154 | if (fullPath) event.sender.send('save-file-path', fullPath)
155 | })
156 | })
157 |
158 | ipc.on('get-app-path', function (event) {
159 | event.sender.send('got-app-path', app.getAppPath())
160 | })
161 |
162 | // In this file you can include the rest of your app's specific main process
163 | // code. You can also put them in separate files and require them here.
164 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp'),
2 | less = require('gulp-less'),
3 | cssMin = require('gulp-clean-css'),
4 | rename = require('gulp-rename'),
5 | concat = require('gulp-concat'),
6 | path = require('path'),
7 | replace = require("gulp-replace"),
8 | fs = require("fs"),
9 | htmlmin = require('gulp-htmlmin'),
10 | exec = require('child_process').exec;
11 |
12 | /**
13 | * 常用变量
14 | */
15 | const projectPath = __dirname;
16 | const appPath = path.join(projectPath, "app");
17 | const assetsPath = path.join(appPath, "assets");
18 |
19 | gulp.task("makeCss", function() {
20 | gulp.src([
21 | "less/**/*.less"
22 | ])
23 | .pipe(less())
24 | .pipe(concat("sections.css"))
25 | .pipe(gulp.dest(path.join(projectPath, "less")));
26 | });
27 |
28 | /**
29 | * 合并压缩所含有用到的css到一个文件
30 | */
31 | gulp.task('handleCss', ["makeCss"], function () {
32 | gulp.src([
33 | "./3rd/jsdifflib/diffview.css",
34 | path.join(projectPath, "less", "sections.css")
35 | ])
36 | .pipe(concat("tools.css"))
37 | .pipe(cssMin())
38 | .pipe(rename({suffix:'.min'}))
39 | .pipe(gulp.dest(path.join(assetsPath, "css")));
40 | });
41 |
42 | gulp.task("handleImg", function () {
43 | return gulp.src(["img/**/*.png", "img/**/*.icns", "img/**/*.ico"])
44 | .pipe(gulp.dest(path.join(assetsPath, "img")));
45 | });
46 |
47 |
48 | /**
49 | * 处理html
50 | */
51 | gulp.task("handleHtml", function() {
52 | let getHtmlSection = (sectionName) => {
53 | let html = fs.readFileSync(
54 | path.join(projectPath, "sections", sectionName + ".html"),
55 | "utf8"
56 | );
57 | return html;
58 | };
59 | let getSvgSection = (sectionName) => {
60 | let svg = fs.readFileSync(
61 | path.join(projectPath, "sections", "svg", sectionName + ".svg"),
62 | "utf8"
63 | );
64 | return svg;
65 | };
66 | gulp.src(["./index.html"])
67 | .pipe(replace(
68 | "{JsonFormat}", getHtmlSection("JsonFormat")
69 | ))
70 | .pipe(replace(
71 | "{TxtDiff}", getHtmlSection("TxtDiff")
72 | ))
73 | .pipe(replace(
74 | "{About}", getHtmlSection("About")
75 | ))
76 | .pipe(replace(
77 | "{JsonFormatSvg}", getSvgSection("JsonFormatSvg")
78 | ))
79 | .pipe(replace(
80 | "{TxtDiffSvg}", getSvgSection("TxtDiffSvg")
81 | ))
82 | .pipe(replace(
83 | "{LoadingSvg}", getSvgSection("LoadingSvg")
84 | ))
85 | .pipe(replace(
86 | "{SwitchSvg}", getSvgSection("SwitchSvg")
87 | ))
88 | .pipe(replace(
89 | "{AboutSvg}", getSvgSection("AboutSvg")
90 | ))
91 | .pipe(htmlmin({collapseWhitespace: true}))
92 | .pipe(gulp.dest(appPath));
93 | });
94 |
95 | gulp.task('pack', () => {
96 | const ELECTRON_VERSION = "1.7.9";
97 | const APP_VERSION = "1.0.0";
98 | let pack = {}
99 | pack.macOS = `electron-packager ./app 'GramTools' --platform=darwin --arch=x64 --electron-version=${ELECTRON_VERSION} --overwrite --asar=true --prune --icon=app/assets/img/logo128x128.icns --out=dist --app-version=${APP_VERSION}`
100 | pack.win64 = `electron-packager ./app 'GramTools' --platform=win32 --arch=x64 --electron-version=${ELECTRON_VERSION} --overwrite --asar=true --prune --icon=app/assets/img/logo128x128.ico --out=dist --app-version=${APP_VERSION}`
101 | pack.win32 = `electron-packager ./app 'GramTools' --platform=win32 --arch=ia32 --electron-version=${ELECTRON_VERSION} --overwrite --asar=true --prune --icon=app/assets/img/logo128x128.ico --out=dist --app-version=${APP_VERSION}`
102 | pack.linux = `electron-packager ./app 'GramTools' --platform=linux --arch=x64 --electron-version=${ELECTRON_VERSION} --overwrite --asar=true --prune --icon=app/assets/img/logo128x128.ico --out=dist --app-version=${APP_VERSION}`
103 |
104 | let cmds = []
105 | cmds = [pack.macOS, pack.win64, pack.win32, pack.linux]
106 |
107 | console.log(cmds.join('\n'))
108 | exec(cmds.join('\n'), (error, stdout, stderr) => {
109 | console.log('end pack.')
110 | if (error) {
111 | console.error(`exec error: ${error}`)
112 | }
113 | })
114 | })
--------------------------------------------------------------------------------
/sections/svg/JsonFormatSvg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/js/TxtDiff.js:
--------------------------------------------------------------------------------
1 | var JsDiff = require("diff");
2 | import Tools from "./Tools.js";
3 | const tools = new Tools();
4 | function handleLine(leftTxt, rightTxt, resultObj) {
5 | let diff = JsDiff.diffLines(leftTxt, rightTxt, {
6 | ignoreWhitespace: false
7 | });
8 | let lineLeft = 0, lineRight = 0;
9 | let lines = [];
10 | let element = "";
11 | let parts;
12 | console.log(diff);
13 | for (let i=0 ; i < diff.length; i++) {
14 | if (diff[i].value[diff[i].value.length - 1] == "\n") {
15 | diff[i].value = diff[i].value.substr(0, diff[i].value.length - 1);
16 | }
17 | }
18 |
19 | diff.forEach(function(part){
20 | parts = part.value.split("\n");
21 | if (part.added) {
22 | parts.forEach(function(item) {
23 | item = tools.htmlEntities(item);
24 | lineRight++;
25 | element = "";
26 | lines.push(element);
27 | });
28 | } else if (part.removed) {
29 | parts.forEach(function(item) {
30 | item = tools.htmlEntities(item);
31 | lineLeft++;
32 | element = "";
33 | lines.push(element);
34 | });
35 | } else {
36 | parts.forEach(function(item) {
37 | item = tools.htmlEntities(item);
38 | lineLeft++;
39 | lineRight++;
40 | element = "";
41 | lines.push(element);
42 | });
43 | }
44 | });
45 | let html = lines.join("");
46 | $(resultObj).html("");
47 | $(resultObj).append(html);
48 | }
49 |
50 | function handleChar(leftTxt, rightTxt, resultObj) {
51 | let diff = JsDiff.diffChars(leftTxt, rightTxt);
52 | console.log(diff);
53 | let lines = [], line = 0, partValue, charAtPos;
54 | let element = "", changed = 0, last;
55 | let isOnlyEnter = function(str) {
56 | let ret = true;
57 | for (let i=0; i