commit
3602d8a987
|
@ -1,12 +1,7 @@
|
|||
/wide.exe
|
||||
/wide
|
||||
/main
|
||||
/packer
|
||||
|
||||
/data/workspace/bin/
|
||||
/data/workspace/pkg/
|
||||
/data/workspace/src/
|
||||
/static/user/admin/style.css
|
||||
|
||||
/data/user_workspaces/*/bin/
|
||||
/data/user_workspaces/*/pkg/
|
||||
/data/user_workspaces/*/src/**/*.exe
|
||||
/header
|
||||
/header.exe
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
author: DL88250@gmail.com
|
||||
description: A Web IDE for Teams using Golang.
|
||||
description: A Web-based IDE for Teams using Golang.
|
||||
|
||||
filesets:
|
||||
depth: 10
|
||||
includes:
|
||||
- conf
|
||||
- data
|
||||
- doc
|
||||
- i18n
|
||||
- static
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"Dir": ".",
|
||||
"Template": ".header.txt",
|
||||
"Includes": [
|
||||
"*.go",
|
||||
"*/*.go",
|
||||
"static/css/*.css",
|
||||
"static/js/*.js",
|
||||
"static/js/overwrite/*.js"
|
||||
],
|
||||
"Excludes": [
|
||||
"static/js/lib/*",
|
||||
"static/user/*"
|
||||
],
|
||||
"UseDefaultExcludes": true,
|
||||
"Properties": {
|
||||
"Year": "2014",
|
||||
"Owner": "B3log"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
Copyright (c) {{.Year}}, {{.Owner}}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,17 @@
|
|||
FROM golang:latest
|
||||
MAINTAINER Liang Ding <dl88250@gmail.com>
|
||||
|
||||
ADD . /wide/gogogo/src/github.com/b3log/wide
|
||||
|
||||
RUN useradd wide && chown -R wide:wide /wide
|
||||
USER wide
|
||||
|
||||
ENV GOROOT /usr/src/go
|
||||
ENV GOPATH /wide/gogogo
|
||||
|
||||
RUN go get -v github.com/88250/ide_stub github.com/nsf/gocode github.com/bradfitz/goimports
|
||||
|
||||
WORKDIR /wide/gogogo/src/github.com/b3log/wide
|
||||
RUN go get -v && go build -v
|
||||
|
||||
EXPOSE 7070
|
20
README.md
20
README.md
|
@ -1,5 +1,7 @@
|
|||
# Wide [![Build Status](https://drone.io/github.com/b3log/wide/status.png)](https://drone.io/github.com/b3log/wide/latest)
|
||||
|
||||
_Have a [**try**](http://121.41.106.121:7070/signup) first!_
|
||||
|
||||
## Intro
|
||||
|
||||
A <b>W</b>eb-based <b>IDE</b> for Teams using Golang.
|
||||
|
@ -98,17 +100,22 @@ We have provided OS-specific executable binary as follows:
|
|||
* windows-amd64/386
|
||||
* darwin-amd64/386
|
||||
|
||||
Download [here](http://pan.baidu.com/s/1dD3XwOT)!
|
||||
Download [HERE](http://pan.baidu.com/s/1dD3XwOT)!
|
||||
|
||||
### Build Wide for yourself
|
||||
|
||||
1. Download source or by `git clone`
|
||||
1. [Download](https://github.com/b3log/wide/archive/master.zip) source or by `git clone`
|
||||
2. Get dependencies with
|
||||
* `go get -u`
|
||||
* `go get -u github.com/88250/ide_stub`
|
||||
* `go get -u github.com/nsf/gocode`
|
||||
* `go get`
|
||||
* `go get github.com/88250/ide_stub`
|
||||
* `go get github.com/nsf/gocode`
|
||||
3. Compile wide with `go build`
|
||||
|
||||
### Docker
|
||||
|
||||
1. Get image: `sudo docker pull 88250/wide:latest`
|
||||
2. Run: `sudo docker run -u wide -p {ip}:{port}:7070 88250/wide:latest ./wide -docker=true -channel=ws://{ip}:{port}`
|
||||
|
||||
## Known Issues
|
||||
|
||||
* [Shell is not available on Windows](https://github.com/b3log/wide/issues/32)
|
||||
|
@ -127,8 +134,9 @@ Licensed under the [Apache License 2.0](https://github.com/b3log/wide/blob/maste
|
|||
* [LiteIDE](https://github.com/visualfc/liteide)
|
||||
* [gocode](https://github.com/nsf/gocode)
|
||||
* [Gorilla](https://github.com/gorilla)
|
||||
* [GoBuild](http://gobuild.io)
|
||||
* [Docker](https://docker.com)
|
||||
|
||||
----
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/873584/4606328/4e848b96-5219-11e4-8db1-fa12774b57b4.png" width="256px" />
|
||||
</center>
|
||||
|
|
182
conf/wide.go
182
conf/wide.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Configurations manipulations, all configurations (including user configurations) are stored in wide.json.
|
||||
package conf
|
||||
|
||||
|
@ -7,7 +21,7 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
@ -51,23 +65,24 @@ type User struct {
|
|||
type Editor struct {
|
||||
FontFamily string
|
||||
FontSize string
|
||||
LineHeight string
|
||||
}
|
||||
|
||||
// Configuration.
|
||||
type conf struct {
|
||||
IP string // server ip, ${ip}
|
||||
Server string // server host and port ({IP}:7070)
|
||||
StaticServer string // static resources server scheme, host and port (http://{IP}:7070)
|
||||
EditorChannel string // editor channel (ws://{IP}:7070)
|
||||
OutputChannel string // output channel (ws://{IP}:7070)
|
||||
ShellChannel string // shell channel(ws://{IP}:7070)
|
||||
SessionChannel string // wide session channel (ws://{IP}:7070)
|
||||
Port string // server port
|
||||
Server string // server host and port ({IP}:{Port})
|
||||
StaticServer string // static resources server scheme, host and port (http://{IP}:{Port})
|
||||
EditorChannel string // editor channel (ws://{IP}:{Port})
|
||||
OutputChannel string // output channel (ws://{IP}:{Port})
|
||||
ShellChannel string // shell channel(ws://{IP}:{Port})
|
||||
SessionChannel string // wide session channel (ws://{IP}:{Port})
|
||||
HTTPSessionMaxAge int // HTTP session max age (in seciond)
|
||||
StaticResourceVersion string // version of static resources
|
||||
MaxProcs int // Go max procs
|
||||
RuntimeMode string // runtime mode (dev/prod)
|
||||
WD string // current working direcitory, ${pwd}
|
||||
Workspace string // path of master workspace
|
||||
Locale string // default locale
|
||||
Users []*User // configurations of users
|
||||
}
|
||||
|
@ -95,28 +110,31 @@ func FixedTimeCheckEnv() {
|
|||
}
|
||||
|
||||
func checkEnv() {
|
||||
cmd := exec.Command("go", "version")
|
||||
buf, err := cmd.CombinedOutput()
|
||||
if nil != err {
|
||||
glog.Fatal("Not found 'go' command, please make sure Go has been installed correctly")
|
||||
|
||||
os.Exit(-1)
|
||||
}
|
||||
glog.V(5).Info(string(buf))
|
||||
|
||||
if "" == os.Getenv("GOPATH") {
|
||||
glog.Fatal("Not found $GOPATH, please configure it before running Wide")
|
||||
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
if "" == os.Getenv("GOROOT") {
|
||||
glog.Fatal("Not found $GOROOT, please configure it before running Wide")
|
||||
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
gocode := Wide.GetExecutableInGOBIN("gocode")
|
||||
cmd := exec.Command(gocode, "close")
|
||||
_, err := cmd.Output()
|
||||
gocode := util.Go.GetExecutableInGOBIN("gocode")
|
||||
cmd = exec.Command(gocode, "close")
|
||||
_, err = cmd.Output()
|
||||
if nil != err {
|
||||
event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}
|
||||
|
||||
glog.Warningf("Not found gocode [%s]", gocode)
|
||||
}
|
||||
|
||||
ide_stub := Wide.GetExecutableInGOBIN("ide_stub")
|
||||
ide_stub := util.Go.GetExecutableInGOBIN("ide_stub")
|
||||
cmd = exec.Command(ide_stub, "version")
|
||||
_, err = cmd.Output()
|
||||
if nil != err {
|
||||
|
@ -141,25 +159,14 @@ func FixedTimeSave() {
|
|||
func (c *conf) GetUserWorkspace(username string) string {
|
||||
for _, user := range c.Users {
|
||||
if user.Name == username {
|
||||
ret := strings.Replace(user.Workspace, "{WD}", c.WD, 1)
|
||||
|
||||
return filepath.FromSlash(ret)
|
||||
return user.GetWorkspace()
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetWorkspace gets the master workspace path.
|
||||
//
|
||||
// Compared to the use of Wide.Workspace, this function will be processed as follows:
|
||||
// 1. Replace {WD} variable with the actual directory path
|
||||
// 2. Replace "/" with "\\" (Windows)
|
||||
func (c *conf) GetWorkspace() string {
|
||||
return filepath.FromSlash(strings.Replace(c.Workspace, "{WD}", c.WD, 1))
|
||||
}
|
||||
|
||||
// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found.
|
||||
// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found "goimports".
|
||||
func (c *conf) GetGoFmt(username string) string {
|
||||
for _, user := range c.Users {
|
||||
if user.Name == username {
|
||||
|
@ -167,7 +174,7 @@ func (c *conf) GetGoFmt(username string) string {
|
|||
case "gofmt":
|
||||
return "gofmt"
|
||||
case "goimports":
|
||||
return c.GetExecutableInGOBIN("goimports")
|
||||
return util.Go.GetExecutableInGOBIN("goimports")
|
||||
default:
|
||||
glog.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
|
||||
return "gofmt"
|
||||
|
@ -182,9 +189,13 @@ func (c *conf) GetGoFmt(username string) string {
|
|||
//
|
||||
// Compared to the use of Wide.Workspace, this function will be processed as follows:
|
||||
// 1. Replace {WD} variable with the actual directory path
|
||||
// 2. Replace "/" with "\\" (Windows)
|
||||
// 2. Replace ${GOPATH} with enviorment variable GOPATH
|
||||
// 3. Replace "/" with "\\" (Windows)
|
||||
func (u *User) GetWorkspace() string {
|
||||
return filepath.FromSlash(strings.Replace(u.Workspace, "{WD}", Wide.WD, 1))
|
||||
w := strings.Replace(u.Workspace, "{WD}", Wide.WD, 1)
|
||||
w = strings.Replace(w, "${GOPATH}", os.Getenv("GOPATH"), 1)
|
||||
|
||||
return filepath.FromSlash(w)
|
||||
}
|
||||
|
||||
// GetUser gets configuration of the user specified by the given username, returns nil if not found.
|
||||
|
@ -198,42 +209,6 @@ func (*conf) GetUser(username string) *User {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetExecutableInGOBIN gets executable file under GOBIN path.
|
||||
//
|
||||
// The specified executable should not with extension, this function will append .exe if on Windows.
|
||||
func (*conf) GetExecutableInGOBIN(executable string) string {
|
||||
if util.OS.IsWindows() {
|
||||
executable += ".exe"
|
||||
}
|
||||
|
||||
gopaths := filepath.SplitList(os.Getenv("GOPATH"))
|
||||
|
||||
for _, gopath := range gopaths {
|
||||
// $GOPATH/bin/$GOOS_$GOARCH/executable
|
||||
ret := gopath + PathSeparator + "bin" + PathSeparator +
|
||||
os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") + PathSeparator + executable
|
||||
if isExist(ret) {
|
||||
return ret
|
||||
}
|
||||
|
||||
// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/executable
|
||||
ret = gopath + PathSeparator + "bin" + PathSeparator +
|
||||
runtime.GOOS + "_" + runtime.GOARCH + PathSeparator + executable
|
||||
if isExist(ret) {
|
||||
return ret
|
||||
}
|
||||
|
||||
// $GOPATH/bin/executable
|
||||
ret = gopath + PathSeparator + "bin" + PathSeparator + executable
|
||||
if isExist(ret) {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
// $GOBIN/executable
|
||||
return os.Getenv("GOBIN") + PathSeparator + executable
|
||||
}
|
||||
|
||||
// Save saves Wide configurations.
|
||||
func Save() bool {
|
||||
// just the Users field are volatile
|
||||
|
@ -258,12 +233,12 @@ func Save() bool {
|
|||
}
|
||||
|
||||
// Load loads the configurations from wide.json.
|
||||
func Load() {
|
||||
bytes, _ := ioutil.ReadFile("conf/wide.json")
|
||||
func Load(confPath, confIP, confPort, confServer, confChannel string, confDocker bool) {
|
||||
bytes, _ := ioutil.ReadFile(confPath)
|
||||
|
||||
err := json.Unmarshal(bytes, &Wide)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
glog.Error("Parses wide.json error: ", err)
|
||||
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
@ -271,6 +246,11 @@ func Load() {
|
|||
// keep the raw content
|
||||
json.Unmarshal(bytes, &rawWide)
|
||||
|
||||
// Working Driectory
|
||||
Wide.WD = util.OS.Pwd()
|
||||
glog.V(5).Infof("${pwd} [%s]", Wide.WD)
|
||||
|
||||
// IP
|
||||
ip, err := util.Net.LocalIP()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
|
@ -280,17 +260,54 @@ func Load() {
|
|||
|
||||
glog.V(5).Infof("${ip} [%s]", ip)
|
||||
|
||||
Wide.WD = util.OS.Pwd()
|
||||
glog.V(5).Infof("${pwd} [%s]", Wide.WD)
|
||||
if confDocker {
|
||||
// TODO: may be we need to do something here
|
||||
}
|
||||
|
||||
if "" != confIP {
|
||||
ip = confIP
|
||||
}
|
||||
|
||||
Wide.IP = strings.Replace(Wide.IP, "${ip}", ip, 1)
|
||||
|
||||
if "" != confPort {
|
||||
Wide.Port = confPort
|
||||
}
|
||||
|
||||
// Server
|
||||
Wide.Server = strings.Replace(Wide.Server, "{IP}", Wide.IP, 1)
|
||||
if "" != confServer {
|
||||
Wide.Server = confServer
|
||||
}
|
||||
|
||||
// Static Server
|
||||
Wide.StaticServer = strings.Replace(Wide.StaticServer, "{IP}", Wide.IP, 1)
|
||||
Wide.StaticResourceVersion = strings.Replace(Wide.StaticResourceVersion, "${time}", strconv.FormatInt(time.Now().UnixNano(), 10), 1)
|
||||
|
||||
// Channels
|
||||
Wide.EditorChannel = strings.Replace(Wide.EditorChannel, "{IP}", Wide.IP, 1)
|
||||
if "" != confChannel {
|
||||
Wide.EditorChannel = confChannel
|
||||
}
|
||||
Wide.OutputChannel = strings.Replace(Wide.OutputChannel, "{IP}", Wide.IP, 1)
|
||||
if "" != confChannel {
|
||||
Wide.OutputChannel = confChannel
|
||||
}
|
||||
Wide.ShellChannel = strings.Replace(Wide.ShellChannel, "{IP}", Wide.IP, 1)
|
||||
if "" != confChannel {
|
||||
Wide.ShellChannel = confChannel
|
||||
}
|
||||
Wide.SessionChannel = strings.Replace(Wide.SessionChannel, "{IP}", Wide.IP, 1)
|
||||
if "" != confChannel {
|
||||
Wide.SessionChannel = confChannel
|
||||
}
|
||||
|
||||
Wide.Server = strings.Replace(Wide.Server, "{Port}", Wide.Port, 1)
|
||||
Wide.StaticServer = strings.Replace(Wide.StaticServer, "{Port}", Wide.Port, 1)
|
||||
Wide.EditorChannel = strings.Replace(Wide.EditorChannel, "{Port}", Wide.Port, 1)
|
||||
Wide.OutputChannel = strings.Replace(Wide.OutputChannel, "{Port}", Wide.Port, 1)
|
||||
Wide.ShellChannel = strings.Replace(Wide.ShellChannel, "{Port}", Wide.Port, 1)
|
||||
Wide.SessionChannel = strings.Replace(Wide.SessionChannel, "{Port}", Wide.Port, 1)
|
||||
|
||||
glog.V(5).Info("Conf: \n" + string(bytes))
|
||||
|
||||
|
@ -331,7 +348,7 @@ func UpdateCustomizedConf(username string) {
|
|||
|
||||
wd := util.OS.Pwd()
|
||||
dir := filepath.Clean(wd + "/static/user/" + u.Name)
|
||||
if err := os.MkdirAll(dir, 0664); nil != err {
|
||||
if err := os.MkdirAll(dir, 0755); nil != err {
|
||||
glog.Error(err)
|
||||
|
||||
os.Exit(-1)
|
||||
|
@ -353,11 +370,11 @@ func UpdateCustomizedConf(username string) {
|
|||
}
|
||||
}
|
||||
|
||||
// initWorkspaceDirs initializes the directories of master workspace, users' workspaces.
|
||||
// initWorkspaceDirs initializes the directories of users' workspaces.
|
||||
//
|
||||
// Creates directories if not found on path of workspace.
|
||||
func initWorkspaceDirs() {
|
||||
paths := filepath.SplitList(Wide.GetWorkspace())
|
||||
paths := []string{}
|
||||
|
||||
for _, user := range Wide.Users {
|
||||
paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
|
||||
|
@ -383,7 +400,7 @@ func CreateWorkspaceDir(path string) {
|
|||
|
||||
// createDir creates a directory on the path if it not exists.
|
||||
func createDir(path string) {
|
||||
if !isExist(path) {
|
||||
if !util.File.IsExist(path) {
|
||||
if err := os.MkdirAll(path, 0775); nil != err {
|
||||
glog.Error(err)
|
||||
|
||||
|
@ -393,10 +410,3 @@ func createDir(path string) {
|
|||
glog.V(7).Infof("Created a directory [%s]", path)
|
||||
}
|
||||
}
|
||||
|
||||
// isExist determines whether the file spcified by the given filename is exists.
|
||||
func isExist(filename string) bool {
|
||||
_, err := os.Stat(filename)
|
||||
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
||||
|
|
|
@ -1,40 +1,36 @@
|
|||
{
|
||||
"IP": "${ip}",
|
||||
"Server": "{IP}:7070",
|
||||
"StaticServer": "http://{IP}:7070",
|
||||
"EditorChannel": "ws://{IP}:7070",
|
||||
"OutputChannel": "ws://{IP}:7070",
|
||||
"ShellChannel": "ws://{IP}:7070",
|
||||
"SessionChannel": "ws://{IP}:7070",
|
||||
"Port": "7070",
|
||||
"Server": "{IP}:{Port}",
|
||||
"StaticServer": "",
|
||||
"EditorChannel": "ws://{IP}:{Port}",
|
||||
"OutputChannel": "ws://{IP}:{Port}",
|
||||
"ShellChannel": "ws://{IP}:{Port}",
|
||||
"SessionChannel": "ws://{IP}:{Port}",
|
||||
"HTTPSessionMaxAge": 86400,
|
||||
"StaticResourceVersion": "201411062300",
|
||||
"StaticResourceVersion": "${time}",
|
||||
"MaxProcs": 4,
|
||||
"RuntimeMode": "dev",
|
||||
"WD": "${pwd}",
|
||||
"Workspace": "{WD}/data/workspace",
|
||||
"Locale": "en_US",
|
||||
"Users": [
|
||||
{
|
||||
"Name": "admin",
|
||||
"Password": "admin",
|
||||
"Workspace": "{WD}/data/user_workspaces/admin",
|
||||
"Workspace": "${GOPATH}",
|
||||
"Locale": "en_US",
|
||||
"GoFormat": "gofmt",
|
||||
"FontFamily": "Helvetica",
|
||||
"FontSize": "13px",
|
||||
"Editor": {
|
||||
"FontFamily": "Consolas, 'Courier New', monospace",
|
||||
"FontSize": "inherit"
|
||||
"FontSize": "13px",
|
||||
"LineHeight": "17px"
|
||||
},
|
||||
"LatestSessionContent": {
|
||||
"FileTree": [
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src",
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\time"
|
||||
],
|
||||
"Files": [
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\time\\main.go"
|
||||
],
|
||||
"CurrentFile": "D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\time\\main.go"
|
||||
"FileTree": [],
|
||||
"Files": [],
|
||||
"CurrentFile": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
User workspaces.
|
|
@ -1,319 +0,0 @@
|
|||
/* start icon */
|
||||
|
||||
@font-face {
|
||||
font-family: 'icomoon';
|
||||
src: url('fonts/icomoon.eot?35cb2z');
|
||||
src: url('fonts/icomoon.eot?#iefix35cb2z') format('embedded-opentype'), url('fonts/icomoon.woff?35cb2z') format('woff'), url('fonts/icomoon.ttf?35cb2z') format('truetype'), url('fonts/icomoon.svg?35cb2z#icomoon') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
.font-ico {
|
||||
font-family: 'icomoon';
|
||||
/* Better Font Rendering =========== */
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.font-ico:hover {
|
||||
color: #333;
|
||||
}
|
||||
.ico-play:before {
|
||||
content: "\e605";
|
||||
}
|
||||
.ico-save:before {
|
||||
content: "\f0c7";
|
||||
}
|
||||
.ico-max:before {
|
||||
content: "\f096";
|
||||
}
|
||||
.ico-format:before {
|
||||
content: "\e60b";
|
||||
}
|
||||
.ico-buildrun:before {
|
||||
content: "\e607";
|
||||
}
|
||||
.ico-stop:before {
|
||||
content: "\e608";
|
||||
}
|
||||
.ico-restore:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
.ico-min:before {
|
||||
content: "\e60d";
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
}
|
||||
.ico-close:before {
|
||||
content: "\e60a";
|
||||
}
|
||||
/* end ico */
|
||||
/* start frame */
|
||||
|
||||
.frame {
|
||||
position: absolute;
|
||||
border: 1px solid #5F5F5F;
|
||||
background-color: #F8F8F8;
|
||||
width: 320px;
|
||||
z-index: 21;
|
||||
display: none;
|
||||
}
|
||||
.frame li {
|
||||
padding: 0 5px;
|
||||
line-height: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.frame li.disabled {
|
||||
color: #999;
|
||||
}
|
||||
.frame li:hover {
|
||||
background-color: #3875D7;
|
||||
color: #FFF;
|
||||
}
|
||||
.frame li.disabled:hover {
|
||||
background-color: #F8F8F8;
|
||||
color: #999;
|
||||
}
|
||||
.frame a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
.frame li:hover a,
|
||||
.frame a:hover {
|
||||
color: #FFF;
|
||||
}
|
||||
.frame .hr {
|
||||
background-color: #bdbdbd;
|
||||
height: 1px;
|
||||
margin: 0 1px;
|
||||
}
|
||||
/* end frame */
|
||||
/* start tabs */
|
||||
|
||||
.tabs {
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
background-color: #E6E6E6;
|
||||
border-top: 1px solid #A4A4A4;
|
||||
border-bottom: 1px solid #9D9D9D;
|
||||
}
|
||||
.tabs > div {
|
||||
float: left;
|
||||
line-height: 18px;
|
||||
height: 18px;
|
||||
padding: 0 5px;
|
||||
cursor: pointer;
|
||||
background-color: #DDD;
|
||||
color: #8B8B8B;
|
||||
border-right: 1px solid #ADADAD;
|
||||
}
|
||||
.tabs > div.current {
|
||||
background-color: #9F9F9F;
|
||||
color: #FFF;
|
||||
}
|
||||
/* end tabs */
|
||||
/* start framework */
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* end framework */
|
||||
/* start menu */
|
||||
|
||||
.menu {
|
||||
background-color: #F0F0F0;
|
||||
height: 24px;
|
||||
}
|
||||
.menu > ul > li {
|
||||
float: left;
|
||||
}
|
||||
.menu > ul > li > span {
|
||||
color: #000;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
padding: 5px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
/* end menu */
|
||||
/* start editor */
|
||||
|
||||
.edit-panel {
|
||||
width: 80%;
|
||||
position: absolute;
|
||||
left: 20%;
|
||||
width: 80%;
|
||||
height: 70%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.edit-panel .tabs > div {
|
||||
background-color: #d1d1d1;
|
||||
border-right-color: #9b9b9b;
|
||||
color: #333;
|
||||
cursor: auto;
|
||||
}
|
||||
.edit-panel .tabs > div.current {
|
||||
background-color: #F7F7F7;
|
||||
}
|
||||
.toolbars {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 1px;
|
||||
}
|
||||
.edit-panel .tabs .ico {
|
||||
background-image: url("../images/ico-file.png");
|
||||
float: left;
|
||||
height: 16px;
|
||||
margin: 2px 0 0 -2px;
|
||||
width: 16px;
|
||||
}
|
||||
/* 统一为 static/js/lib/codemirror-4.5/addon/hint/show-hint.css 中的.CodeMirror-hints */
|
||||
|
||||
.edit-exprinfo {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
-webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
|
||||
-moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
|
||||
box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
|
||||
border-radius: 3px;
|
||||
border: 1px solid silver;
|
||||
background: white;
|
||||
font-size: 90%;
|
||||
font-family: Consolas, Courier New, monospace;
|
||||
max-height: 20em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.CodeMirror,
|
||||
.CodeMirror-hints {
|
||||
font-family: Consolas, Courier New, monospace;
|
||||
}
|
||||
.CodeMirror-focused .cm-matchhighlight {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
|
||||
background-position: bottom;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.CodeMirror-hint {
|
||||
padding-right: 18px;
|
||||
max-width: none;
|
||||
}
|
||||
.CodeMirror-hint:hover {
|
||||
background: #08f;
|
||||
color: white;
|
||||
}
|
||||
/* end editor */
|
||||
/* start bottom-window-group */
|
||||
|
||||
.bottom-window-group {
|
||||
width: 80%;
|
||||
position: absolute;
|
||||
left: 20%;
|
||||
width: 80%;
|
||||
height: 30%;
|
||||
top: 70%;
|
||||
z-index: 7;
|
||||
background-color: #fff;
|
||||
}
|
||||
.bottom-window-group-max {
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 11;
|
||||
}
|
||||
.bottom-window-group > div > div {
|
||||
overflow: auto;
|
||||
}
|
||||
.bottom-window-group .output {
|
||||
font-family: Consolas, Courier New, monospace;
|
||||
padding: 0 5px;
|
||||
line-height: 16px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.bottom-window-group .output pre {
|
||||
margin: 0;
|
||||
}
|
||||
.bottom-window-group .output .start-build,
|
||||
.bottom-window-group .output .start-test,
|
||||
.bottom-window-group .output .start-install,
|
||||
.bottom-window-group .output .start-get {
|
||||
color: #999;
|
||||
}
|
||||
.bottom-window-group .output .build-succ,
|
||||
.bottom-window-group .output .test-succ,
|
||||
.bottom-window-group .output .install-succ,
|
||||
.bottom-window-group .output .get-succ {
|
||||
color: rgb(0, 153, 0);
|
||||
}
|
||||
.bottom-window-group .output .build-error,
|
||||
.bottom-window-group .output .test-error,
|
||||
.bottom-window-group .output .install-error,
|
||||
.bottom-window-group .output .get-error {
|
||||
color: red;
|
||||
}
|
||||
.bottom-window-group table {
|
||||
width: 100%;
|
||||
}
|
||||
.bottom-window-group td {
|
||||
border-bottom: 1px solid #DDD;
|
||||
line-height: 20px;
|
||||
}
|
||||
.bottom-window-group .notification .type,
|
||||
.bottom-window-group .notification .severity {
|
||||
width: 50px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.bottom-window-group .search li {
|
||||
cursor: pointer;
|
||||
line-height: 20px;
|
||||
padding: 0 3px;
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.bottom-window-group .search li.selected {
|
||||
background-color: #3875d7;
|
||||
color: #FFF;
|
||||
}
|
||||
.bottom-window-group .search .path {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
.bottom-window-group .search li.selected .path {
|
||||
color: #FFF;
|
||||
}
|
||||
/* end bottom-window-group */
|
||||
/* start footer */
|
||||
|
||||
.footer {
|
||||
border-top: 1px solid #919191;
|
||||
background-color: #F0F0F0;
|
||||
padding-left: 5px;
|
||||
height: 19px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.footer .cursor {
|
||||
cursor: pointer;
|
||||
}
|
||||
.notification-count {
|
||||
float: right;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
background-color: red;
|
||||
color: #FFF;
|
||||
margin: 1px 5px;
|
||||
padding: 0 2px;
|
||||
border-radius: 3px;
|
||||
line-height: 16px;
|
||||
}
|
||||
/* end footer */
|
|
@ -1,333 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{.i18n.wide}}</title>
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/codemirror.css">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/hint/show-hint.css">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/lint/lint.css">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/foldgutter.css">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/dialog/dialog.css">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/overwrite/codemirror/theme/wide.css">
|
||||
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/lib/ztree/zTreeStyle.css">
|
||||
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/dialog.css?{{.conf.StaticResourceVersion}}">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/base.css?{{.conf.StaticResourceVersion}}">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/base-{{.conf.Locale}}.css?{{.conf.StaticResourceVersion}}">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/wide.css?{{.conf.StaticResourceVersion}}">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/side.css?{{.conf.StaticResourceVersion}}">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/start.css?{{.conf.StaticResourceVersion}}">
|
||||
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/about.css?{{.conf.StaticResourceVersion}}">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- 主菜单 -->
|
||||
<div class="menu fn-clear">
|
||||
<ul class="fn-cleaer">
|
||||
<li>
|
||||
<span>{{.i18n.file}}</span>
|
||||
<div class="frame">
|
||||
<ul>
|
||||
<li class="save-all disabled" onclick="wide.saveAllFiles()">
|
||||
<span>{{.i18n.save_all_files}}</span>
|
||||
</li>
|
||||
<li class="close-all disabled" onclick="wide.closeAllFiles()">
|
||||
<span>{{.i18n.close_all_files}}</span>
|
||||
</li>
|
||||
<li class="hr"></li>
|
||||
<li onclick="wide.exit()">
|
||||
<span>{{.i18n.exit}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<span>{{.i18n.run}}</span>
|
||||
<div class="frame">
|
||||
<ul>
|
||||
<li class="build disabled" onclick="wide.build()">
|
||||
<span>{{.i18n.build}}</span>
|
||||
</li>
|
||||
<li class="run disabled" onclick="wide.run()">
|
||||
<span>{{.i18n.build_n_run}}</span>
|
||||
</li>
|
||||
<li class="hr"></li>
|
||||
<li class="go-test disabled" onclick="wide.test()">
|
||||
<span>{{.i18n.test}}</span>
|
||||
</li>
|
||||
<li class="hr"></li>
|
||||
<li class="go-get disabled" onclick="wide.goget()">
|
||||
<span>{{.i18n.goget}}</span>
|
||||
</li>
|
||||
<li class="go-install disabled" onclick="wide.goinstall()">
|
||||
<span>{{.i18n.goinstall}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!--
|
||||
<li>
|
||||
<span>{{.i18n.debug}}</span>
|
||||
<div class="frame">
|
||||
<ul>
|
||||
<li>
|
||||
<span>{{.i18n.debug}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
-->
|
||||
<li>
|
||||
<span>{{.i18n.team}}</span>
|
||||
<div class="frame">
|
||||
<ul>
|
||||
<li class="signup" onclick="window.open('/signup')">
|
||||
<span>{{.i18n.sign_up}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<span>{{.i18n.help}}</span>
|
||||
<div class="frame">
|
||||
<ul>
|
||||
<li onclick="window.open('https://www.gitbook.io/book/88250/wide-user-guide')">
|
||||
<span>{{.i18n.wide_doc}}</span>
|
||||
</li>
|
||||
<li onclick="window.open('https://github.com/b3log/wide/issues/new')">
|
||||
{{.i18n.report_issues}}
|
||||
</li>
|
||||
<li class="hr"></li>
|
||||
<li onclick="window.open('/keyboard_shortcuts')">
|
||||
{{.i18n.keyboard_shortcuts}}
|
||||
</li>
|
||||
<li class="hr"></li>
|
||||
<li onclick="editors.openStartPage()">
|
||||
<span>{{.i18n.start_page}}</span>
|
||||
</li>
|
||||
<li onclick="wide.openAbout()">
|
||||
<span>{{.i18n.about}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="side">
|
||||
<span title="{{.i18n.min}}" class="font-ico ico-min"></span>
|
||||
<div class="tabs">
|
||||
<div class="current" data-index="filreTree">
|
||||
<span title="{{.i18n.file}}">{{.i18n.file}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs-panel">
|
||||
<div data-index="filreTree">
|
||||
<ul id="files" tabindex="-1" class="ztree"></ul>
|
||||
<!-- 目录右键菜单 -->
|
||||
<div id="dirRMenu" class="frame">
|
||||
<ul>
|
||||
<li onclick="tree.newFile();">{{.i18n.create_file}}</li>
|
||||
<li onclick="tree.newDir();">{{.i18n.create_dir}}</li>
|
||||
<li onclick="tree.removeIt();">{{.i18n.delete}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 文件右键菜单 -->
|
||||
<div id="fileRMenu" class="frame">
|
||||
<ul>
|
||||
<li onclick="tree.removeIt();">{{.i18n.delete}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="edit-panel">
|
||||
<div class="toolbars fn-none">
|
||||
<span onclick="wide.run()" class="font-ico ico-buildrun" title="{{.i18n.build_n_run}}"></span>
|
||||
<span onclick="wide.saveFile()" title="{{.i18n.save}}" class="font-ico ico-save"></span>
|
||||
<span onclick="wide.fmt(editors.getCurrentPath(), wide.curEditor)" class="ico-format font-ico" title="{{.i18n.format}}"></span>
|
||||
<span class="font-ico ico-max" onclick="windows.maxEditor()" title="{{.i18n.max_editor}}"></span>
|
||||
</div>
|
||||
<div class="tabs"></div>
|
||||
<div class="tabs-panel"></div>
|
||||
</div>
|
||||
|
||||
<div class="bottom-window-group">
|
||||
<span title="{{.i18n.min}}" class="font-ico ico-min"></span>
|
||||
<div class="tabs">
|
||||
<div class="current" data-index="output">
|
||||
<span title="{{.i18n.output}}">{{.i18n.output}}</span>
|
||||
</div>
|
||||
<div data-index="search">
|
||||
<span title="{{.i18n.search}}">{{.i18n.search}}</span>
|
||||
</div>
|
||||
<div data-index="notification">
|
||||
<span title="{{.i18n.notification}}">{{.i18n.notification}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs-panel">
|
||||
<div data-index="output">
|
||||
<div class="output" tabindex="-1">
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fn-none" data-index="search">
|
||||
<div tabindex="-1" class="search">
|
||||
<div class="tabs fn-none">
|
||||
<div class="current" data-index="first">
|
||||
<span class="first"></span><span class="ico-close font-ico"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs-panel">
|
||||
<div data-index="first"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fn-none" data-index="notification">
|
||||
<div tabindex="-1" class="notification">
|
||||
<table cellpadding="0" cellspacing="0"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer fn-clear">
|
||||
<div class="fn-left">
|
||||
<span title="{{.i18n.restore_side}}" class="font-ico ico-restore fn-none"></span>
|
||||
<span title="{{.i18n.restore_bottom}}" class="font-ico ico-restore fn-none"></span>
|
||||
</div>
|
||||
<div class="fn-right">
|
||||
<span class="cursor"></span>
|
||||
<span class="notification-count" title="{{.i18n.unread_notification}}">{{.i18n.notification}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dialogRemoveConfirm" class="fn-none">
|
||||
{{.i18n.isDelete}} <b></b>?
|
||||
</div>
|
||||
<div id="dialogAlert" class="fn-none"></div>
|
||||
<div id="dialogAbout" class="fn-none"></div>
|
||||
<div id="dialogNewFilePrompt" class="dialog-prompt fn-none">
|
||||
<input/>
|
||||
</div>
|
||||
<div id="dialogNewDirPrompt" class="dialog-prompt fn-none">
|
||||
<input/>
|
||||
</div>
|
||||
<div id="dialogGoLinePrompt" class="dialog-prompt fn-none">
|
||||
<input/>
|
||||
</div>
|
||||
<div id="dialogSearchForm" class="dialog-form fn-none">
|
||||
<input placeholder="{{.i18n.keyword}}" />
|
||||
<input placeholder="{{.i18n.file_format}}" />
|
||||
</div>
|
||||
<script>
|
||||
var config = {
|
||||
"pathSeparator": {
|
||||
{.pathSeparator
|
||||
}
|
||||
},
|
||||
"latestSessionContent": {
|
||||
{.latestSessionContent
|
||||
}
|
||||
},
|
||||
"label": {
|
||||
"restore_editor": "{{.i18n.restore_editor}}",
|
||||
"max_editor": "{{.i18n.max_editor}}",
|
||||
"delete": "{{.i18n.delete}}",
|
||||
"cancel": "{{.i18n.cancel}}",
|
||||
"goto_line": "{{.i18n.goto_line}}",
|
||||
"go": "{{.i18n.go}}",
|
||||
"create": "{{.i18n.create}}",
|
||||
"create_file": "{{.i18n.create_file}}",
|
||||
"create_dir": "{{.i18n.create_dir}}",
|
||||
"tip": "{{.i18n.tip}}",
|
||||
"confirm": "{{.i18n.confirm}}",
|
||||
"build_n_run": "{{.i18n.build_n_run}}",
|
||||
"stop": "{{.i18n.stop}}",
|
||||
"find_usages": "{{.i18n.find_usages}}",
|
||||
"search_text": "{{.i18n.search_text}}",
|
||||
"search": "{{.i18n.search}}",
|
||||
"start_page": "{{.i18n.start_page}}",
|
||||
"confirm_save": "{{.i18n.confirm_save}}",
|
||||
"community": "{{.i18n.community}}",
|
||||
"about": "{{.i18n.about}}",
|
||||
"new_version_available": "{{.i18n.new_version_available}}",
|
||||
"colon": "{{.i18n.colon}}",
|
||||
"uptodate": "{{.i18n.uptodate}}"
|
||||
},
|
||||
"channel": {
|
||||
"editor": '{{.conf.EditorChannel}}',
|
||||
"shell": '{{.conf.ShellChannel}}',
|
||||
"output": '{{.conf.OutputChannel}}',
|
||||
"session": '{{.conf.SessionChannel}}'
|
||||
},
|
||||
"wideSessionId": '{{.session.Id}}'
|
||||
};
|
||||
// 发往 Wide 的所有 AJAX 请求需要使用该函数创建请求参数.
|
||||
function newWideRequest() {
|
||||
var ret = {
|
||||
sid: config.wideSessionId
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/jquery-2.1.1.min.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/reconnecting-websocket.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/ztree/jquery.ztree.all-3.5.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/codemirror.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/lint/lint.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/lint/json-lint.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/selection/active-line.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/overwrite/codemirror/addon/hint/show-hint.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/hint/anyword-hint.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/display/rulers.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/edit/closebrackets.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/edit/matchbrackets.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/edit/closetag.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/search/searchcursor.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/search/search.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/dialog/dialog.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/search/match-highlighter.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/foldcode.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/foldgutter.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/brace-fold.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/xml-fold.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/markdown-fold.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/comment-fold.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/go/go.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/xml/xml.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/htmlmixed/htmlmixed.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/javascript/javascript.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/markdown/markdown.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/css/css.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/shell/shell.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/sql/sql.js"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/lint/json-lint.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/lint/go-lint.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/js-beautify-1.5.4/beautify.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/js-beautify-1.5.4/beautify-html.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/js-beautify-1.5.4/beautify-css.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/tabs.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/dialog.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/editors.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/notification.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/tree.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/wide.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/session.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/menu.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/windows.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/hotkeys.js?{{.conf.StaticResourceVersion}}"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"Server": "{IP}:7070",
|
||||
"StaticServer": "http://{IP}:7070",
|
||||
"EditorChannel": "ws://{IP}:7070",
|
||||
"OutputChannel": "ws://{IP}:7070",
|
||||
"ShellChannel": "ws://{IP}:7070",
|
||||
"SessionChannel": "ws://{IP}:7070",
|
||||
"HTTPSessionMaxAge": 86400,
|
||||
"StaticResourceVersion": "201410271700",
|
||||
"MaxProcs": 4,
|
||||
"RuntimeMode": "dev",
|
||||
"Pwd": "{pwd}",
|
||||
"Workspace": "{pwd}/data/workspace",
|
||||
"Locale": "en_US",
|
||||
"Users": [{
|
||||
"Name": "admin",
|
||||
"Password": "admin",
|
||||
"Workspace": "{pwd}/data/user_workspaces/admin",
|
||||
"Locale": "en_US",
|
||||
"GoFormat": "gofmt",
|
||||
"LatestSessionContent": {
|
||||
"FileTree": [
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src",
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\format",
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\gotest",
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\hello",
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\time"
|
||||
],
|
||||
"Files": [
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\time\\main.go",
|
||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\format\\index.html"
|
||||
],
|
||||
"CurrentFile": "D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\format\\index.html"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Division_1(t *testing.T) {
|
||||
if i, e := Division(6, 2); i != 3 || e != nil { //try a unit test on function
|
||||
t.Error("除法函数测试没通过") // 如果不是如预期的那么就报错
|
||||
} else {
|
||||
t.Log("第一个测试通过了") //记录一些你期望记录的信息
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Division_2(t *testing.T) {
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Division_3(t *testing.T) {
|
||||
if i, e := Division(6, 2); i != 3 || e != nil { //try a unit test on function
|
||||
t.Error("除法函数测试没通过") // 如果不是如预期的那么就报错
|
||||
} else {
|
||||
t.Log("第一个测试通过了") //记录一些你期望记录的信息
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Division_4(t *testing.T) {
|
||||
t.Error("就是不通过")
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
func Division(a, b float64) (float64, error) {
|
||||
if b == 0 {
|
||||
return 0, errors.New("除数不能为0")
|
||||
}
|
||||
|
||||
return a / b, nil
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, 世界")
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"time/pkg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 50; i++ {
|
||||
fmt.Println("Hello, 世界", pkg.Now())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package pkg
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
func Now1() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
func Now2() {
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
Master workspace.
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Editor manipulations.
|
||||
package editor
|
||||
|
||||
|
@ -24,30 +38,30 @@ import (
|
|||
// WSHandler handles request of creating editor channel.
|
||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if httpSession.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
sid := httpSession.Values["id"].(string)
|
||||
|
||||
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||
editorChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
||||
|
||||
session.EditorWS[sid] = &editorChan
|
||||
|
||||
ret := map[string]interface{}{"output": "Editor initialized", "cmd": "init-editor"}
|
||||
editorChan.Conn.WriteJSON(&ret)
|
||||
err := editorChan.WriteJSON(&ret)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
session.EditorWS[sid] = &editorChan
|
||||
|
||||
glog.Infof("Open a new [Editor] with session [%s], %d", sid, len(session.EditorWS))
|
||||
|
||||
args := map[string]interface{}{}
|
||||
for {
|
||||
if err := session.EditorWS[sid].Conn.ReadJSON(&args); err != nil {
|
||||
if err.Error() == "EOF" {
|
||||
return
|
||||
}
|
||||
|
||||
if err.Error() == "unexpected EOF" {
|
||||
return
|
||||
}
|
||||
|
||||
glog.Error("Editor WS ERROR: " + err.Error())
|
||||
if err := session.EditorWS[sid].ReadJSON(&args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -59,7 +73,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// glog.Infof("offset: %d", offset)
|
||||
|
||||
gocode := conf.Wide.GetExecutableInGOBIN("gocode")
|
||||
gocode := util.Go.GetExecutableInGOBIN("gocode")
|
||||
argv := []string{"-f=json", "autocomplete", strconv.Itoa(offset)}
|
||||
|
||||
var output bytes.Buffer
|
||||
|
@ -75,7 +89,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
ret = map[string]interface{}{"output": string(output.Bytes()), "cmd": "autocomplete"}
|
||||
|
||||
if err := session.EditorWS[sid].Conn.WriteJSON(&ret); err != nil {
|
||||
if err := session.EditorWS[sid].WriteJSON(&ret); err != nil {
|
||||
glog.Error("Editor WS ERROR: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -94,6 +108,11 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if session.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := session.Values["username"].(string)
|
||||
|
||||
path := args["path"].(string)
|
||||
|
@ -136,7 +155,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
glog.V(5).Infof("gocode set lib-path %s", libPath)
|
||||
|
||||
// FIXME: using gocode set lib-path has some issues while accrossing workspaces
|
||||
gocode := conf.Wide.GetExecutableInGOBIN("gocode")
|
||||
gocode := util.Go.GetExecutableInGOBIN("gocode")
|
||||
argv := []string{"set", "lib-path", libPath}
|
||||
exec.Command(gocode, argv...).Run()
|
||||
|
||||
|
@ -205,7 +224,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// glog.Infof("offset [%d]", offset)
|
||||
|
||||
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
||||
ide_stub := util.Go.GetExecutableInGOBIN("ide_stub")
|
||||
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-info", "."}
|
||||
cmd := exec.Command(ide_stub, argv...)
|
||||
cmd.Dir = curDir
|
||||
|
@ -236,6 +255,11 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.RetJSON(w, r, data)
|
||||
|
||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if session.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := session.Values["username"].(string)
|
||||
|
||||
var args map[string]interface{}
|
||||
|
@ -276,7 +300,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// glog.Infof("offset [%d]", offset)
|
||||
|
||||
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
||||
ide_stub := util.Go.GetExecutableInGOBIN("ide_stub")
|
||||
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-def", "."}
|
||||
cmd := exec.Command(ide_stub, argv...)
|
||||
cmd.Dir = curDir
|
||||
|
@ -315,6 +339,11 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.RetJSON(w, r, data)
|
||||
|
||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if session.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := session.Values["username"].(string)
|
||||
|
||||
var args map[string]interface{}
|
||||
|
@ -355,7 +384,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
|||
offset := getCursorOffset(code, line, ch)
|
||||
// glog.Infof("offset [%d]", offset)
|
||||
|
||||
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
||||
ide_stub := util.Go.GetExecutableInGOBIN("ide_stub")
|
||||
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-use", "."}
|
||||
cmd := exec.Command(ide_stub, argv...)
|
||||
cmd.Dir = curDir
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package editor
|
||||
|
||||
import (
|
||||
|
@ -5,8 +19,6 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/b3log/wide/conf"
|
||||
"github.com/b3log/wide/session"
|
||||
|
@ -24,6 +36,11 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.RetJSON(w, r, data)
|
||||
|
||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if session.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := session.Values["username"].(string)
|
||||
|
||||
var args map[string]interface{}
|
||||
|
@ -37,8 +54,7 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
filePath := args["file"].(string)
|
||||
|
||||
apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg"
|
||||
if strings.HasPrefix(filePath, apiPath) { // if it is Go API source code
|
||||
if util.Go.IsAPI(filePath) {
|
||||
// ignore it
|
||||
return
|
||||
}
|
||||
|
@ -70,7 +86,9 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
|||
bytes, _ := cmd.Output()
|
||||
output := string(bytes)
|
||||
if "" == output {
|
||||
data["succ"] = false
|
||||
// format error, returns the original content
|
||||
data["succ"] = true
|
||||
data["code"] = code
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Event manipulations.
|
||||
package event
|
||||
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/b3log/wide/util"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// GetZip handles request of retrieving zip file.
|
||||
func GetZip(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
path := q["path"][0]
|
||||
|
||||
if ".zip" != filepath.Ext(path) {
|
||||
http.Error(w, "Bad Request", 400)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !util.File.IsExist(path) {
|
||||
http.Error(w, "Not Found", 404)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filename := filepath.Base(path)
|
||||
|
||||
w.Header().Set("Content-Disposition", "attachment; filename="+filename)
|
||||
w.Header().Set("Content-type", "application/zip")
|
||||
http.ServeFile(w, r, path)
|
||||
|
||||
os.Remove(path)
|
||||
}
|
||||
|
||||
// CreateZip handles request of creating zip.
|
||||
func CreateZip(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
||||
var args map[string]interface{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||
glog.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
path := args["path"].(string)
|
||||
base := filepath.Base(path)
|
||||
|
||||
if !util.File.IsExist(path) {
|
||||
data["succ"] = false
|
||||
data["msg"] = "Can't find file [" + path + "]"
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
zipFile, err := util.Zip.Create(path + ".zip")
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
if util.File.IsDir(path) {
|
||||
zipFile.AddDirectory(base, path)
|
||||
} else {
|
||||
zipFile.AddEntry(base, path)
|
||||
}
|
||||
}
|
268
file/files.go
268
file/files.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// File tree manipulations.
|
||||
package file
|
||||
|
||||
|
@ -7,7 +21,6 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
@ -21,7 +34,6 @@ import (
|
|||
// File node, used to construct the file tree.
|
||||
type FileNode struct {
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Path string `json:"path"`
|
||||
IconSkin string `json:"iconSkin"` // Value should be end with a space
|
||||
Type string `json:"type"` // "f": file, "d": directory
|
||||
|
@ -39,28 +51,49 @@ type Snippet struct {
|
|||
Contents []string `json:"contents"` // lines nearby
|
||||
}
|
||||
|
||||
var apiNode *FileNode
|
||||
|
||||
// initAPINode builds the Go API file node.
|
||||
func initAPINode() {
|
||||
apiPath := util.Go.GetAPIPath()
|
||||
|
||||
apiNode = &FileNode{Name: "Go API", Path: apiPath, IconSkin: "ico-ztree-dir-api ", Type: "d",
|
||||
Creatable: false, Removable: false, FileNodes: []*FileNode{}}
|
||||
|
||||
walk(apiPath, apiNode, false, false)
|
||||
}
|
||||
|
||||
// GetFiles handles request of constructing user workspace file tree.
|
||||
//
|
||||
// The Go API source code package ($GOROOT/src/pkg) also as a child node,
|
||||
// so that users can easily view the Go API source code.
|
||||
// The Go API source code package also as a child node,
|
||||
// so that users can easily view the Go API source code in file tree.
|
||||
func GetFiles(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if session.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := session.Values["username"].(string)
|
||||
|
||||
userWorkspace := conf.Wide.GetUserWorkspace(username)
|
||||
workspaces := filepath.SplitList(userWorkspace)
|
||||
|
||||
root := FileNode{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", FileNodes: []*FileNode{}}
|
||||
|
||||
if nil == apiNode { // lazy init
|
||||
initAPINode()
|
||||
}
|
||||
|
||||
// workspace node process
|
||||
for _, workspace := range workspaces {
|
||||
workspacePath := workspace + conf.PathSeparator + "src"
|
||||
|
||||
workspaceNode := FileNode{Name: workspace[strings.LastIndex(workspace, conf.PathSeparator)+1:],
|
||||
Title: workspace, Path: workspacePath, IconSkin: "ico-ztree-dir-workspace ", Type: "d",
|
||||
Path: workspacePath, IconSkin: "ico-ztree-dir-workspace ", Type: "d",
|
||||
Creatable: true, Removable: false, FileNodes: []*FileNode{}}
|
||||
|
||||
walk(workspacePath, &workspaceNode, true, true)
|
||||
|
@ -69,28 +102,31 @@ func GetFiles(w http.ResponseWriter, r *http.Request) {
|
|||
root.FileNodes = append(root.FileNodes, &workspaceNode)
|
||||
}
|
||||
|
||||
// construct Go API node
|
||||
apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg"
|
||||
apiNode := FileNode{Name: "Go API", Title: apiPath, Path: apiPath, IconSkin: "ico-ztree-dir-api ", Type: "d",
|
||||
Creatable: false, Removable: false, FileNodes: []*FileNode{}}
|
||||
|
||||
goapiBuildOKSignal := make(chan bool)
|
||||
go func() {
|
||||
walk(apiPath, &apiNode, false, false)
|
||||
|
||||
// go-ahead
|
||||
close(goapiBuildOKSignal)
|
||||
}()
|
||||
|
||||
// waiting
|
||||
<-goapiBuildOKSignal
|
||||
|
||||
// add Go API node
|
||||
root.FileNodes = append(root.FileNodes, &apiNode)
|
||||
root.FileNodes = append(root.FileNodes, apiNode)
|
||||
|
||||
data["root"] = root
|
||||
}
|
||||
|
||||
// RefreshDirectory handles request of refresh a directory of file tree.
|
||||
func RefreshDirectory(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
path := r.FormValue("path")
|
||||
|
||||
node := FileNode{Name: "root", Path: path, IconSkin: "ico-ztree-dir ", Type: "d", FileNodes: []*FileNode{}}
|
||||
|
||||
walk(path, &node, true, true)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
data, err := json.Marshal(node.FileNodes)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
// GetFile handles request of opening file by editor.
|
||||
func GetFile(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
|
@ -106,25 +142,40 @@ func GetFile(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
path := args["path"].(string)
|
||||
|
||||
size := util.File.GetFileSize(path)
|
||||
if size > 5242880 { // 5M
|
||||
data["succ"] = false
|
||||
data["msg"] = "This file is too large to open :("
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
buf, _ := ioutil.ReadFile(path)
|
||||
|
||||
extension := filepath.Ext(path)
|
||||
|
||||
if isImg(extension) {
|
||||
if util.File.IsImg(extension) {
|
||||
// image file will be open in a browser tab
|
||||
|
||||
data["mode"] = "img"
|
||||
|
||||
path2 := strings.Replace(path, "\\", "/", -1)
|
||||
idx := strings.Index(path2, "/data/user_workspaces")
|
||||
data["path"] = path2[idx:]
|
||||
user := GetUsre(path)
|
||||
if nil == user {
|
||||
glog.Warningf("The path [%s] has no owner")
|
||||
data["path"] = ""
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
data["path"] = "/workspace/" + user.Name + "/" + strings.Replace(path, user.GetWorkspace(), "", 1)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
content := string(buf)
|
||||
|
||||
if isBinary(content) {
|
||||
if util.File.IsBinary(content) {
|
||||
data["succ"] = false
|
||||
data["msg"] = "Can't open a binary file :("
|
||||
} else {
|
||||
|
@ -262,10 +313,70 @@ func RenameFile(w http.ResponseWriter, r *http.Request) {
|
|||
data["succ"] = false
|
||||
|
||||
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
|
||||
Data: "can't rename file " + path}
|
||||
Data: "can't rename file " + oldPath}
|
||||
}
|
||||
}
|
||||
|
||||
// Use to find results sorting.
|
||||
type foundPath struct {
|
||||
Path string `json:"path"`
|
||||
score int
|
||||
}
|
||||
|
||||
type foundPaths []*foundPath
|
||||
|
||||
func (f foundPaths) Len() int { return len(f) }
|
||||
func (f foundPaths) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
||||
func (f foundPaths) Less(i, j int) bool { return f[i].score > f[j].score }
|
||||
|
||||
// Find handles request of find files under the specified directory with the specified filename pattern.
|
||||
func Find(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
||||
var args map[string]interface{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||
glog.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
path := args["path"].(string) // path of selected file in file tree
|
||||
name := args["name"].(string)
|
||||
|
||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if session.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := session.Values["username"].(string)
|
||||
|
||||
userWorkspace := conf.Wide.GetUserWorkspace(username)
|
||||
workspaces := filepath.SplitList(userWorkspace)
|
||||
|
||||
if "" != path && !util.File.IsDir(path) {
|
||||
path = filepath.Dir(path)
|
||||
}
|
||||
|
||||
founds := foundPaths{}
|
||||
|
||||
for _, workspace := range workspaces {
|
||||
rs := find(workspace+conf.PathSeparator+"src", name, []*string{})
|
||||
|
||||
for _, r := range rs {
|
||||
substr := util.Str.LCS(path, *r)
|
||||
|
||||
founds = append(founds, &foundPath{Path: *r, score: len(substr)})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(founds)
|
||||
|
||||
data["founds"] = founds
|
||||
}
|
||||
|
||||
// SearchText handles request of searching files under the specified directory with the specified keyword.
|
||||
func SearchText(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
|
@ -298,7 +409,7 @@ func walk(path string, node *FileNode, creatable, removable bool) {
|
|||
|
||||
fio, _ := os.Lstat(fpath)
|
||||
|
||||
child := FileNode{Name: filename, Title: fpath, Path: fpath, Removable: removable, FileNodes: []*FileNode{}}
|
||||
child := FileNode{Name: filename, Path: fpath, Removable: removable, FileNodes: []*FileNode{}}
|
||||
node.FileNodes = append(node.FileNodes, &child)
|
||||
|
||||
if nil == fio {
|
||||
|
@ -340,7 +451,14 @@ func listFiles(dirname string) []string {
|
|||
|
||||
// sort: directories in front of files
|
||||
for _, name := range names {
|
||||
fio, _ := os.Lstat(filepath.Join(dirname, name))
|
||||
path := filepath.Join(dirname, name)
|
||||
fio, err := os.Lstat(path)
|
||||
|
||||
if nil != err {
|
||||
glog.Warningf("Can't read file info [%s]", path)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if fio.IsDir() {
|
||||
// exclude the .git direcitory
|
||||
|
@ -361,7 +479,7 @@ func listFiles(dirname string) []string {
|
|||
//
|
||||
// Refers to the zTree document for CSS class names.
|
||||
func getIconSkin(filenameExtension string) string {
|
||||
if isImg(filenameExtension) {
|
||||
if util.File.IsImg(filenameExtension) {
|
||||
return "ico-ztree-img "
|
||||
}
|
||||
|
||||
|
@ -426,7 +544,7 @@ func getEditorMode(filenameExtension string) string {
|
|||
func createFile(path, fileType string) bool {
|
||||
switch fileType {
|
||||
case "f":
|
||||
file, err := os.OpenFile(path, os.O_CREATE, 0664)
|
||||
file, err := os.OpenFile(path, os.O_CREATE, 0775)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
|
||||
|
@ -465,7 +583,7 @@ func removeFile(path string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
glog.Infof("Removed [%s]", path)
|
||||
glog.V(5).Infof("Removed [%s]", path)
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -473,17 +591,69 @@ func removeFile(path string) bool {
|
|||
// renameFile renames (moves) a file from the specified old path to the specified new path.
|
||||
func renameFile(oldPath, newPath string) bool {
|
||||
if err := os.Rename(oldPath, newPath); nil != err {
|
||||
glog.Errorf("Renames [%s] failed: [%s]", path, err.Error())
|
||||
glog.Errorf("Renames [%s] failed: [%s]", oldPath, err.Error())
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
glog.Infof("Renamed [%s] to [%s]", oldPath, newPath)
|
||||
glog.V(5).Infof("Renamed [%s] to [%s]", oldPath, newPath)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// search finds file under the specified dir and its sub-directories with the specified text, likes the command grep/findstr.
|
||||
// Default exclude file name patterns when find.
|
||||
var defaultExcludesFind = []string{".git", ".svn", ".repository", "CVS", "RCS", "SCCS", ".bzr", ".metadata", ".hg"}
|
||||
|
||||
// find finds files under the specified dir and its sub-directoryies with the specified name,
|
||||
// likes the command 'find dir -name name'.
|
||||
func find(dir, name string, results []*string) []*string {
|
||||
if !strings.HasSuffix(dir, conf.PathSeparator) {
|
||||
dir += conf.PathSeparator
|
||||
}
|
||||
|
||||
f, _ := os.Open(dir)
|
||||
fileInfos, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
|
||||
if nil != err {
|
||||
glog.Errorf("Read dir [%s] failed: [%s]", dir, err.Error())
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
for _, fileInfo := range fileInfos {
|
||||
fname := fileInfo.Name()
|
||||
path := dir + fname
|
||||
|
||||
if fileInfo.IsDir() {
|
||||
if util.Str.Contains(fname, defaultExcludesFind) {
|
||||
continue
|
||||
}
|
||||
|
||||
// enter the directory recursively
|
||||
results = find(path, name, results)
|
||||
} else {
|
||||
// match filename
|
||||
pattern := filepath.Dir(path) + conf.PathSeparator + name
|
||||
match, err := filepath.Match(pattern, path)
|
||||
|
||||
if nil != err {
|
||||
glog.Errorf("Find match filename failed: [%s]", err.Error)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if match {
|
||||
results = append(results, &path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// search finds file under the specified dir and its sub-directories with the specified text, likes the command 'grep'
|
||||
// or 'findstr'.
|
||||
func search(dir, extension, text string, snippets []*Snippet) []*Snippet {
|
||||
if !strings.HasSuffix(dir, conf.PathSeparator) {
|
||||
dir += conf.PathSeparator
|
||||
|
@ -528,7 +698,7 @@ func searchInFile(path string, text string) []*Snippet {
|
|||
}
|
||||
|
||||
content := string(bytes)
|
||||
if isBinary(content) {
|
||||
if util.File.IsBinary(content) {
|
||||
return ret
|
||||
}
|
||||
|
||||
|
@ -547,25 +717,13 @@ func searchInFile(path string, text string) []*Snippet {
|
|||
return ret
|
||||
}
|
||||
|
||||
// isBinary determines whether the specified content is a binary file content.
|
||||
func isBinary(content string) bool {
|
||||
for _, b := range content {
|
||||
if 0 == b {
|
||||
return true
|
||||
// GetUsre gets the user the specified path belongs to. Returns nil if not found.
|
||||
func GetUsre(path string) *conf.User {
|
||||
for _, user := range conf.Wide.Users {
|
||||
if strings.HasPrefix(path, user.GetWorkspace()) {
|
||||
return user
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// isImg determines whether the specified extension is a image.
|
||||
func isImg(extension string) bool {
|
||||
ext := strings.ToLower(extension)
|
||||
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg", ".bmp", ".gif", ".png", ".svg", ".ico":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"create": "Create",
|
||||
"create_dir": "Create Dir",
|
||||
"delete": "Delete",
|
||||
"rename": "Rename",
|
||||
"save": "Save",
|
||||
"exit": "Exit",
|
||||
"close_all_files": "Close All",
|
||||
|
@ -39,6 +40,7 @@
|
|||
"notification_3": "Not found [ide_stub], thereby [Jump to Decl], [Find Usages] will not work",
|
||||
"notification_4": "Server Internal Error",
|
||||
"goto_line": "Goto Line",
|
||||
"goto_file": "Goto File",
|
||||
"go": "Go",
|
||||
"tip": "Tip",
|
||||
"confirm": "Confirm",
|
||||
|
@ -73,8 +75,10 @@
|
|||
"show_expr_info": "Show Expression Info",
|
||||
"find_usages": "Find Usages",
|
||||
"delete_line": "Delete Line",
|
||||
"copy_line_up": "Copy Line Up",
|
||||
"copy_line_down": "Copy Line Down",
|
||||
"copy_lines_up": "Copy Lines Up",
|
||||
"copy_lines_down": "Copy Lines Down",
|
||||
"move_lines_up": "Move Lines Up",
|
||||
"move_lines_down": "Move Lines Down",
|
||||
"save_editor_file": "Save File",
|
||||
"save_all_editors_files": "Save All",
|
||||
"close_editor": "Close File",
|
||||
|
@ -118,5 +122,18 @@
|
|||
"discard": "Discard",
|
||||
"close": "Close",
|
||||
"close_other": "Close Other",
|
||||
"clear": "Clear"
|
||||
"clear": "Clear",
|
||||
"perference": "Perference",
|
||||
"appearence": "Appearence",
|
||||
"gotool": "Go Tool",
|
||||
"user": "User",
|
||||
"font": "Font",
|
||||
"font_size": "Font Size",
|
||||
"line_height": "Line Height",
|
||||
"go_format": "Go Format",
|
||||
"locale": "Locale",
|
||||
"apply": "Apply",
|
||||
"clearOutput": "Clear Output",
|
||||
"export": "Export",
|
||||
"refresh": "Refresh"
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
"create": "作成",
|
||||
"create_dir": "新規ディレクトリ",
|
||||
"delete": "削除",
|
||||
"rename": "名前の変更",
|
||||
"save": "保存",
|
||||
"exit": "終了",
|
||||
"close_all_files": "全てのファイルを閉じる",
|
||||
|
@ -39,6 +40,7 @@
|
|||
"notification_3": "[ide_stub] が見つかりません。[Jump to Decl]、[Find Usages] は動作しません。",
|
||||
"notification_4": "内部サーバーエラー",
|
||||
"goto_line": "指定行にジャンプ",
|
||||
"goto_file": "ファイルをオープンする",
|
||||
"go": "Go",
|
||||
"tip": "ヒント",
|
||||
"confirm": "確認",
|
||||
|
@ -73,8 +75,10 @@
|
|||
"show_expr_info": "式の情報を表示",
|
||||
"find_usages": "使用方法を検索する",
|
||||
"delete_line": "行を削除",
|
||||
"copy_line_up": "前行にコピー",
|
||||
"copy_line_down": "次行にコピー",
|
||||
"copy_lines_up": "フロントへのコピー",
|
||||
"copy_lines_down": "一番下にコピー",
|
||||
"move_lines_up": "前面に移動します",
|
||||
"move_lines_down": "以下に移動",
|
||||
"save_editor_file": "保存",
|
||||
"save_all_editors_files": "全てを保存",
|
||||
"close_editor": "エディタを閉じる",
|
||||
|
@ -118,5 +122,18 @@
|
|||
"discard": "あきらめる",
|
||||
"close": "クローズ",
|
||||
"close_other": "閉じるその他",
|
||||
"clear": "空の"
|
||||
"clear": "空の",
|
||||
"perference": "偏好设定",
|
||||
"appearence": "エクステリア",
|
||||
"gotool": "Go ツール",
|
||||
"user": "ユーザー",
|
||||
"font": "フォント",
|
||||
"font_size": "フォント·サイズ",
|
||||
"line_height": "行の高さ",
|
||||
"go_format": "Go フォーマット",
|
||||
"locale": "ロケール",
|
||||
"apply": "適用する",
|
||||
"clearOutput": "空の出力",
|
||||
"export": "輸出",
|
||||
"refresh": "リフレッシュ"
|
||||
}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Internationalization manipulations.
|
||||
package i18n
|
||||
|
||||
|
@ -67,3 +81,14 @@ func Get(locale, key string) interface{} {
|
|||
func GetAll(locale string) map[string]interface{} {
|
||||
return Locales[locale].Langs
|
||||
}
|
||||
|
||||
// GetLocalesNames gets names of all locales. Returns ["zh_CN", "en_US"] for example.
|
||||
func GetLocalesNames() []string {
|
||||
ret := []string{}
|
||||
|
||||
for name, _ := range Locales {
|
||||
ret = append(ret, name)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"create": "创建",
|
||||
"create_dir": "创建目录",
|
||||
"delete": "删除",
|
||||
"rename": "重命名",
|
||||
"save": "保存",
|
||||
"exit": "退出",
|
||||
"close_all_files": "关闭所有文件",
|
||||
|
@ -39,6 +40,7 @@
|
|||
"notification_3": "没有检查到 ide_stub,这将会导致 [跳转到声明]、[查找使用] 失效",
|
||||
"notification_4": "服务器内部错误",
|
||||
"goto_line": "跳转到行",
|
||||
"goto_file": "打开文件",
|
||||
"go": "跳转",
|
||||
"tip": "提示",
|
||||
"confirm": "确定",
|
||||
|
@ -73,8 +75,10 @@
|
|||
"show_expr_info": "查看表达式信息",
|
||||
"find_usages": "查找使用",
|
||||
"delete_line": "删除当前行",
|
||||
"copy_line_up": "复制当前行到上一行",
|
||||
"copy_line_down": "复制当前行到下一行",
|
||||
"copy_lines_up": "复制到上方",
|
||||
"copy_lines_down": "复制到下方",
|
||||
"move_lines_up": "移动到上方",
|
||||
"move_lines_down": "移动到下方",
|
||||
"save_editor_file": "保存当前编辑器文件",
|
||||
"save_all_editors_files": "保存所有编辑器文件",
|
||||
"close_editor": "关闭当前编辑器",
|
||||
|
@ -118,5 +122,18 @@
|
|||
"discard": "放弃",
|
||||
"close": "关闭",
|
||||
"close_other": "关闭其它",
|
||||
"clear": "清空"
|
||||
"clear": "清空",
|
||||
"perference": "偏好设定",
|
||||
"appearence": "外观",
|
||||
"gotool": "Go 工具",
|
||||
"user": "用户",
|
||||
"font": "字体",
|
||||
"font_size": "字体大小",
|
||||
"line_height": "行高",
|
||||
"go_format": "Go 格式化",
|
||||
"locale": "语言环境",
|
||||
"apply": "应用",
|
||||
"clearOutput": "清空输出",
|
||||
"export": "导出",
|
||||
"refresh": "刷新"
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
"create": "建立",
|
||||
"create_dir": "建立目錄",
|
||||
"delete": "删除",
|
||||
"rename": "重命名",
|
||||
"save": "儲存",
|
||||
"exit": "退出",
|
||||
"close_all_files": "關閉所有文件",
|
||||
|
@ -39,6 +40,7 @@
|
|||
"notification_3": "没有檢查到 ide_stub,這將會導致 [跳轉到聲明]、[查找使用] 失效",
|
||||
"notification_4": "服務器內部錯誤",
|
||||
"goto_line": "跳轉到行",
|
||||
"goto_file": "打開文件",
|
||||
"go": "跳到",
|
||||
"tip": "提示",
|
||||
"confirm": "確定",
|
||||
|
@ -73,8 +75,10 @@
|
|||
"show_expr_info": "查看表達式信息",
|
||||
"find_usages": "尋找使用",
|
||||
"delete_line": "删除當前行",
|
||||
"copy_line_up": "插入當前行到上一行",
|
||||
"copy_line_down": "插入當前行到下一行",
|
||||
"copy_lines_up": "複製到上方",
|
||||
"copy_lines_down": "複製到下方",
|
||||
"move_lines_up": "移動到上方",
|
||||
"move_lines_down": "移動到下方",
|
||||
"save_editor_file": "保存當前編輯器文件",
|
||||
"save_all_editors_files": "保存所有編輯器文件",
|
||||
"close_editor": "關閉當前編輯器",
|
||||
|
@ -118,5 +122,18 @@
|
|||
"discard": "放棄",
|
||||
"close": "關閉",
|
||||
"close_other": "關閉其它",
|
||||
"clear": "清空"
|
||||
"clear": "清空",
|
||||
"perference": "偏好設定",
|
||||
"appearence": "外觀",
|
||||
"gotool": "Go 工具",
|
||||
"user": "用戶",
|
||||
"font": "字形",
|
||||
"font_size": "字體大小",
|
||||
"line_height": "行高",
|
||||
"go_format": "Go 格式化",
|
||||
"locale": "語言環境",
|
||||
"apply": "應用",
|
||||
"clearOutput": "清空輸出",
|
||||
"export": "導出",
|
||||
"refresh": "刷新"
|
||||
}
|
64
main.go
64
main.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -6,8 +20,10 @@ import (
|
|||
"math/rand"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/b3log/wide/conf"
|
||||
|
@ -25,26 +41,46 @@ import (
|
|||
|
||||
// The only one init function in Wide.
|
||||
func init() {
|
||||
// TODO: args
|
||||
flag.Set("logtostderr", "true")
|
||||
confPath := flag.String("conf", "conf/wide.json", "path of wide.json")
|
||||
confIP := flag.String("ip", "", "ip to visit")
|
||||
confPort := flag.String("port", "", "port to visit")
|
||||
confServer := flag.String("server", "", "this will overwrite Wide.Server if specified")
|
||||
confChannel := flag.String("channel", "", "this will overwrite Wide.XXXChannel if specified")
|
||||
confStat := flag.Bool("stat", false, "whether report statistics periodically")
|
||||
confDocker := flag.Bool("docker", false, "whether run in a docker container")
|
||||
|
||||
flag.Set("alsologtostderr", "true")
|
||||
flag.Set("stderrthreshold", "INFO")
|
||||
flag.Set("v", "3")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
wd := util.OS.Pwd()
|
||||
if strings.HasPrefix(wd, os.TempDir()) {
|
||||
glog.Error("Don't run wide in OS' temp directory or with `go run`")
|
||||
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
i18n.Load()
|
||||
|
||||
event.Load()
|
||||
|
||||
conf.Load()
|
||||
conf.Load(*confPath, *confIP, *confPort, *confServer, *confChannel, *confDocker)
|
||||
|
||||
conf.FixedTimeCheckEnv()
|
||||
conf.FixedTimeSave()
|
||||
|
||||
session.FixedTimeRelease()
|
||||
|
||||
if *confStat {
|
||||
session.FixedTimeReport()
|
||||
}
|
||||
}
|
||||
|
||||
// indexHandler handles request of Wide index.
|
||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
|
||||
if httpSession.IsNew {
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
|
||||
|
@ -72,10 +108,9 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
|
|||
locale := user.Locale
|
||||
|
||||
wideSessions := session.WideSessions.GetByUsername(username)
|
||||
userConf := conf.Wide.GetUser(username)
|
||||
|
||||
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
|
||||
"session": wideSession, "latestSessionContent": userConf.LatestSessionContent,
|
||||
"session": wideSession, "latestSessionContent": user.LatestSessionContent,
|
||||
"pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer}
|
||||
|
||||
glog.V(3).Infof("User [%s] has [%d] sessions", username, len(wideSessions))
|
||||
|
@ -102,7 +137,6 @@ func serveSingle(pattern string, filename string) {
|
|||
// startHandler handles request of start page.
|
||||
func startHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
|
||||
if httpSession.IsNew {
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
|
||||
|
@ -140,7 +174,6 @@ func startHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// keyboardShortcutsHandler handles request of keyboard shortcuts page.
|
||||
func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
|
||||
if httpSession.IsNew {
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
|
||||
|
@ -170,7 +203,6 @@ func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// aboutHandle handles request of about page.
|
||||
func aboutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
|
||||
if httpSession.IsNew {
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
|
||||
|
@ -217,7 +249,10 @@ func main() {
|
|||
serveSingle("/favicon.ico", "./static/favicon.ico")
|
||||
|
||||
// workspaces
|
||||
http.Handle("/data/", http.StripPrefix("/data/", http.FileServer(http.Dir("data"))))
|
||||
for _, user := range conf.Wide.Users {
|
||||
http.Handle("/workspace/"+user.Name+"/",
|
||||
http.StripPrefix("/workspace/"+user.Name+"/", http.FileServer(http.Dir(user.GetWorkspace()))))
|
||||
}
|
||||
|
||||
// session
|
||||
http.HandleFunc("/session/ws", handlerWrapper(session.WSHandler))
|
||||
|
@ -234,12 +269,18 @@ func main() {
|
|||
|
||||
// file tree
|
||||
http.HandleFunc("/files", handlerWrapper(file.GetFiles))
|
||||
http.HandleFunc("/file/refresh", handlerWrapper(file.RefreshDirectory))
|
||||
http.HandleFunc("/file", handlerWrapper(file.GetFile))
|
||||
http.HandleFunc("/file/save", handlerWrapper(file.SaveFile))
|
||||
http.HandleFunc("/file/new", handlerWrapper(file.NewFile))
|
||||
http.HandleFunc("/file/remove", handlerWrapper(file.RemoveFile))
|
||||
http.HandleFunc("/file/rename", handlerWrapper(file.RenameFile))
|
||||
http.HandleFunc("/file/search/text", handlerWrapper(file.SearchText))
|
||||
http.HandleFunc("/file/find/name", handlerWrapper(file.Find))
|
||||
|
||||
// file export/import
|
||||
http.HandleFunc("/file/zip", handlerWrapper(file.GetZip))
|
||||
http.HandleFunc("/file/zip/new", handlerWrapper(file.CreateZip))
|
||||
|
||||
// editor
|
||||
http.HandleFunc("/editor/ws", handlerWrapper(editor.WSHandler))
|
||||
|
@ -260,8 +301,9 @@ func main() {
|
|||
http.HandleFunc("/login", handlerWrapper(session.LoginHandler))
|
||||
http.HandleFunc("/logout", handlerWrapper(session.LogoutHandler))
|
||||
http.HandleFunc("/signup", handlerWrapper(session.SignUpUser))
|
||||
http.HandleFunc("/preference", handlerWrapper(session.PreferenceHandler))
|
||||
|
||||
glog.V(0).Infof("Wide is running [%s]", conf.Wide.Server)
|
||||
glog.Infof("Wide is running [%s]", conf.Wide.Server)
|
||||
|
||||
err := http.ListenAndServe(conf.Wide.Server, nil)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,11 +1,25 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Notification manipulations.
|
||||
package notification
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"strconv"
|
||||
"github.com/b3log/wide/conf"
|
||||
"github.com/b3log/wide/event"
|
||||
"github.com/b3log/wide/i18n"
|
||||
|
@ -65,7 +79,7 @@ func event2Notification(e *event.Event) {
|
|||
return
|
||||
}
|
||||
|
||||
wsChannel.Conn.WriteJSON(notification)
|
||||
wsChannel.WriteJSON(notification)
|
||||
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
@ -83,6 +97,12 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||
wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
||||
|
||||
ret := map[string]interface{}{"notification": "Notification initialized", "cmd": "init-notification"}
|
||||
err := wsChan.WriteJSON(&ret)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
session.NotificationWS[sid] = &wsChan
|
||||
|
||||
glog.V(4).Infof("Open a new [Notification] with session [%s], %d", sid, len(session.NotificationWS))
|
||||
|
@ -93,17 +113,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
input := map[string]interface{}{}
|
||||
|
||||
for {
|
||||
if err := wsChan.Conn.ReadJSON(&input); err != nil {
|
||||
if err.Error() == "EOF" {
|
||||
return
|
||||
}
|
||||
|
||||
if err.Error() == "unexpected EOF" {
|
||||
return
|
||||
}
|
||||
|
||||
glog.Error("Notification WS ERROR: " + err.Error())
|
||||
|
||||
if err := wsChan.ReadJSON(&input); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Build, run and go tool manipulations.
|
||||
package output
|
||||
|
||||
|
@ -44,10 +58,13 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||
wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
||||
|
||||
session.OutputWS[sid] = &wsChan
|
||||
|
||||
ret := map[string]interface{}{"output": "Ouput initialized", "cmd": "init-output"}
|
||||
wsChan.Conn.WriteJSON(&ret)
|
||||
err := wsChan.WriteJSON(&ret)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
session.OutputWS[sid] = &wsChan
|
||||
|
||||
glog.V(4).Infof("Open a new [Output] with session [%s], %d", sid, len(session.OutputWS))
|
||||
}
|
||||
|
@ -106,7 +123,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
channelRet["cmd"] = "run-done"
|
||||
channelRet["output"] = ""
|
||||
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
return
|
||||
|
@ -127,13 +144,13 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.Recover()
|
||||
defer cmd.Wait()
|
||||
|
||||
glog.V(3).Infof("Session [%s] is running [id=%d, file=%s]", sid, runningId, filePath)
|
||||
glog.V(5).Infof("Session [%s] is running [id=%d, file=%s]", sid, runningId, filePath)
|
||||
|
||||
// push once for front-end to get the 'run' state and pid
|
||||
if nil != wsChannel {
|
||||
channelRet["cmd"] = "run"
|
||||
channelRet["output"] = ""
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
return
|
||||
|
@ -155,12 +172,12 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// remove the exited process from user process set
|
||||
processes.remove(wSession, cmd.Process)
|
||||
|
||||
glog.V(3).Infof("Session [%s] 's running [id=%d, file=%s] has done [stdout err]", sid, runningId, filePath)
|
||||
glog.V(5).Infof("Session [%s] 's running [id=%d, file=%s] has done [stdout err]", sid, runningId, filePath)
|
||||
|
||||
if nil != wsChannel {
|
||||
channelRet["cmd"] = "run-done"
|
||||
channelRet["output"] = "<pre>" + buf + "</pre>"
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
channelRet["output"] = buf
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
break
|
||||
|
@ -173,8 +190,8 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
} else {
|
||||
if nil != wsChannel {
|
||||
channelRet["cmd"] = "run"
|
||||
channelRet["output"] = "<pre>" + buf + "</pre>"
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
channelRet["output"] = buf
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
break
|
||||
|
@ -195,14 +212,14 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// remove the exited process from user process set
|
||||
processes.remove(wSession, cmd.Process)
|
||||
|
||||
glog.V(3).Infof("Session [%s] 's running [id=%d, file=%s] has done [stderr err]", sid, runningId, filePath)
|
||||
glog.V(5).Infof("Session [%s] 's running [id=%d, file=%s] has done [stderr err]", sid, runningId, filePath)
|
||||
|
||||
if nil != session.OutputWS[sid] {
|
||||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
channelRet["cmd"] = "run-done"
|
||||
channelRet["output"] = "<pre>" + buf + "</pre>"
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
channelRet["output"] = buf
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
break
|
||||
|
@ -217,8 +234,8 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
channelRet["cmd"] = "run"
|
||||
channelRet["output"] = "<pre>" + buf + "</pre>"
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
channelRet["output"] = buf
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
break
|
||||
|
@ -237,6 +254,11 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.RetJSON(w, r, data)
|
||||
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if httpSession.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := httpSession.Values["username"].(string)
|
||||
locale := conf.Wide.GetUser(username).Locale
|
||||
|
||||
|
@ -319,7 +341,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
return
|
||||
|
@ -341,7 +363,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.Recover()
|
||||
defer cmd.Wait()
|
||||
|
||||
glog.V(3).Infof("Session [%s] is building [id=%d, dir=%s]", sid, runningId, curDir)
|
||||
glog.V(5).Infof("Session [%s] is building [id=%d, dir=%s]", sid, runningId, curDir)
|
||||
|
||||
// read all
|
||||
buf, _ := ioutil.ReadAll(reader)
|
||||
|
@ -419,10 +441,10 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if nil != session.OutputWS[sid] {
|
||||
glog.V(3).Infof("Session [%s] 's build [id=%d, dir=%s] has done", sid, runningId, curDir)
|
||||
glog.V(5).Infof("Session [%s] 's build [id=%d, dir=%s] has done", sid, runningId, curDir)
|
||||
|
||||
wsChannel := session.OutputWS[sid]
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
@ -439,6 +461,11 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.RetJSON(w, r, data)
|
||||
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if httpSession.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := httpSession.Values["username"].(string)
|
||||
locale := conf.Wide.GetUser(username).Locale
|
||||
|
||||
|
@ -491,7 +518,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
return
|
||||
|
@ -512,7 +539,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
go func(runningId int) {
|
||||
defer util.Recover()
|
||||
|
||||
glog.V(3).Infof("Session [%s] is running [go test] [runningId=%d]", sid, runningId)
|
||||
glog.V(5).Infof("Session [%s] is running [go test] [runningId=%d]", sid, runningId)
|
||||
|
||||
channelRet := map[string]interface{}{}
|
||||
channelRet["cmd"] = "go test"
|
||||
|
@ -524,11 +551,11 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
cmd.Wait()
|
||||
|
||||
if !cmd.ProcessState.Success() {
|
||||
glog.V(3).Infof("Session [%s] 's running [go test] [runningId=%d] has done (with error)", sid, runningId)
|
||||
glog.V(5).Infof("Session [%s] 's running [go test] [runningId=%d] has done (with error)", sid, runningId)
|
||||
|
||||
channelRet["output"] = "<span class='test-error'>" + i18n.Get(locale, "test-error").(string) + "</span>\n" + string(buf)
|
||||
} else {
|
||||
glog.V(3).Infof("Session [%s] 's running [go test] [runningId=%d] has done", sid, runningId)
|
||||
glog.V(5).Infof("Session [%s] 's running [go test] [runningId=%d] has done", sid, runningId)
|
||||
|
||||
channelRet["output"] = "<span class='test-succ'>" + i18n.Get(locale, "test-succ").(string) + "</span>\n" + string(buf)
|
||||
}
|
||||
|
@ -536,7 +563,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if nil != session.OutputWS[sid] {
|
||||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
@ -552,6 +579,11 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.RetJSON(w, r, data)
|
||||
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if httpSession.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := httpSession.Values["username"].(string)
|
||||
locale := conf.Wide.GetUser(username).Locale
|
||||
|
||||
|
@ -606,7 +638,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
return
|
||||
|
@ -628,7 +660,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.Recover()
|
||||
defer cmd.Wait()
|
||||
|
||||
glog.V(3).Infof("Session [%s] is running [go install] [id=%d, dir=%s]", sid, runningId, curDir)
|
||||
glog.V(5).Infof("Session [%s] is running [go install] [id=%d, dir=%s]", sid, runningId, curDir)
|
||||
|
||||
// read all
|
||||
buf, _ := ioutil.ReadAll(reader)
|
||||
|
@ -692,10 +724,10 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if nil != session.OutputWS[sid] {
|
||||
glog.V(3).Infof("Session [%s] 's running [go install] [id=%d, dir=%s] has done", sid, runningId, curDir)
|
||||
glog.V(5).Infof("Session [%s] 's running [go install] [id=%d, dir=%s] has done", sid, runningId, curDir)
|
||||
|
||||
wsChannel := session.OutputWS[sid]
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
@ -712,6 +744,11 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.RetJSON(w, r, data)
|
||||
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if httpSession.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := httpSession.Values["username"].(string)
|
||||
locale := conf.Wide.GetUser(username).Locale
|
||||
|
||||
|
@ -764,7 +801,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
return
|
||||
|
@ -786,7 +823,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
defer util.Recover()
|
||||
defer cmd.Wait()
|
||||
|
||||
glog.V(3).Infof("Session [%s] is running [go get] [runningId=%d]", sid, runningId)
|
||||
glog.V(5).Infof("Session [%s] is running [go get] [runningId=%d]", sid, runningId)
|
||||
|
||||
channelRet := map[string]interface{}{}
|
||||
channelRet["cmd"] = "go get"
|
||||
|
@ -795,11 +832,11 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
buf, _ := ioutil.ReadAll(reader)
|
||||
|
||||
if 0 != len(buf) {
|
||||
glog.V(3).Infof("Session [%s] 's running [go get] [runningId=%d] has done (with error)", sid, runningId)
|
||||
glog.V(5).Infof("Session [%s] 's running [go get] [runningId=%d] has done (with error)", sid, runningId)
|
||||
|
||||
channelRet["output"] = "<span class='get-error'>" + i18n.Get(locale, "get-error").(string) + "</span>\n" + string(buf)
|
||||
} else {
|
||||
glog.V(3).Infof("Session [%s] 's running [go get] [runningId=%d] has done", sid, runningId)
|
||||
glog.V(5).Infof("Session [%s] 's running [go get] [runningId=%d] has done", sid, runningId)
|
||||
|
||||
channelRet["output"] = "<span class='get-succ'>" + i18n.Get(locale, "get-succ").(string) + "</span>\n"
|
||||
|
||||
|
@ -808,7 +845,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if nil != session.OutputWS[sid] {
|
||||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
@ -846,10 +883,9 @@ func StopHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func setCmdEnv(cmd *exec.Cmd, username string) {
|
||||
userWorkspace := conf.Wide.GetUserWorkspace(username)
|
||||
masterWorkspace := conf.Wide.GetWorkspace()
|
||||
|
||||
cmd.Env = append(cmd.Env,
|
||||
"GOPATH="+userWorkspace+conf.PathListSeparator+masterWorkspace,
|
||||
"GOPATH="+userWorkspace,
|
||||
"GOOS="+runtime.GOOS,
|
||||
"GOARCH="+runtime.GOARCH,
|
||||
"GOROOT="+runtime.GOROOT(),
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package output
|
||||
|
||||
import (
|
||||
|
@ -33,7 +47,7 @@ func (procs *procs) add(wSession *session.WideSession, proc *os.Process) {
|
|||
// bind process with wide session
|
||||
wSession.SetProcesses(userProcesses)
|
||||
|
||||
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
||||
glog.V(5).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
||||
}
|
||||
|
||||
// remove removes the specified process from the user process set.
|
||||
|
@ -54,7 +68,7 @@ func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) {
|
|||
// bind process with wide session
|
||||
wSession.SetProcesses(newProcesses)
|
||||
|
||||
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
||||
glog.V(5).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -83,7 +97,7 @@ func (procs *procs) kill(wSession *session.WideSession, pid int) {
|
|||
// bind process with wide session
|
||||
wSession.SetProcesses(newProcesses)
|
||||
|
||||
glog.V(3).Infof("Killed a process [pid=%d] of session [%s]", pid, sid)
|
||||
glog.V(5).Infof("Killed a process [pid=%d] of session [%s]", pid, sid)
|
||||
}
|
||||
|
||||
return
|
||||
|
|
2
pkg.sh
2
pkg.sh
|
@ -9,7 +9,7 @@
|
|||
|
||||
ver=$1
|
||||
target=$2
|
||||
list="conf data doc i18n static views README.md LICENSE"
|
||||
list="conf doc i18n static views README.md LICENSE"
|
||||
|
||||
mkdir -p ${target}
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Session manipulations.
|
||||
//
|
||||
// Wide server side needs maintain two kinds of sessions:
|
||||
|
@ -9,9 +23,11 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -79,7 +95,7 @@ func FixedTimeRelease() {
|
|||
|
||||
for _, s := range WideSessions {
|
||||
if s.Updated.Before(threshold) {
|
||||
glog.V(3).Infof("Removes a invalid session [%s]", s.Id)
|
||||
glog.V(3).Infof("Removes a invalid session [%s], user [%s]", s.Id, s.Username)
|
||||
|
||||
WideSessions.Remove(s.Id)
|
||||
}
|
||||
|
@ -88,6 +104,50 @@ func FixedTimeRelease() {
|
|||
}()
|
||||
}
|
||||
|
||||
// Online user statistic report.
|
||||
type userReport struct {
|
||||
username string
|
||||
sessionCnt int
|
||||
updated time.Time
|
||||
}
|
||||
|
||||
// report returns a online user statistics in pretty format.
|
||||
func (u *userReport) report() string {
|
||||
return "[" + u.username + "] has [" + strconv.Itoa(u.sessionCnt) + "] sessions, latest activity [" +
|
||||
u.updated.Format("2006-01-02 15:04:05") + "]"
|
||||
}
|
||||
|
||||
// FixedTimeReport reports the Wide sessions status periodically (10 minutes).
|
||||
func FixedTimeReport() {
|
||||
go func() {
|
||||
for _ = range time.Tick(10 * time.Minute) {
|
||||
users := map[string]*userReport{} // <username, *userReport>
|
||||
|
||||
for _, s := range WideSessions {
|
||||
if report, exists := users[s.Username]; exists {
|
||||
if s.Updated.After(report.updated) {
|
||||
users[s.Username].updated = s.Updated
|
||||
}
|
||||
|
||||
report.sessionCnt++
|
||||
} else {
|
||||
users[s.Username] = &userReport{username: s.Username, sessionCnt: 1, updated: s.Updated}
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("\n [" + strconv.Itoa(len(users)) + "] users are online and [" + strconv.Itoa(len(WideSessions)) +
|
||||
"] sessions currently\n")
|
||||
|
||||
for _, t := range users {
|
||||
buf.WriteString(" " + t.report() + "\n")
|
||||
}
|
||||
|
||||
glog.Info(buf.String())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// WSHandler handles request of creating session channel.
|
||||
//
|
||||
// When a channel closed, releases all resources associated with it.
|
||||
|
@ -104,26 +164,30 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
|
||||
httpSession.Save(r, w)
|
||||
|
||||
WideSessions.New(httpSession, sid)
|
||||
wSession = WideSessions.New(httpSession, sid)
|
||||
|
||||
glog.Infof("Created a wide session [%s] for websocket reconnecting", sid)
|
||||
glog.Infof("Created a wide session [%s] for websocket reconnecting, user [%s]", sid, wSession.Username)
|
||||
}
|
||||
|
||||
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||
wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
||||
|
||||
SessionWS[sid] = &wsChan
|
||||
|
||||
ret := map[string]interface{}{"output": "Session initialized", "cmd": "init-session"}
|
||||
wsChan.Conn.WriteJSON(&ret)
|
||||
err := wsChan.WriteJSON(&ret)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
SessionWS[sid] = &wsChan
|
||||
|
||||
glog.V(4).Infof("Open a new [Session Channel] with session [%s], %d", sid, len(SessionWS))
|
||||
|
||||
input := map[string]interface{}{}
|
||||
|
||||
for {
|
||||
if err := wsChan.Conn.ReadJSON(&input); err != nil {
|
||||
glog.V(3).Infof("[Session Channel] of session [%s] disconnected, releases all resources with it", sid)
|
||||
if err := wsChan.ReadJSON(&input); err != nil {
|
||||
glog.V(5).Infof("[Session Channel] of session [%s] disconnected, releases all resources with it, user [%s]",
|
||||
sid, wSession.Username)
|
||||
|
||||
WideSessions.Remove(sid)
|
||||
|
||||
|
@ -132,8 +196,9 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
ret = map[string]interface{}{"output": "", "cmd": "session-output"}
|
||||
|
||||
if err := wsChan.Conn.WriteJSON(&ret); err != nil {
|
||||
if err := wsChan.WriteJSON(&ret); err != nil {
|
||||
glog.Error("Session WS ERROR: " + err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -253,9 +318,9 @@ func (sessions *Sessions) Remove(sid string) {
|
|||
// kill processes
|
||||
for _, p := range s.Processes {
|
||||
if err := p.Kill(); nil != err {
|
||||
glog.Errorf("Can't kill process [%d] of session [%s]", p.Pid, sid)
|
||||
glog.Errorf("Can't kill process [%d] of session [%s], user [%s]", p.Pid, sid, s.Username)
|
||||
} else {
|
||||
glog.V(3).Infof("Killed a process [%d] of session [%s]", p.Pid, sid)
|
||||
glog.V(3).Infof("Killed a process [%d] of session [%s], user [%s]", p.Pid, sid, s.Username)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,8 +340,6 @@ func (sessions *Sessions) Remove(sid string) {
|
|||
delete(SessionWS, sid)
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Removed a session [%s]", s.Id)
|
||||
|
||||
cnt := 0 // count wide sessions associated with HTTP session
|
||||
for _, s := range *sessions {
|
||||
if s.HTTPSession.ID == s.HTTPSession.ID {
|
||||
|
@ -284,7 +347,7 @@ func (sessions *Sessions) Remove(sid string) {
|
|||
}
|
||||
}
|
||||
|
||||
glog.V(3).Infof("User [%s] has [%d] sessions", s.Username, cnt)
|
||||
glog.V(5).Infof("Removed a session [%s] of user [%s], it has [%d] sessions currently", sid, s.Username, cnt)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
152
session/users.go
152
session/users.go
|
@ -1,12 +1,29 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package session
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"text/template"
|
||||
"sync"
|
||||
|
||||
"github.com/b3log/wide/conf"
|
||||
"github.com/b3log/wide/i18n"
|
||||
|
@ -20,6 +37,85 @@ const (
|
|||
UserCreateError = "user create error"
|
||||
)
|
||||
|
||||
// Exclusive lock for adding user.
|
||||
var addUserMutex sync.Mutex
|
||||
|
||||
// PreferenceHandle handles request of preference page.
|
||||
func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := HTTPSession.Get(r, "wide-session")
|
||||
|
||||
if httpSession.IsNew {
|
||||
http.Redirect(w, r, "/preference", http.StatusFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
|
||||
httpSession.Save(r, w)
|
||||
|
||||
username := httpSession.Values["username"].(string)
|
||||
user := conf.Wide.GetUser(username)
|
||||
|
||||
if "GET" == r.Method {
|
||||
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(user.Locale), "user": user,
|
||||
"ver": conf.WideVersion, "goos": runtime.GOOS, "goarch": runtime.GOARCH, "gover": runtime.Version(),
|
||||
"locales": i18n.GetLocalesNames(), "gofmts": util.Go.GetGoFormats()}
|
||||
|
||||
t, err := template.ParseFiles("views/preference.html")
|
||||
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
http.Error(w, err.Error(), 500)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
t.Execute(w, model)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// non-GET request as save request
|
||||
|
||||
succ := true
|
||||
data := map[string]interface{}{"succ": &succ}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
||||
args := struct {
|
||||
FontFamily string
|
||||
FontSize string
|
||||
EditorFontFamily string
|
||||
EditorFontSize string
|
||||
EditorLineHeight string
|
||||
GoFmt string
|
||||
Workspace string
|
||||
Username string
|
||||
Password string
|
||||
Locale string
|
||||
}{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||
glog.Error(err)
|
||||
succ = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
user.FontFamily = args.FontFamily
|
||||
user.FontSize = args.FontSize
|
||||
user.Editor.FontFamily = args.EditorFontFamily
|
||||
user.Editor.FontSize = args.EditorFontSize
|
||||
user.Editor.LineHeight = args.EditorLineHeight
|
||||
user.GoFormat = args.GoFmt
|
||||
user.Workspace = args.Workspace
|
||||
user.Password = args.Password
|
||||
user.Locale = args.Locale
|
||||
|
||||
conf.UpdateCustomizedConf(username)
|
||||
|
||||
succ = conf.Save()
|
||||
}
|
||||
|
||||
// LoginHandler handles request of user login.
|
||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if "GET" == r.Method {
|
||||
|
@ -44,7 +140,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// non-GET request as login request
|
||||
|
||||
succ := false
|
||||
succ := true
|
||||
data := map[string]interface{}{"succ": &succ}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
||||
|
@ -55,14 +151,17 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||
glog.Error(err)
|
||||
succ = true
|
||||
succ = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
succ = false
|
||||
for _, user := range conf.Wide.Users {
|
||||
if user.Name == args.Username && user.Password == args.Password {
|
||||
succ = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,8 +241,15 @@ func SignUpUser(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// addUser add a user with the specified username and password.
|
||||
//
|
||||
// 1. create the user's workspace
|
||||
// 2. generate 'Hello, 世界' demo code in the workspace
|
||||
// 3. update the user customized configurations, such as style.css
|
||||
// 4. serve files of the user's workspace via HTTP
|
||||
func addUser(username, password string) string {
|
||||
// XXX: validate
|
||||
addUserMutex.Lock()
|
||||
defer addUserMutex.Unlock()
|
||||
|
||||
for _, user := range conf.Wide.Users {
|
||||
if user.Name == username {
|
||||
|
@ -155,19 +261,51 @@ func addUser(username, password string) string {
|
|||
dir := filepath.Dir(firstUserWorkspace)
|
||||
workspace := filepath.Join(dir, username)
|
||||
|
||||
conf.Wide.Users = append(conf.Wide.Users,
|
||||
&conf.User{Name: username, Password: password, Workspace: workspace,
|
||||
Locale: conf.Wide.Locale, GoFormat: "gofmt", FontFamily: "Helvetica", FontSize: "inherit",
|
||||
Editor: &conf.Editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit"}})
|
||||
newUser := &conf.User{Name: username, Password: password, Workspace: workspace,
|
||||
Locale: conf.Wide.Locale, GoFormat: "gofmt", FontFamily: "Helvetica", FontSize: "13px",
|
||||
Editor: &conf.Editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit"}}
|
||||
conf.Wide.Users = append(conf.Wide.Users, newUser)
|
||||
|
||||
if !conf.Save() {
|
||||
return UserCreateError
|
||||
}
|
||||
|
||||
conf.CreateWorkspaceDir(workspace)
|
||||
helloWorld(workspace)
|
||||
conf.UpdateCustomizedConf(username)
|
||||
|
||||
http.Handle("/workspace/"+username+"/",
|
||||
http.StripPrefix("/workspace/"+username+"/", http.FileServer(http.Dir(newUser.GetWorkspace()))))
|
||||
|
||||
glog.Infof("Created a user [%s]", username)
|
||||
|
||||
return UserCreated
|
||||
}
|
||||
|
||||
// helloWorld generates the 'Hello, 世界' source code in workspace/src/hello/main.go.
|
||||
func helloWorld(workspace string) {
|
||||
dir := workspace + conf.PathSeparator + "src" + conf.PathSeparator + "hello"
|
||||
if err := os.MkdirAll(dir, 0755); nil != err {
|
||||
glog.Error(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
fout, err := os.Create(dir + conf.PathSeparator + "main.go")
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
fout.WriteString(`package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, 世界")
|
||||
}
|
||||
`)
|
||||
|
||||
fout.Close()
|
||||
}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Shell.
|
||||
package shell
|
||||
|
||||
|
@ -28,7 +42,6 @@ var ShellWS = map[string]*util.WSChannel{}
|
|||
// IndexHandler handles request of Shell index.
|
||||
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
|
||||
if httpSession.IsNew {
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
|
||||
|
@ -68,6 +81,11 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// WSHandler handles request of creating Shell channel.
|
||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if httpSession.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := httpSession.Values["username"].(string)
|
||||
|
||||
sid := r.URL.Query()["sid"][0]
|
||||
|
@ -75,26 +93,22 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||
wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
||||
|
||||
ShellWS[sid] = &wsChan
|
||||
|
||||
ret := map[string]interface{}{"output": "Shell initialized", "cmd": "init-shell"}
|
||||
wsChan.Conn.WriteJSON(&ret)
|
||||
err := wsChan.WriteJSON(&ret)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
ShellWS[sid] = &wsChan
|
||||
|
||||
glog.V(4).Infof("Open a new [Shell] with session [%s], %d", sid, len(ShellWS))
|
||||
|
||||
input := map[string]interface{}{}
|
||||
|
||||
for {
|
||||
if err := wsChan.Conn.ReadJSON(&input); err != nil {
|
||||
if err.Error() == "EOF" {
|
||||
return
|
||||
}
|
||||
|
||||
if err.Error() == "unexpected EOF" {
|
||||
return
|
||||
}
|
||||
|
||||
if err := wsChan.ReadJSON(&input); err != nil {
|
||||
glog.Error("Shell WS ERROR: " + err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -121,7 +135,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
ret = map[string]interface{}{"output": output, "cmd": "shell-output"}
|
||||
|
||||
if err := wsChan.Conn.WriteJSON(&ret); err != nil {
|
||||
if err := wsChan.WriteJSON(&ret); err != nil {
|
||||
glog.Error("Shell WS ERROR: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#dialogAboutDialog .dialog-main {
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* start reset & function */
|
||||
body {
|
||||
font-size: 13px;
|
||||
|
@ -44,3 +60,32 @@ button {
|
|||
display: none;
|
||||
}
|
||||
/* end reset & function */
|
||||
|
||||
/* start common */
|
||||
.ft-small {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.list li {
|
||||
cursor: pointer;
|
||||
line-height: 20px;
|
||||
padding: 0 3px;
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.list li.selected,
|
||||
.list li:hover{
|
||||
background-color: #3875d7;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.list li.selected .ft-small,
|
||||
.list li:hover .ft-small {
|
||||
color: #FFF;
|
||||
}
|
||||
/* end common */
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* dialig style
|
||||
*
|
||||
|
@ -81,3 +97,27 @@
|
|||
width: 100%;
|
||||
margin: 2px auto;
|
||||
}
|
||||
|
||||
#dialogGoFilePrompt > ul {
|
||||
position: relative;
|
||||
height: 260px;
|
||||
overflow: auto;
|
||||
margin-top: 5px;
|
||||
background-color: #FFF;
|
||||
border: 1px solid #9B9B9B;
|
||||
}
|
||||
|
||||
#dialogPreference {
|
||||
margin: 10px;
|
||||
border: 1px solid #A4A4A4;
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
#dialogPreference .select {
|
||||
width: 100%;
|
||||
margin: 2px auto;
|
||||
}
|
||||
|
||||
#dialogPreference .tabs-panel {
|
||||
padding: 10px;
|
||||
}
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#shellOutput, #shellInput {
|
||||
width: 100%;
|
||||
}
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* start side */
|
||||
.side {
|
||||
background-color: #FFF;
|
||||
|
@ -29,6 +45,7 @@
|
|||
border-width: 0;
|
||||
color: #fff;
|
||||
height: 18px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ztree li a:hover {
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.wrapper {
|
||||
margin: 0 auto;
|
||||
width: 980px;
|
||||
|
@ -71,6 +87,33 @@
|
|||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075) inset, 0 0 12px rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.25);
|
||||
background-color: #60b044;
|
||||
background-image: linear-gradient(#8add6d, #60b044);
|
||||
border: 1px solid #5ca941;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #569e3d;
|
||||
background-image: linear-gradient(#79d858, #569e3d);
|
||||
border-color: #4a993e;
|
||||
}
|
||||
|
||||
.header .button {
|
||||
color: #fff;
|
||||
padding: 0 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.header .button:hover {
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
#msg {
|
||||
background-color: #fcdede;
|
||||
border: 1px solid #d2b2b2;
|
||||
|
@ -112,6 +155,10 @@
|
|||
}
|
||||
|
||||
.form.sign-up {
|
||||
margin-top: -20px;
|
||||
margin-top: -71px;
|
||||
}
|
||||
|
||||
#signUpBtn {
|
||||
margin-top: 20px;
|
||||
}
|
||||
/* end sign up */
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#startPage {
|
||||
padding: 50px 70px;
|
||||
line-height: 28px;
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* start icon */
|
||||
@font-face {
|
||||
font-family: 'icomoon';
|
||||
|
@ -193,7 +209,8 @@
|
|||
top: 1px;
|
||||
}
|
||||
|
||||
.edit-panel .tabs .ico {
|
||||
.edit-panel .tabs .ico,
|
||||
#dialogGoFilePrompt .ico {
|
||||
background-image: url("../images/ico-file.png");
|
||||
float: left;
|
||||
height: 16px;
|
||||
|
@ -243,6 +260,10 @@
|
|||
background: #08f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.CodeMirror div.CodeMirror-cursor {
|
||||
border-left: 2px solid #333;
|
||||
}
|
||||
/* end editor */
|
||||
|
||||
/* start bottom-window-group */
|
||||
|
@ -315,31 +336,6 @@
|
|||
width: 50px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.bottom-window-group .search li {
|
||||
cursor: pointer;
|
||||
line-height: 20px;
|
||||
padding: 0 3px;
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.bottom-window-group .search li.selected {
|
||||
background-color: #3875d7;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.bottom-window-group .search .path {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.bottom-window-group .search li.selected .path {
|
||||
color: #FFF;
|
||||
}
|
||||
/* end bottom-window-group */
|
||||
|
||||
/* start footer */
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var bottomGroup = {
|
||||
tabs: undefined,
|
||||
searchTab: undefined,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2011, Liyuan Li
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
$.fn.extend({
|
||||
dialog: {
|
||||
|
@ -143,6 +144,9 @@
|
|||
} else {
|
||||
// 20(footer) + 23(header)
|
||||
top = parseInt((windowH - dialogH - 43) / 2);
|
||||
if (top < 0) {
|
||||
top = 0;
|
||||
}
|
||||
left = parseInt((windowW - dialogW) / 2);
|
||||
}
|
||||
$dialog.css({
|
||||
|
@ -175,6 +179,10 @@
|
|||
}
|
||||
});
|
||||
|
||||
$(window).resize(function () {
|
||||
$(".dialog-background").height($("body").height());
|
||||
});
|
||||
|
||||
if (typeof settings.afterInit === "function") {
|
||||
settings.afterInit();
|
||||
}
|
||||
|
@ -210,12 +218,12 @@
|
|||
if (positionX > $(window).width() - $(dialog).width()) {
|
||||
positionX = $(window).width() - $(dialog).width();
|
||||
}
|
||||
if (positionY < 0) {
|
||||
positionY = 0;
|
||||
}
|
||||
if (positionY > $(window).height() - $(dialog).height()) {
|
||||
positionY = $(window).height() - $(dialog).height();
|
||||
}
|
||||
if (positionY < 0) {
|
||||
positionY = 0;
|
||||
}
|
||||
dialog.style.left = positionX + "px";
|
||||
dialog.style.top = positionY + "px";
|
||||
};
|
||||
|
@ -231,7 +239,7 @@
|
|||
_document.ondragstart = null;
|
||||
_document.onselectstart = null;
|
||||
_document.onselect = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
_close: function (id, settings) {
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var editors = {
|
||||
data: [],
|
||||
tabs: {},
|
||||
|
@ -94,6 +110,7 @@ var editors = {
|
|||
id: ".edit-panel",
|
||||
clickAfter: function (id) {
|
||||
if (id === 'startPage') {
|
||||
$(".footer .cursor").text('');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -109,7 +126,11 @@ var editors = {
|
|||
}
|
||||
}
|
||||
|
||||
var cursor = wide.curEditor.getCursor();
|
||||
wide.curEditor.setCursor(cursor);
|
||||
wide.curEditor.focus();
|
||||
|
||||
$(".footer .cursor").text('| ' + (cursor.line + 1) + ':' + (cursor.ch + 1) + ' |');
|
||||
},
|
||||
removeBefore: function (id) {
|
||||
if (id === 'startPage') { // 当前关闭的 tab 是起始页
|
||||
|
@ -117,7 +138,6 @@ var editors = {
|
|||
return true;
|
||||
}
|
||||
|
||||
// 移除编辑器
|
||||
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
||||
if (editors.data[i].id === id) {
|
||||
if (editors.data[i].editor.doc.isClean()) {
|
||||
|
@ -163,6 +183,7 @@ var editors = {
|
|||
tree.fileTree.cancelSelectedNode();
|
||||
wide.curNode = undefined;
|
||||
wide.curEditor = undefined;
|
||||
$(".footer .cursor").text('');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -182,6 +203,9 @@ var editors = {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var cursor = wide.curEditor.getCursor();
|
||||
$(".footer .cursor").text('| ' + (cursor.line + 1) + ':' + (cursor.ch + 1) + ' |');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -347,6 +371,11 @@ var editors = {
|
|||
});
|
||||
|
||||
CodeMirror.commands.autocompleteAfterDot = function (cm) {
|
||||
var token = cm.getTokenAt(cm.getCursor());
|
||||
if ("comment" === token.type) {
|
||||
return CodeMirror.Pass;
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
if (!cm.state.completionActive) {
|
||||
cm.showHint({hint: CodeMirror.hint.go, completeSingle: false});
|
||||
|
@ -413,33 +442,11 @@ var editors = {
|
|||
return;
|
||||
}
|
||||
|
||||
var cursorLine = data.cursorLine;
|
||||
var cursorCh = data.cursorCh;
|
||||
var tId = tree.getTIdByPath(data.path);
|
||||
wide.curNode = tree.fileTree.getNodeByTId(tId);
|
||||
tree.fileTree.selectNode(wide.curNode);
|
||||
|
||||
var request = newWideRequest();
|
||||
request.path = data.path;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/file',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
$("#dialogAlert").dialog("open", data.msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var tId = tree.getTIdByPath(data.path);
|
||||
wide.curNode = tree.fileTree.getNodeByTId(tId);
|
||||
tree.fileTree.selectNode(wide.curNode);
|
||||
|
||||
data.cursorLine = cursorLine;
|
||||
data.cursorCh = cursorCh;
|
||||
editors.newEditor(data);
|
||||
}
|
||||
});
|
||||
tree.openFile(wide.curNode, CodeMirror.Pos(data.cursorLine - 1, data.cursorCh - 1));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -469,7 +476,7 @@ var editors = {
|
|||
};
|
||||
},
|
||||
appendSearch: function (data, type, key) {
|
||||
var searcHTML = '<ul>';
|
||||
var searcHTML = '<ul class="list">';
|
||||
|
||||
for (var i = 0, ii = data.length; i < ii; i++) {
|
||||
var contents = data[i].contents[0],
|
||||
|
@ -479,7 +486,7 @@ var editors = {
|
|||
+ contents.substring(index + key.length);
|
||||
|
||||
searcHTML += '<li title="' + data[i].path + '">'
|
||||
+ contents + " <span class='path'>" + data[i].path
|
||||
+ contents + " <span class='ft-small'>" + data[i].path
|
||||
+ '<i class="position" data-line="'
|
||||
+ data[i].line + '" data-ch="' + data[i].ch + '"> (' + data[i].line + ':'
|
||||
+ data[i].ch + ')</i></span></li>';
|
||||
|
@ -544,33 +551,10 @@ var editors = {
|
|||
$(".bottom-window-group .search").focus();
|
||||
},
|
||||
// 新建一个编辑器 Tab,如果已经存在 Tab 则切换到该 Tab.
|
||||
newEditor: function (data) {
|
||||
newEditor: function (data, cursor) {
|
||||
$(".toolbars").show();
|
||||
var id = wide.curNode.tId;
|
||||
|
||||
var cursor = CodeMirror.Pos(0, 0);
|
||||
if (data.cursorLine && data.cursorCh) {
|
||||
cursor = CodeMirror.Pos(data.cursorLine - 1, data.cursorCh - 1);
|
||||
}
|
||||
|
||||
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
||||
if (editors.data[i].id === id) {
|
||||
editors.tabs.setCurrent(id);
|
||||
wide.curEditor = editors.data[i].editor;
|
||||
var editor = wide.curEditor;
|
||||
|
||||
editor.setCursor(cursor);
|
||||
|
||||
var half = Math.floor(editor.getScrollInfo().clientHeight / editor.defaultTextHeight() / 2);
|
||||
var cursorCoords = editor.cursorCoords({line: cursor.line - half, ch: 0}, "local");
|
||||
editor.scrollTo(0, cursorCoords.top);
|
||||
|
||||
editor.focus();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
editors.tabs.add({
|
||||
id: id,
|
||||
title: '<span title="' + wide.curNode.path + '"><span class="'
|
||||
|
@ -580,9 +564,6 @@ var editors = {
|
|||
|
||||
menu.undisabled(['save-all', 'close-all', 'build', 'run', 'go-test', 'go-get', 'go-install']);
|
||||
|
||||
var rulers = [];
|
||||
rulers.push({color: "#ccc", column: 120, lineStyle: "dashed"});
|
||||
|
||||
var textArea = document.getElementById("editor" + id);
|
||||
textArea.value = data.content;
|
||||
|
||||
|
@ -592,11 +573,12 @@ var editors = {
|
|||
autoCloseBrackets: true,
|
||||
matchBrackets: true,
|
||||
highlightSelectionMatches: {showToken: /\w/},
|
||||
rulers: rulers,
|
||||
rulers: [{color: "#ccc", column: 120, lineStyle: "dashed"}],
|
||||
styleActiveLine: true,
|
||||
theme: 'wide',
|
||||
indentUnit: 4,
|
||||
foldGutter: true,
|
||||
cursorHeight: 1,
|
||||
path: data.path,
|
||||
extraKeys: {
|
||||
"Ctrl-\\": "autocompleteAnyWord",
|
||||
|
@ -610,7 +592,7 @@ var editors = {
|
|||
wide.saveFile();
|
||||
},
|
||||
"Shift-Ctrl-S": function () {
|
||||
wide.saveAllFiles();
|
||||
menu.saveAllFiles();
|
||||
},
|
||||
"Shift-Alt-F": function () {
|
||||
var currentPath = editors.getCurrentPath();
|
||||
|
@ -628,26 +610,121 @@ var editors = {
|
|||
}
|
||||
},
|
||||
"Shift-Ctrl-Up": function (cm) {
|
||||
var cursor = cm.getCursor();
|
||||
var line = cursor.line;
|
||||
var content = cm.getLine(line);
|
||||
var content = '',
|
||||
selectoion = cm.listSelections()[0];
|
||||
|
||||
if (0 === line) {
|
||||
cm.replaceRange("", CodeMirror.Pos(0));
|
||||
line++;
|
||||
var from = selectoion.anchor,
|
||||
to = selectoion.head;
|
||||
if (from.line > to.line) {
|
||||
from = selectoion.head;
|
||||
to = selectoion.anchor;
|
||||
}
|
||||
|
||||
cm.replaceRange("\n" + content, CodeMirror.Pos(line - 1));
|
||||
cm.setCursor(cursor);
|
||||
for (var i = from.line, max = to.line; i <= max; i++) {
|
||||
if (to.ch !== 0 || i !== max) { // 下一行选中为0时,不应添加内容
|
||||
content += '\n' + cm.getLine(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 下一行选中为0时,应添加到上一行末
|
||||
var replaceToLine = to.line;
|
||||
if (to.ch === 0) {
|
||||
replaceToLine = to.line - 1;
|
||||
}
|
||||
cm.replaceRange(content, CodeMirror.Pos(replaceToLine));
|
||||
|
||||
cm.setSelection(CodeMirror.Pos(from.line, from.ch),
|
||||
CodeMirror.Pos(to.line, to.ch));
|
||||
},
|
||||
"Shift-Ctrl-Down": function (cm) {
|
||||
var cursor = cm.getCursor();
|
||||
var line = cursor.line;
|
||||
var content = cm.getLine(line);
|
||||
var content = '',
|
||||
selectoion = cm.listSelections()[0];
|
||||
|
||||
cm.replaceRange("\n", CodeMirror.Pos(line));
|
||||
cm.replaceRange(content, CodeMirror.Pos(line + 1));
|
||||
cm.setCursor(CodeMirror.Pos(line + 1, cursor.ch));
|
||||
var from = selectoion.anchor,
|
||||
to = selectoion.head;
|
||||
if (from.line > to.line) {
|
||||
from = selectoion.head;
|
||||
to = selectoion.anchor;
|
||||
}
|
||||
|
||||
for (var i = from.line, max = to.line; i <= max; i++) {
|
||||
if (to.ch !== 0 || i !== max) { // 下一行选中为0时,不应添加内容
|
||||
content += '\n' + cm.getLine(i);
|
||||
}
|
||||
}
|
||||
// 下一行选中为0时,应添加到上一行末
|
||||
var replaceToLine = to.line;
|
||||
if (to.ch === 0) {
|
||||
replaceToLine = to.line - 1;
|
||||
}
|
||||
cm.replaceRange(content, CodeMirror.Pos(replaceToLine));
|
||||
|
||||
var offset = replaceToLine - from.line + 1;
|
||||
cm.setSelection(CodeMirror.Pos(from.line + offset, from.ch),
|
||||
CodeMirror.Pos(to.line + offset, to.ch));
|
||||
},
|
||||
"Shift-Alt-Up": function (cm) {
|
||||
var selectoion = cm.listSelections()[0];
|
||||
|
||||
var from = selectoion.anchor,
|
||||
to = selectoion.head;
|
||||
if (from.line > to.line) {
|
||||
from = selectoion.head;
|
||||
to = selectoion.anchor;
|
||||
}
|
||||
|
||||
if (from.line === 0) {
|
||||
return false;
|
||||
}
|
||||
// 下一行选中为0时,应添加到上一行末
|
||||
var replaceToLine = to.line;
|
||||
if (to.ch === 0) {
|
||||
replaceToLine = to.line - 1;
|
||||
}
|
||||
cm.replaceRange('\n' + cm.getLine(from.line - 1), CodeMirror.Pos(replaceToLine));
|
||||
if (from.line === 1) {
|
||||
// 移除第一行的换行
|
||||
cm.replaceRange('', CodeMirror.Pos(0, 0),
|
||||
CodeMirror.Pos(1, 0));
|
||||
} else {
|
||||
cm.replaceRange('', CodeMirror.Pos(from.line - 2, cm.getLine(from.line - 2).length),
|
||||
CodeMirror.Pos(from.line - 1, cm.getLine(from.line - 1).length));
|
||||
}
|
||||
|
||||
cm.setSelection(CodeMirror.Pos(from.line - 1, from.ch),
|
||||
CodeMirror.Pos(to.line - 1, to.ch));
|
||||
},
|
||||
"Shift-Alt-Down": function (cm) {
|
||||
var selectoion = cm.listSelections()[0];
|
||||
|
||||
var from = selectoion.anchor,
|
||||
to = selectoion.head;
|
||||
if (from.line > to.line) {
|
||||
from = selectoion.head;
|
||||
to = selectoion.anchor;
|
||||
}
|
||||
|
||||
if (to.line === cm.lastLine()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 下一行选中为0时,应添加到上一行末
|
||||
var replaceToLine = to.line;
|
||||
if (to.ch === 0) {
|
||||
replaceToLine = to.line - 1;
|
||||
}
|
||||
// 把选中的下一行添加到选中区域的上一行
|
||||
if (from.line === 0) {
|
||||
cm.replaceRange(cm.getLine(replaceToLine + 1) + '\n', CodeMirror.Pos(0, 0));
|
||||
} else {
|
||||
cm.replaceRange('\n' + cm.getLine(replaceToLine + 1), CodeMirror.Pos(from.line - 1));
|
||||
}
|
||||
// 删除选中的下一行
|
||||
cm.replaceRange('', CodeMirror.Pos(replaceToLine + 1, cm.getLine(replaceToLine + 1).length),
|
||||
CodeMirror.Pos(replaceToLine + 2, cm.getLine(replaceToLine + 2).length));
|
||||
|
||||
cm.setSelection(CodeMirror.Pos(from.line + 1, from.ch),
|
||||
CodeMirror.Pos(to.line + 1, to.ch));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -657,7 +734,6 @@ var editors = {
|
|||
var cursor = cm.getCursor();
|
||||
|
||||
$(".footer .cursor").text('| ' + (cursor.line + 1) + ':' + (cursor.ch + 1) + ' |');
|
||||
// TODO: 关闭 tab 的时候要重置
|
||||
});
|
||||
|
||||
editor.on('focus', function (cm) {
|
||||
|
@ -700,16 +776,19 @@ var editors = {
|
|||
editor.setOption("autoCloseTags", true);
|
||||
}
|
||||
|
||||
editor.setCursor(cursor);
|
||||
|
||||
var half = Math.floor(editor.getScrollInfo().clientHeight / editor.defaultTextHeight() / 2);
|
||||
var cursorCoords = editor.cursorCoords({line: cursor.line - half, ch: 0}, "local");
|
||||
editor.scrollTo(0, cursorCoords.top);
|
||||
|
||||
wide.curEditor = editor;
|
||||
editors.data.push({
|
||||
"editor": editor,
|
||||
"id": id
|
||||
});
|
||||
|
||||
$(".footer .cursor").text('| ' + (cursor.line + 1) + ':' + (cursor.ch + 1) + ' |');
|
||||
|
||||
var half = Math.floor(wide.curEditor.getScrollInfo().clientHeight / wide.curEditor.defaultTextHeight() / 2);
|
||||
var cursorCoords = wide.curEditor.cursorCoords({line: cursor.line - half, ch: 0}, "local");
|
||||
wide.curEditor.scrollTo(0, cursorCoords.top);
|
||||
|
||||
editor.setCursor(cursor);
|
||||
editor.focus();
|
||||
}
|
||||
};
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var hotkeys = {
|
||||
defaultKeyMap: {
|
||||
// Ctrl+0 焦点切换到当前编辑器
|
||||
|
@ -15,7 +31,7 @@ var hotkeys = {
|
|||
which: 49
|
||||
},
|
||||
// Ctrl+4 焦点切换到输出窗口
|
||||
goOutPut: {
|
||||
goOutput: {
|
||||
ctrlKey: true,
|
||||
altKey: false,
|
||||
shiftKey: false,
|
||||
|
@ -63,7 +79,21 @@ var hotkeys = {
|
|||
shiftKey: false,
|
||||
which: 81
|
||||
},
|
||||
// F6 构建并运行
|
||||
// Shift+Alt+O 跳转到文件
|
||||
goFile: {
|
||||
ctrlKey: false,
|
||||
altKey: true,
|
||||
shiftKey: true,
|
||||
which: 79
|
||||
},
|
||||
// F5 Build
|
||||
build: {
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
shiftKey: false,
|
||||
which: 116
|
||||
},
|
||||
// F6 Build & Run
|
||||
buildRun: {
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
|
@ -71,6 +101,60 @@ var hotkeys = {
|
|||
which: 117
|
||||
}
|
||||
},
|
||||
bindList: function ($source, $list, enterFun) {
|
||||
$list.data("index", 0);
|
||||
$source.keydown(function (event) {
|
||||
var index = $list.data("index"),
|
||||
count = $list.find("li").length;
|
||||
|
||||
if (count === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.which === 38) { // up
|
||||
index--;
|
||||
if (index < 0) {
|
||||
index = count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.which === 40) { // down
|
||||
index++;
|
||||
if (index > count - 1) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var $selected = $list.find("li:eq(" + index + ")");
|
||||
|
||||
if (event.which === 13) { // enter
|
||||
enterFun($selected);
|
||||
}
|
||||
|
||||
$list.find("li").removeClass("selected");
|
||||
$list.data("index", index);
|
||||
$selected.addClass("selected");
|
||||
|
||||
if (index === 0) {
|
||||
$list.scrollTop(0);
|
||||
} else {
|
||||
if ($selected[0].offsetTop + $list.scrollTop() > $list.height()) {
|
||||
if (event.which === 40) {
|
||||
$list.scrollTop($list.scrollTop() + $selected.height());
|
||||
} else {
|
||||
$list.scrollTop($selected[0].offsetTop);
|
||||
}
|
||||
} else {
|
||||
$list.scrollTop(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 阻止上下键改变光标位置
|
||||
if (event.which === 38 || event.which === 40 || event.which === 13) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
_bindOutput: function () {
|
||||
$(".bottom-window-group .output").keydown(function (event) {
|
||||
event.preventDefault();
|
||||
|
@ -241,8 +325,8 @@ var hotkeys = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (event.ctrlKey === hotKeys.goOutPut.ctrlKey
|
||||
&& event.which === hotKeys.goOutPut.which) { // Ctrl+4 焦点切换到输出窗口
|
||||
if (event.ctrlKey === hotKeys.goOutput.ctrlKey
|
||||
&& event.which === hotKeys.goOutput.which) { // Ctrl+4 焦点切换到输出窗口
|
||||
bottomGroup.tabs.setCurrent("output");
|
||||
|
||||
windows.flowBottom();
|
||||
|
@ -339,12 +423,26 @@ var hotkeys = {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (event.which === hotKeys.buildRun.which) { // F6 构建并运行
|
||||
wide.run();
|
||||
if (event.which === hotKeys.build.which) { // F5 Build
|
||||
menu.build();
|
||||
event.preventDefault();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.which === hotKeys.buildRun.which) { // F6 Build & Run
|
||||
menu.run();
|
||||
event.preventDefault();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.ctrlKey === hotKeys.goFile.ctrlKey
|
||||
&& event.altKey === hotKeys.goFile.altKey
|
||||
&& event.shiftKey === hotKeys.goFile.shiftKey
|
||||
&& event.which === hotKeys.goFile.which) { // Shift+Alt+O 跳转到文件
|
||||
$("#dialogGoFilePrompt").dialog("open");
|
||||
}
|
||||
});
|
||||
},
|
||||
init: function () {
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var menu = {
|
||||
init: function () {
|
||||
this.subMenu();
|
||||
this._initPreference();
|
||||
this._initAbout();
|
||||
|
||||
// 点击子菜单后消失
|
||||
$(".frame li").click(function () {
|
||||
|
@ -8,6 +26,34 @@ var menu = {
|
|||
$(".menu > ul > li > a, .menu > ul> li > span").removeClass("selected");
|
||||
});
|
||||
},
|
||||
_initAbout: function () {
|
||||
$("#dialogAbout").load('/about', function () {
|
||||
$("#dialogAbout").dialog({
|
||||
"modal": true,
|
||||
"height": 460,
|
||||
"width": 800,
|
||||
"title": config.label.about,
|
||||
"hideFooter": true,
|
||||
"afterOpen": function () {
|
||||
$.ajax({
|
||||
url: "http://rhythm.b3log.org/version/wide/latest",
|
||||
type: "GET",
|
||||
dataType: "jsonp",
|
||||
jsonp: "callback",
|
||||
success: function (data, textStatus) {
|
||||
if ($("#dialogAbout .version").text() === data.wideVersion) {
|
||||
$(".upgrade").text(config.label.uptodate);
|
||||
} else {
|
||||
$(".upgrade").html(config.label.new_version_available + config.label.colon
|
||||
+ "<a href='" + data.wideDownload
|
||||
+ "' target='_blank'>" + data.wideVersion + "</a>");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
disabled: function (list) {
|
||||
for (var i = 0, max = list.length; i < max; i++) {
|
||||
$(".menu li." + list[i]).addClass("disabled");
|
||||
|
@ -35,5 +81,321 @@ var menu = {
|
|||
$(this).addClass("selected");
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
openPreference: function () {
|
||||
$("#dialogPreference").dialog("open");
|
||||
},
|
||||
saveAllFiles: function () {
|
||||
if ($(".menu li.save-all").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
||||
var path = tree.fileTree.getNodeByTId(editors.data[i].id).path;
|
||||
var editor = editors.data[i].editor;
|
||||
|
||||
if ("text/x-go" === editor.getOption("mode")) {
|
||||
wide.fmt(path, editor);
|
||||
} else {
|
||||
wide._save(path, editor);
|
||||
}
|
||||
}
|
||||
},
|
||||
closeAllFiles: function () {
|
||||
if ($(".menu li.close-all").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置全部关闭标识
|
||||
var removeData = [];
|
||||
$(".edit-panel .tabs > div").each(function (i) {
|
||||
if (i !== 0) {
|
||||
removeData.push($(this).data("index"));
|
||||
}
|
||||
});
|
||||
$("#dialogCloseEditor").data("removeData", removeData);
|
||||
// 开始关闭
|
||||
$(".edit-panel .tabs .ico-close:eq(0)").click();
|
||||
},
|
||||
exit: function () {
|
||||
var request = newWideRequest();
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/logout',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (data.succ) {
|
||||
window.location.href = "/login";
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
openAbout: function () {
|
||||
$("#dialogAbout").dialog("open");
|
||||
},
|
||||
goget: function () {
|
||||
menu.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.go-get").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/go/get',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
}
|
||||
});
|
||||
},
|
||||
goinstall: function () {
|
||||
menu.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.go-install").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/go/install',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
}
|
||||
});
|
||||
},
|
||||
// 测试.
|
||||
test: function () {
|
||||
menu.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.test").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/go/test',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
}
|
||||
});
|
||||
},
|
||||
// Build & Run.
|
||||
run: function () {
|
||||
menu.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.run").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".toolbars .ico-stop").length === 1) {
|
||||
wide.stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
request.code = wide.curEditor.getValue();
|
||||
request.nextCmd = "run";
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/build',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
$(".toolbars .ico-buildrun").addClass("ico-stop")
|
||||
.removeClass("ico-buildrun").attr("title", config.label.stop);
|
||||
}
|
||||
});
|
||||
},
|
||||
// Build.
|
||||
build: function () {
|
||||
menu.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.build").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
request.code = wide.curEditor.getValue();
|
||||
request.nextCmd = ""; // 只构建,无下一步操作
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/build',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
}
|
||||
});
|
||||
},
|
||||
_initPreference: function () {
|
||||
$("#dialogPreference").load('/preference', function () {
|
||||
$("#localeSelect").on('change', function () {
|
||||
var $dialogPreference = $("#dialogPreference"),
|
||||
$input = $dialogPreference.find("input[name=locale]")
|
||||
|
||||
$input.val(this.value);
|
||||
});
|
||||
|
||||
$("#goFmtSelect").on('change', function () {
|
||||
var $dialogPreference = $("#dialogPreference"),
|
||||
$input = $dialogPreference.find("input[name=goFmt]")
|
||||
|
||||
$input.val(this.value);
|
||||
});
|
||||
|
||||
$("#dialogPreference input").keyup(function () {
|
||||
var isChange = false;
|
||||
$("#dialogPreference input").each(function () {
|
||||
if ($(this).val() !== $(this).data("value")) {
|
||||
isChange = true;
|
||||
}
|
||||
});
|
||||
|
||||
var $okBtn = $("#dialogPreference").closest(".dialog-main").find(".dialog-footer > button:eq(0)");
|
||||
if (isChange) {
|
||||
$okBtn.prop("disabled", false);
|
||||
} else {
|
||||
$okBtn.prop("disabled", true);
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogPreference select").on("change", function () {
|
||||
var isChange = false;
|
||||
$("#dialogPreference input").each(function () {
|
||||
if ($(this).val() !== $(this).data("value")) {
|
||||
isChange = true;
|
||||
}
|
||||
});
|
||||
|
||||
var $okBtn = $("#dialogPreference").closest(".dialog-main").find(".dialog-footer > button:eq(0)");
|
||||
if (isChange) {
|
||||
$okBtn.prop("disabled", false);
|
||||
} else {
|
||||
$okBtn.prop("disabled", true);
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogPreference").dialog({
|
||||
"modal": true,
|
||||
"height": 460,
|
||||
"width": 800,
|
||||
"title": config.label.perference,
|
||||
"okText": config.label.apply,
|
||||
"cancelText": config.label.cancel,
|
||||
"afterOpen": function () {
|
||||
var $okBtn = $("#dialogPreference").closest(".dialog-main").find(".dialog-footer > button:eq(0)");
|
||||
$okBtn.prop("disabled", true);
|
||||
},
|
||||
"ok": function () {
|
||||
var request = newWideRequest(),
|
||||
$dialogPreference = $("#dialogPreference"),
|
||||
$fontFamily = $dialogPreference.find("input[name=fontFamily]"),
|
||||
$fontSize = $dialogPreference.find("input[name=fontSize]"),
|
||||
$editorFontFamily = $dialogPreference.find("input[name=editorFontFamily]"),
|
||||
$editorFontSize = $dialogPreference.find("input[name=editorFontSize]"),
|
||||
$editorLineHeight = $dialogPreference.find("input[name=editorLineHeight]"),
|
||||
$goFmt = $dialogPreference.find("input[name=goFmt]"),
|
||||
$workspace = $dialogPreference.find("input[name=workspace]"),
|
||||
$password = $dialogPreference.find("input[name=password]"),
|
||||
$locale = $dialogPreference.find("input[name=locale]");
|
||||
|
||||
$.extend(request, {
|
||||
"fontFamily": $fontFamily.val(),
|
||||
"fontSize": $fontSize.val(),
|
||||
"editorFontFamily": $editorFontFamily.val(),
|
||||
"editorFontSize": $editorFontSize.val(),
|
||||
"editorLineHeight": $editorLineHeight.val(),
|
||||
"goFmt": $goFmt.val(),
|
||||
"workspace": $workspace.val(),
|
||||
"password": $password.val(),
|
||||
"locale": $locale.val()
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/preference',
|
||||
data: JSON.stringify(request),
|
||||
success: function (data, textStatus, jqXHR) {
|
||||
if (!data.succ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fontFamily.data("value", $fontFamily.val());
|
||||
$fontSize.data("value", $fontSize.val());
|
||||
$editorFontFamily.data("value", $editorFontFamily.val());
|
||||
$editorFontSize.data("value", $editorFontSize.val());
|
||||
$editorLineHeight.data("value", $editorLineHeight.val());
|
||||
$goFmt.data("value", $goFmt.val());
|
||||
$workspace.data("value", $workspace.val());
|
||||
$password.data("value", $password.val());
|
||||
$locale.data("value", $locale.val());
|
||||
|
||||
var $okBtn = $("#dialogPreference").closest(".dialog-main").find(".dialog-footer > button:eq(0)");
|
||||
$okBtn.prop("disabled", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
new Tabs({
|
||||
id: ".preference"
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var notification = {
|
||||
init: function () {
|
||||
$(".notification-count").click(function () {
|
||||
|
@ -20,6 +36,12 @@ var notification = {
|
|||
$notification = $('.bottom-window-group .notification > table'),
|
||||
notificationHTML = '';
|
||||
|
||||
if (data.cmd && "init-notification" === data.cmd) {
|
||||
console.log('[notification onmessage]' + e.data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
notificationHTML += '<tr><td class="severity">' + data.severity
|
||||
+ '</td><td class="message">' + data.message
|
||||
+ '</td><td class="type">' + data.type + '</td></tr>';
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
|
|
|
@ -1,6 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.cm-s-wide span.cm-meta {color: rgb(98,143,181);}
|
||||
.cm-s-wide span.cm-keyword { color: rgb(0,0,230); }
|
||||
.cm-s-wide span.cm-atom {color: #219;}
|
||||
.cm-s-wide span.cm-atom {color: rgb(153,51,204);}
|
||||
.cm-s-wide span.cm-number {color: #B35E4D;}
|
||||
.cm-s-wide span.cm-def {color: #00f;}
|
||||
.cm-s-wide span.cm-variable {color: black;}
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var session = {
|
||||
init: function () {
|
||||
this._initWS();
|
||||
|
@ -47,7 +63,20 @@ var session = {
|
|||
// expand tree
|
||||
for (var j = 0, jj = fileTree.length; j < jj; j++) {
|
||||
if (nodes[i].path === fileTree[j]) {
|
||||
tree.fileTree.expandNode(nodes[i], true, false, true);
|
||||
// 当父节点都展开时,才展开该节点
|
||||
var parents = tree.getAllParents(tree.fileTree.getNodeByTId(nodes[i].tId)),
|
||||
isOpen = true;
|
||||
for (var l = 0, max = parents.length; l < max; l++) {
|
||||
if (parents[l].open === false) {
|
||||
isOpen = false;
|
||||
}
|
||||
}
|
||||
if (isOpen) {
|
||||
tree.fileTree.expandNode(nodes[i], true, false, true);
|
||||
} else {
|
||||
// 设置状态
|
||||
nodes[i].open = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var shell = {
|
||||
_shellWS: undefined,
|
||||
_initWS: function () {
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var Tabs = function (obj) {
|
||||
obj._$tabsPanel = $(obj.id + " > .tabs-panel");
|
||||
obj._$tabs = $(obj.id + " > .tabs");
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var tree = {
|
||||
fileTree: undefined,
|
||||
// 递归获取当前节点展开中的最后一个节点
|
||||
|
@ -61,7 +77,19 @@ var tree = {
|
|||
|
||||
return paths;
|
||||
},
|
||||
_isParents: function (tId, parentTId) {
|
||||
getAllParents: function (node, parents) {
|
||||
if (!parents) {
|
||||
parents = [];
|
||||
}
|
||||
|
||||
if (!node || !node.parentTId) {
|
||||
return parents;
|
||||
} else {
|
||||
parents.push(node.getParentNode());
|
||||
return tree.getAllParents(node.getParentNode(), parents);
|
||||
}
|
||||
},
|
||||
isParents: function (tId, parentTId) {
|
||||
var node = tree.fileTree.getNodeByTId(tId);
|
||||
if (!node || !node.parentTId) {
|
||||
return false;
|
||||
|
@ -69,7 +97,7 @@ var tree = {
|
|||
if (node.parentTId === parentTId) {
|
||||
return true;
|
||||
} else {
|
||||
return tree._isParents(node.parentTId, parentTId);
|
||||
return tree.isParents(node.parentTId, parentTId);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -111,6 +139,58 @@ var tree = {
|
|||
$("#fileRMenu").hide();
|
||||
$("#dialogRemoveConfirm").dialog("open");
|
||||
},
|
||||
rename: function (it) {
|
||||
if (it) {
|
||||
if ($(it).hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$("#dirRMenu").hide();
|
||||
$("#fileRMenu").hide();
|
||||
$("#dialogRenamePrompt").dialog("open");
|
||||
},
|
||||
export: function (it) {
|
||||
if (it) {
|
||||
if ($(it).hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.path = wide.curNode.path;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/file/zip/new',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
$("#dialogAlert").dialog("open", data.msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
window.open('/file/zip?path=' + wide.curNode.path + '.zip');
|
||||
}
|
||||
});
|
||||
|
||||
$("#dirRMenu").hide();
|
||||
$("#fileRMenu").hide();
|
||||
},
|
||||
refresh: function (it) {
|
||||
if (it) {
|
||||
if ($(it).hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tree.fileTree.reAsyncChildNodes(wide.curNode, "refresh");
|
||||
|
||||
$("#dirRMenu").hide();
|
||||
$("#fileRMenu").hide();
|
||||
},
|
||||
init: function () {
|
||||
$("#file").click(function () {
|
||||
$(this).focus();
|
||||
|
@ -130,13 +210,18 @@ var tree = {
|
|||
var setting = {
|
||||
data: {
|
||||
key: {
|
||||
title: "title"
|
||||
title: "path"
|
||||
}
|
||||
},
|
||||
view: {
|
||||
showTitle: true,
|
||||
selectedMulti: false
|
||||
},
|
||||
async: {
|
||||
enable: true,
|
||||
url: "/file/refresh",
|
||||
autoParam: ["path"]
|
||||
},
|
||||
callback: {
|
||||
onDblClick: function (event, treeId, treeNode) {
|
||||
if (treeNode) {
|
||||
|
@ -164,9 +249,9 @@ var tree = {
|
|||
});
|
||||
} else { // 右击了目录
|
||||
if (wide.curNode.removable) {
|
||||
$("#dirRMenu .remove").removeClass("disabled");
|
||||
$("#dirRMenu .remove, #dirRMenu .rename").removeClass("disabled");
|
||||
} else {
|
||||
$("#dirRMenu .remove").addClass("disabled");
|
||||
$("#dirRMenu .remove, #dirRMenu .rename").addClass("disabled");
|
||||
}
|
||||
|
||||
if (wide.curNode.creatable) {
|
||||
|
@ -200,17 +285,30 @@ var tree = {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._initSearch();
|
||||
},
|
||||
openFile: function (treeNode) {
|
||||
openFile: function (treeNode, cursor) {
|
||||
wide.curNode = treeNode;
|
||||
var tempCursor = cursor;
|
||||
|
||||
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
||||
// 该节点文件已经打开
|
||||
if (editors.data[i].id === treeNode.tId) {
|
||||
editors.tabs.setCurrent(treeNode.tId);
|
||||
wide.curNode = treeNode;
|
||||
wide.curEditor = editors.data[i].editor;
|
||||
|
||||
if (!tempCursor) {
|
||||
tempCursor = wide.curEditor.getCursor();
|
||||
}
|
||||
$(".footer .cursor").text('| ' + (tempCursor.line + 1) + ':' + (tempCursor.ch + 1) + ' |');
|
||||
|
||||
wide.curEditor.setCursor(tempCursor);
|
||||
var half = Math.floor(wide.curEditor.getScrollInfo().clientHeight / wide.curEditor.defaultTextHeight() / 2);
|
||||
var cursorCoords = wide.curEditor.cursorCoords({line: tempCursor.line - half, ch: 0}, "local");
|
||||
wide.curEditor.scrollTo(0, cursorCoords.top);
|
||||
wide.curEditor.focus();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -238,9 +336,68 @@ var tree = {
|
|||
return false;
|
||||
}
|
||||
|
||||
editors.newEditor(data);
|
||||
if (!tempCursor) {
|
||||
tempCursor = CodeMirror.Pos(0, 0);
|
||||
}
|
||||
editors.newEditor(data, tempCursor);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
_initSearch: function () {
|
||||
$("#dialogSearchForm > input:eq(0)").keyup(function (event) {
|
||||
var $okBtn = $(this).closest(".dialog-main").find(".dialog-footer > button:eq(0)");
|
||||
if (event.which === 13 && !$okBtn.prop("disabled")) {
|
||||
$okBtn.click();
|
||||
}
|
||||
|
||||
if ($.trim($(this).val()) === "") {
|
||||
$okBtn.prop("disabled", true);
|
||||
} else {
|
||||
$okBtn.prop("disabled", false);
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogSearchForm > input:eq(1)").keyup(function (event) {
|
||||
var $okBtn = $(this).closest(".dialog-main").find(".dialog-footer > button:eq(0)");
|
||||
if (event.which === 13 && !$okBtn.prop("disabled")) {
|
||||
$okBtn.click();
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogSearchForm").dialog({
|
||||
"modal": true,
|
||||
"height": 62,
|
||||
"width": 260,
|
||||
"title": config.label.search,
|
||||
"okText": config.label.search,
|
||||
"cancelText": config.label.cancel,
|
||||
"afterOpen": function () {
|
||||
$("#dialogSearchForm > input:eq(0)").val('').focus();
|
||||
$("#dialogSearchForm > input:eq(1)").val('');
|
||||
$("#dialogSearchForm").closest(".dialog-main").find(".dialog-footer > button:eq(0)").prop("disabled", true);
|
||||
},
|
||||
"ok": function () {
|
||||
var request = newWideRequest();
|
||||
request.dir = wide.curNode.path;
|
||||
request.text = $("#dialogSearchForm > input:eq(0)").val();
|
||||
request.extension = $("#dialogSearchForm > input:eq(1)").val();
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/file/search/text',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#dialogSearchForm").dialog("close");
|
||||
editors.appendSearch(data.founds, 'founds', request.text);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var wide = {
|
||||
curNode: undefined,
|
||||
curEditor: undefined,
|
||||
|
@ -50,6 +66,10 @@ var wide = {
|
|||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
$("#dialogRemoveConfirm").dialog("close");
|
||||
bottomGroup.tabs.setCurrent("notification");
|
||||
windows.flowBottom();
|
||||
$(".bottom-window-group .notification").focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -66,7 +86,7 @@ var wide = {
|
|||
}
|
||||
} else {
|
||||
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
||||
if (tree._isParents(editors.data[i].id, wide.curNode.tId)) {
|
||||
if (tree.isParents(editors.data[i].id, wide.curNode.tId)) {
|
||||
$(".edit-panel .tabs > div[data-index=" + editors.data[i].id + "]").find(".ico-close").click();
|
||||
i--;
|
||||
ii--;
|
||||
|
@ -78,6 +98,65 @@ var wide = {
|
|||
}
|
||||
});
|
||||
|
||||
$("#dialogRenamePrompt").dialog({
|
||||
"modal": true,
|
||||
"height": 52,
|
||||
"width": 260,
|
||||
"title": config.label.rename,
|
||||
"okText": config.label.rename,
|
||||
"cancelText": config.label.cancel,
|
||||
"afterOpen": function () {
|
||||
var index = wide.curNode.name.lastIndexOf("."),
|
||||
name = wide.curNode.name.substring(0, index);
|
||||
if (index === -1) {
|
||||
name = wide.curNode.name;
|
||||
}
|
||||
$("#dialogRenamePrompt").closest(".dialog-main").find(".dialog-footer > button:eq(0)").prop("disabled", true);
|
||||
$("#dialogRenamePrompt > input").val(name).select().focus();
|
||||
},
|
||||
"ok": function () {
|
||||
var name = $("#dialogRenamePrompt > input").val(),
|
||||
request = newWideRequest();
|
||||
|
||||
request.oldPath = wide.curNode.path;
|
||||
|
||||
var pathIndex = wide.curNode.path.lastIndexOf(config.pathSeparator),
|
||||
nameIndex = wide.curNode.name.lastIndexOf("."),
|
||||
ext = wide.curNode.name.substring(nameIndex, wide.curNode.name.length);
|
||||
request.newPath = wide.curNode.path.substring(0, pathIndex) + config.pathSeparator
|
||||
+ name + ext;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/file/rename',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
$("#dialogRenamePrompt").dialog("close");
|
||||
bottomGroup.tabs.setCurrent("notification");
|
||||
windows.flowBottom();
|
||||
$(".bottom-window-group .notification").focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#dialogRenamePrompt").dialog("close");
|
||||
|
||||
// update tree node
|
||||
wide.curNode.name = name + ext;
|
||||
wide.curNode.title = request.newPath;
|
||||
wide.curNode.path = request.newPath;
|
||||
tree.fileTree.updateNode(wide.curNode);
|
||||
|
||||
// update open editor tab name
|
||||
var $currentSpan = $(".edit-panel .tabs > div[data-index=" + wide.curNode.tId + "] > span:eq(0)");
|
||||
$currentSpan.attr("title", request.newPath);
|
||||
$currentSpan.html($currentSpan.find("span").html() + wide.curNode.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogNewFilePrompt").dialog({
|
||||
"modal": true,
|
||||
"height": 52,
|
||||
|
@ -103,43 +182,15 @@ var wide = {
|
|||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
$("#dialogNewFilePrompt").dialog("close");
|
||||
bottomGroup.tabs.setCurrent("notification");
|
||||
windows.flowBottom();
|
||||
$(".bottom-window-group .notification").focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#dialogNewFilePrompt").dialog("close");
|
||||
var suffix = name.split(".")[1],
|
||||
iconSkin = "ico-ztree-other ";
|
||||
switch (suffix) {
|
||||
case "html", "htm":
|
||||
iconSkin = "ico-ztree-html ";
|
||||
break;
|
||||
case "go":
|
||||
iconSkin = "ico-ztree-go ";
|
||||
break;
|
||||
case "css":
|
||||
iconSkin = "ico-ztree-css ";
|
||||
break;
|
||||
case "txt":
|
||||
iconSkin = "ico-ztree-text ";
|
||||
break;
|
||||
case "sql":
|
||||
iconSkin = "ico-ztree-sql ";
|
||||
break;
|
||||
case "properties":
|
||||
iconSkin = "ico-ztree-pro ";
|
||||
break;
|
||||
case "md":
|
||||
iconSkin = "ico-ztree-md ";
|
||||
break;
|
||||
case "js", "json":
|
||||
iconSkin = "ico-ztree-js ";
|
||||
break;
|
||||
case "xml":
|
||||
iconSkin = "ico-ztree-xml ";
|
||||
break;
|
||||
case "jpg", "jpeg", "bmp", "gif", "png", "svg", "ico":
|
||||
iconSkin = "ico-ztree-img ";
|
||||
break;
|
||||
}
|
||||
var iconSkin = wide.getClassBySuffix(name.split(".")[1]);
|
||||
|
||||
tree.fileTree.addNodes(wide.curNode, [{
|
||||
"name": name,
|
||||
|
@ -179,6 +230,10 @@ var wide = {
|
|||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
$("#dialogNewDirPrompt").dialog("close");
|
||||
bottomGroup.tabs.setCurrent("notification");
|
||||
windows.flowBottom();
|
||||
$(".bottom-window-group .notification").focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -196,6 +251,92 @@ var wide = {
|
|||
}
|
||||
});
|
||||
|
||||
$("#dialogGoFilePrompt").dialog({
|
||||
"modal": true,
|
||||
"height": 300,
|
||||
"width": 660,
|
||||
"title": config.label.goto_file,
|
||||
"okText": config.label.go,
|
||||
"cancelText": config.label.cancel,
|
||||
"afterInit": function () {
|
||||
$("#dialogGoFilePrompt").on("dblclick", "li", function () {
|
||||
var tId = tree.getTIdByPath($(this).find(".ft-small").text());
|
||||
tree.openFile(tree.fileTree.getNodeByTId(tId));
|
||||
tree.fileTree.selectNode(wide.curNode);
|
||||
$("#dialogGoFilePrompt").dialog("close");
|
||||
});
|
||||
|
||||
$("#dialogGoFilePrompt").on("click", "li", function () {
|
||||
var $list = $("#dialogGoFilePrompt > .list")
|
||||
$list.find("li").removeClass("selected");
|
||||
$list.data("index", $(this).data("index"));
|
||||
$(this).addClass("selected");
|
||||
});
|
||||
|
||||
hotkeys.bindList($("#dialogGoFilePrompt > input"), $("#dialogGoFilePrompt > .list"), function ($selected) {
|
||||
var tId = tree.getTIdByPath($selected.find(".ft-small").text());
|
||||
tree.openFile(tree.fileTree.getNodeByTId(tId));
|
||||
tree.fileTree.selectNode(wide.curNode);
|
||||
$("#dialogGoFilePrompt").dialog("close");
|
||||
});
|
||||
|
||||
$("#dialogGoFilePrompt > input").bind("input", function () {
|
||||
var name = $("#dialogGoFilePrompt > input").val();
|
||||
|
||||
var request = newWideRequest();
|
||||
request.path = '';
|
||||
request.name = '*' + name + '*';
|
||||
if (wide.curNode) {
|
||||
request.path = wide.curNode.path;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/file/find/name',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var goFileHTML = '';
|
||||
for (var i = 0, max = data.founds.length; i < max; i++) {
|
||||
var path = data.founds[i].path,
|
||||
name = path.substr(path.lastIndexOf(config.pathSeparator) + 1),
|
||||
icoSkin = wide.getClassBySuffix(name.split(".")[1]);
|
||||
if (i === 0) {
|
||||
goFileHTML += '<li data-index="' + i + '" class="selected" title="'
|
||||
+ path + '"><span class="'
|
||||
+ icoSkin + 'ico"></span>'
|
||||
+ name + ' <span class="ft-small">'
|
||||
+ path + '</span></li>';
|
||||
} else {
|
||||
goFileHTML += '<li data-index="' + i + '" title="'
|
||||
+ path + '"><span class="' + icoSkin + 'ico"></span>'
|
||||
+ name + ' <span class="ft-small">'
|
||||
+ path + '</span></li>';
|
||||
}
|
||||
}
|
||||
|
||||
$("#dialogGoFilePrompt > ul").html(goFileHTML);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
"afterOpen": function () {
|
||||
$("#dialogGoFilePrompt > input").val('').focus();
|
||||
$("#dialogGoFilePrompt").closest(".dialog-main").find(".dialog-footer > button:eq(0)").prop("disabled", true);
|
||||
$("#dialogGoFilePrompt .list").html('').data("index", 0);
|
||||
},
|
||||
"ok": function () {
|
||||
var tId = tree.getTIdByPath($("#dialogGoFilePrompt .selected .ft-small").text());
|
||||
tree.openFile(tree.fileTree.getNodeByTId(tId));
|
||||
tree.fileTree.selectNode(wide.curNode);
|
||||
$("#dialogGoFilePrompt").dialog("close");
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogGoLinePrompt").dialog({
|
||||
"modal": true,
|
||||
"height": 52,
|
||||
|
@ -223,88 +364,6 @@ var wide = {
|
|||
editor.focus();
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogSearchForm > input:eq(0)").keyup(function (event) {
|
||||
var $okBtn = $(this).closest(".dialog-main").find(".dialog-footer > button:eq(0)");
|
||||
if (event.which === 13 && !$okBtn.prop("disabled")) {
|
||||
$okBtn.click();
|
||||
}
|
||||
|
||||
if ($.trim($(this).val()) === "") {
|
||||
$okBtn.prop("disabled", true);
|
||||
} else {
|
||||
$okBtn.prop("disabled", false);
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogSearchForm > input:eq(1)").keyup(function (event) {
|
||||
var $okBtn = $(this).closest(".dialog-main").find(".dialog-footer > button:eq(0)");
|
||||
if (event.which === 13 && !$okBtn.prop("disabled")) {
|
||||
$okBtn.click();
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogSearchForm").dialog({
|
||||
"modal": true,
|
||||
"height": 62,
|
||||
"width": 260,
|
||||
"title": config.label.search,
|
||||
"okText": config.label.search,
|
||||
"cancelText": config.label.cancel,
|
||||
"afterOpen": function () {
|
||||
$("#dialogSearchForm > input:eq(0)").val('').focus();
|
||||
$("#dialogSearchForm > input:eq(1)").val('');
|
||||
$("#dialogSearchForm").closest(".dialog-main").find(".dialog-footer > button:eq(0)").prop("disabled", true);
|
||||
},
|
||||
"ok": function () {
|
||||
var request = newWideRequest();
|
||||
request.dir = wide.curNode.path;
|
||||
request.text = $("#dialogSearchForm > input:eq(0)").val();
|
||||
request.extension = $("#dialogSearchForm > input:eq(1)").val();
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/file/search/text',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#dialogSearchForm").dialog("close");
|
||||
editors.appendSearch(data.founds, 'founds', request.text);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#dialogAbout").load('/about', function () {
|
||||
$("#dialogAbout").dialog({
|
||||
"modal": true,
|
||||
"height": 460,
|
||||
"width": 800,
|
||||
"title": config.label.about,
|
||||
"hideFooter": true,
|
||||
"afterOpen": function () {
|
||||
$.ajax({
|
||||
url: "http://rhythm.b3log.org/version/wide/latest",
|
||||
type: "GET",
|
||||
dataType: "jsonp",
|
||||
jsonp: "callback",
|
||||
success: function (data, textStatus) {
|
||||
if ($("#dialogAbout .version").text() === data.wideVersion) {
|
||||
$(".upgrade").text(config.label.uptodate);
|
||||
} else {
|
||||
$(".upgrade").html(config.label.new_version_available + config.label.colon
|
||||
+ "<a href='" + data.wideDownload
|
||||
+ "' target='_blank'>" + data.wideVersion + "</a>");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
_initLayout: function () {
|
||||
var mainH = $(window).height() - $(".menu").height() - $(".footer").height(),
|
||||
|
@ -343,7 +402,7 @@ var wide = {
|
|||
|
||||
switch (data.cmd) {
|
||||
case 'run': // 正在运行
|
||||
bottomGroup.fillOutput($('.bottom-window-group .output > div').html() + data.output);
|
||||
bottomGroup.fillOutput($('.bottom-window-group .output > div').html() + '<pre>' + data.output + '</pre>');
|
||||
wide.curProcessId = data.pid;
|
||||
|
||||
break;
|
||||
|
@ -358,17 +417,17 @@ var wide = {
|
|||
case 'start-test':
|
||||
case 'start-install':
|
||||
case 'start-get':
|
||||
bottomGroup.fillOutput(data.output);
|
||||
bottomGroup.fillOutput('<pre>' + data.output + '</pre>');
|
||||
|
||||
break;
|
||||
case 'go test':
|
||||
case 'go install':
|
||||
case 'go get':
|
||||
bottomGroup.fillOutput($('.bottom-window-group .output > div').html() + data.output);
|
||||
bottomGroup.fillOutput($('.bottom-window-group .output > div').html() + '<pre>' + data.output + '</pre>');
|
||||
|
||||
break;
|
||||
case 'build':
|
||||
bottomGroup.fillOutput($('.bottom-window-group .output > div').html() + data.output);
|
||||
bottomGroup.fillOutput($('.bottom-window-group .output > div').html() + '<pre>' + data.output + '</pre>');
|
||||
|
||||
if (data.lints) { // 说明编译有错误输出
|
||||
for (var i = 0; i < data.lints.length; i++) {
|
||||
|
@ -495,55 +554,9 @@ var wide = {
|
|||
|
||||
wide._save(path, wide.curEditor);
|
||||
},
|
||||
saveAllFiles: function () {
|
||||
if ($(".menu li.save-all").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
||||
var path = tree.fileTree.getNodeByTId(editors.data[i].id).path;
|
||||
var editor = editors.data[i].editor;
|
||||
|
||||
if ("text/x-go" === editor.getOption("mode")) {
|
||||
wide.fmt(path, editor);
|
||||
} else {
|
||||
wide._save(path, editor);
|
||||
}
|
||||
}
|
||||
},
|
||||
closeAllFiles: function () {
|
||||
if ($(".menu li.close-all").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置全部关闭标识
|
||||
var removeData = [];
|
||||
$(".edit-panel .tabs > div").each(function (i) {
|
||||
if (i !== 0) {
|
||||
removeData.push($(this).data("index"));
|
||||
}
|
||||
});
|
||||
$("#dialogCloseEditor").data("removeData", removeData);
|
||||
// 开始关闭
|
||||
$(".edit-panel .tabs .ico-close:eq(0)").click();
|
||||
},
|
||||
exit: function () {
|
||||
var request = newWideRequest();
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/logout',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (data.succ) {
|
||||
window.location.href = "/login";
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
stop: function () {
|
||||
if ($(".toolbars .ico-buildrun").length === 1) {
|
||||
wide.run();
|
||||
menu.run();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -565,155 +578,6 @@ var wide = {
|
|||
}
|
||||
});
|
||||
},
|
||||
// 构建.
|
||||
build: function () {
|
||||
wide.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.build").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
request.code = wide.curEditor.getValue();
|
||||
request.nextCmd = ""; // 只构建,无下一步操作
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/build',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
}
|
||||
});
|
||||
},
|
||||
// 构建并运行.
|
||||
run: function () {
|
||||
wide.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.run").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".toolbars .ico-stop").length === 1) {
|
||||
wide.stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
request.code = wide.curEditor.getValue();
|
||||
request.nextCmd = "run";
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/build',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
$(".toolbars .ico-buildrun").addClass("ico-stop")
|
||||
.removeClass("ico-buildrun").attr("title", config.label.stop);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 测试.
|
||||
test: function () {
|
||||
wide.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.test").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/go/test',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
}
|
||||
});
|
||||
},
|
||||
goget: function () {
|
||||
wide.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.go-get").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/go/get',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
}
|
||||
});
|
||||
},
|
||||
goinstall: function () {
|
||||
wide.saveAllFiles();
|
||||
|
||||
var currentPath = editors.getCurrentPath();
|
||||
if (!currentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($(".menu li.go-install").hasClass("disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = newWideRequest();
|
||||
request.file = currentPath;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/go/install',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
beforeSend: function (data) {
|
||||
bottomGroup.resetOutput();
|
||||
},
|
||||
success: function (data) {
|
||||
}
|
||||
});
|
||||
},
|
||||
gofmt: function (path, editor) {
|
||||
var cursor = editor.getCursor();
|
||||
var scrollInfo = editor.getScrollInfo();
|
||||
|
@ -793,8 +657,42 @@ var wide = {
|
|||
wide._save(path, editor);
|
||||
}
|
||||
},
|
||||
openAbout: function () {
|
||||
$("#dialogAbout").dialog("open");
|
||||
getClassBySuffix: function (suffix) {
|
||||
var iconSkin = "ico-ztree-other ";
|
||||
switch (suffix) {
|
||||
case "html", "htm":
|
||||
iconSkin = "ico-ztree-html ";
|
||||
break;
|
||||
case "go":
|
||||
iconSkin = "ico-ztree-go ";
|
||||
break;
|
||||
case "css":
|
||||
iconSkin = "ico-ztree-css ";
|
||||
break;
|
||||
case "txt":
|
||||
iconSkin = "ico-ztree-text ";
|
||||
break;
|
||||
case "sql":
|
||||
iconSkin = "ico-ztree-sql ";
|
||||
break;
|
||||
case "properties":
|
||||
iconSkin = "ico-ztree-pro ";
|
||||
break;
|
||||
case "md":
|
||||
iconSkin = "ico-ztree-md ";
|
||||
break;
|
||||
case "js", "json":
|
||||
iconSkin = "ico-ztree-js ";
|
||||
break;
|
||||
case "xml":
|
||||
iconSkin = "ico-ztree-xml ";
|
||||
break;
|
||||
case "jpg", "jpeg", "bmp", "gif", "png", "svg", "ico":
|
||||
iconSkin = "ico-ztree-img ";
|
||||
break;
|
||||
}
|
||||
|
||||
return iconSkin;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2014, B3log
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var windows = {
|
||||
isMaxEditor: false,
|
||||
init: function () {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
This directory is used to hold user _admin_ customized style file `style.css`, it will be generated when wide startup automatically.
|
|
@ -1,13 +0,0 @@
|
|||
body,
|
||||
input,
|
||||
button {
|
||||
font-family: Helvetica;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.edit-exprinfo,
|
||||
.CodeMirror,
|
||||
.CodeMirror-hints {
|
||||
font-family: Consolas, 'Courier New', monospace;
|
||||
font-size: inherit;
|
||||
}
|
|
@ -10,4 +10,5 @@ button {
|
|||
.CodeMirror-hints {
|
||||
font-family: {{.user.Editor.FontFamily}};
|
||||
font-size: {{.user.Editor.FontSize}};
|
||||
line-height: {{.user.Editor.LineHeight}};
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type myfile struct{}
|
||||
|
||||
// File utilities.
|
||||
var File = myfile{}
|
||||
|
||||
// GetFileSize get the length in bytes of file of the specified path.
|
||||
func (*myfile) GetFileSize(path string) int64 {
|
||||
fi, err := os.Stat(path)
|
||||
if nil != err {
|
||||
return -1
|
||||
}
|
||||
|
||||
return fi.Size()
|
||||
}
|
||||
|
||||
// IsExist determines whether the file spcified by the given path is exists.
|
||||
func (*myfile) IsExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
||||
|
||||
// IsBinary determines whether the specified content is a binary file content.
|
||||
func (*myfile) IsBinary(content string) bool {
|
||||
for _, b := range content {
|
||||
if 0 == b {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsImg determines whether the specified extension is a image.
|
||||
func (*myfile) IsImg(extension string) bool {
|
||||
ext := strings.ToLower(extension)
|
||||
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg", ".bmp", ".gif", ".png", ".svg", ".ico":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsDir determines whether the specified path is a directory.
|
||||
func (*myfile) IsDir(path string) bool {
|
||||
fio, err := os.Lstat(path)
|
||||
if nil != err {
|
||||
glog.Warningf("Determines whether [%s] is a directory failed: [%v]", path, err)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return fio.IsDir()
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
PathSeparator = string(os.PathSeparator) // OS-specific path separator
|
||||
PathListSeparator = string(os.PathListSeparator) // OS-specific path list separator
|
||||
)
|
||||
|
||||
type mygo struct{}
|
||||
|
||||
// Go utilities.
|
||||
var Go = mygo{}
|
||||
|
||||
// GetAPIPath gets the Go source code path.
|
||||
//
|
||||
// 1. before Go 1.4: $GOROOT/src/pkg
|
||||
// 2. Go 1.4 and after: $GOROOT/src
|
||||
func (*mygo) GetAPIPath() string {
|
||||
ret := runtime.GOROOT() + "/src/pkg" // before Go 1.4
|
||||
if !File.IsExist(ret) {
|
||||
ret = runtime.GOROOT() + "/src" // Go 1.4 and after
|
||||
}
|
||||
|
||||
return filepath.FromSlash(path.Clean(ret))
|
||||
}
|
||||
|
||||
// IsAPI determines whether the specified path belongs to Go API.
|
||||
func (*mygo) IsAPI(path string) bool {
|
||||
apiPath := Go.GetAPIPath()
|
||||
|
||||
return strings.HasPrefix(path, apiPath)
|
||||
}
|
||||
|
||||
// GetGoFormats gets Go format tools. It may return ["gofmt", "goimports"].
|
||||
func (*mygo) GetGoFormats() []string {
|
||||
ret := []string {"gofmt"}
|
||||
|
||||
p := Go.GetExecutableInGOBIN("goimports")
|
||||
if File.IsExist(p) {
|
||||
ret = append(ret, "goimports")
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetExecutableInGOBIN gets executable file under GOBIN path.
|
||||
//
|
||||
// The specified executable should not with extension, this function will append .exe if on Windows.
|
||||
func (*mygo) GetExecutableInGOBIN(executable string) string {
|
||||
if OS.IsWindows() {
|
||||
executable += ".exe"
|
||||
}
|
||||
|
||||
gopaths := filepath.SplitList(os.Getenv("GOPATH"))
|
||||
|
||||
for _, gopath := range gopaths {
|
||||
// $GOPATH/bin/$GOOS_$GOARCH/executable
|
||||
ret := gopath + PathSeparator + "bin" + PathSeparator +
|
||||
os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") + PathSeparator + executable
|
||||
if File.IsExist(ret) {
|
||||
return ret
|
||||
}
|
||||
|
||||
// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/executable
|
||||
ret = gopath + PathSeparator + "bin" + PathSeparator +
|
||||
runtime.GOOS + "_" + runtime.GOARCH + PathSeparator + executable
|
||||
if File.IsExist(ret) {
|
||||
return ret
|
||||
}
|
||||
|
||||
// $GOPATH/bin/executable
|
||||
ret = gopath + PathSeparator + "bin" + PathSeparator + executable
|
||||
if File.IsExist(ret) {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
// $GOBIN/executable
|
||||
return os.Getenv("GOBIN") + PathSeparator + executable
|
||||
}
|
14
util/net.go
14
util/net.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Utilities.
|
||||
package util
|
||||
|
||||
|
|
14
util/os.go
14
util/os.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
|
@ -8,7 +22,7 @@ import (
|
|||
|
||||
// Recover recovers a panic.
|
||||
func Recover() {
|
||||
if re := recover(); re != nil {
|
||||
if re := recover(); nil != re {
|
||||
glog.Errorf("PANIC RECOVERED:\n %v, %s", re, debug.Stack())
|
||||
}
|
||||
}
|
||||
|
|
14
util/ret.go
14
util/ret.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
type str struct{}
|
||||
|
||||
// String utilities.
|
||||
var Str = str{}
|
||||
|
||||
// Contains determines whether the str is in the strs.
|
||||
func (*str) Contains(str string, strs []string) bool {
|
||||
for _, v := range strs {
|
||||
if v == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// LCS gets the longest common substring of s1 and s2.
|
||||
//
|
||||
// Refers to http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Longest_common_substring.
|
||||
func (*str) LCS(s1 string, s2 string) string {
|
||||
var m = make([][]int, 1+len(s1))
|
||||
|
||||
for i := 0; i < len(m); i++ {
|
||||
m[i] = make([]int, 1+len(s2))
|
||||
}
|
||||
|
||||
longest := 0
|
||||
x_longest := 0
|
||||
|
||||
for x := 1; x < 1+len(s1); x++ {
|
||||
for y := 1; y < 1+len(s2); y++ {
|
||||
if s1[x-1] == s2[y-1] {
|
||||
m[x][y] = m[x-1][y-1] + 1
|
||||
if m[x][y] > longest {
|
||||
longest = m[x][y]
|
||||
x_longest = x
|
||||
}
|
||||
} else {
|
||||
m[x][y] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s1[x_longest-longest : x_longest]
|
||||
}
|
|
@ -1,4 +1,17 @@
|
|||
//部分代码参考(Ctrl+c)自golang bufio
|
||||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,6 +1,21 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
|
@ -15,9 +30,41 @@ type WSChannel struct {
|
|||
Time time.Time // the latest use time
|
||||
}
|
||||
|
||||
// WriteJSON writes the JSON encoding of v to the channel.
|
||||
func (c *WSChannel) WriteJSON(v interface{}) (ret error) {
|
||||
if nil == c.Conn {
|
||||
return errors.New("connection is nil, channel has been closed")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); nil != r {
|
||||
ret = errors.New("channel has been closed")
|
||||
}
|
||||
}()
|
||||
|
||||
return c.Conn.WriteJSON(v)
|
||||
}
|
||||
|
||||
// ReadJSON reads the next JSON-encoded message from the channel and stores it in the value pointed to by v.
|
||||
func (c *WSChannel) ReadJSON(v interface{}) (ret error) {
|
||||
if nil == c.Conn {
|
||||
return errors.New("connection is nil, channel has been closed")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); nil != r {
|
||||
ret = errors.New("channel has been closed")
|
||||
}
|
||||
}()
|
||||
|
||||
return c.Conn.ReadJSON(v)
|
||||
}
|
||||
|
||||
// Close closed the channel.
|
||||
func (c *WSChannel) Close() {
|
||||
c.Conn.Close()
|
||||
if nil != c.Conn {
|
||||
c.Conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh refreshes the channel by updating its use time.
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) 2014, B3log
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type myzip struct{}
|
||||
|
||||
// Zip utilities.
|
||||
var Zip = myzip{}
|
||||
|
||||
type ZipFile struct {
|
||||
zipFile *os.File
|
||||
writer *zip.Writer
|
||||
}
|
||||
|
||||
func (*myzip) Create(filename string) (*ZipFile, error) {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ZipFile{zipFile: file, writer: zip.NewWriter(file)}, nil
|
||||
}
|
||||
|
||||
func (z *ZipFile) Close() error {
|
||||
err := z.writer.Close()
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
return z.zipFile.Close() // close the underlying writer
|
||||
}
|
||||
|
||||
func (z *ZipFile) AddEntryN(path string, names ...string) error {
|
||||
for _, name := range names {
|
||||
zipPath := filepath.Join(path, name)
|
||||
err := z.AddEntry(zipPath, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (z *ZipFile) AddEntry(path, name string) error {
|
||||
fi, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fh, err := zip.FileInfoHeader(fi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fh.Name = filepath.ToSlash(filepath.Clean(path))
|
||||
fh.Method = zip.Deflate // data compression algorithm
|
||||
|
||||
if fi.IsDir() {
|
||||
fh.Name = fh.Name + "/" // be care the ending separator
|
||||
}
|
||||
|
||||
entry, err := z.writer.CreateHeader(fh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(entry, file)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (z *ZipFile) AddDirectoryN(path string, names ...string) error {
|
||||
for _, name := range names {
|
||||
err := z.AddDirectory(path, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (z *ZipFile) AddDirectory(path, dirName string) error {
|
||||
files, err := ioutil.ReadDir(dirName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
localPath := filepath.Join(dirName, file.Name())
|
||||
zipPath := filepath.Join(path, file.Name())
|
||||
|
||||
err = nil
|
||||
if file.IsDir() {
|
||||
z.AddEntry(path, dirName)
|
||||
|
||||
err = z.AddDirectory(zipPath, localPath)
|
||||
} else {
|
||||
err = z.AddEntry(zipPath, localPath)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -31,14 +31,14 @@
|
|||
<span>{{.i18n.file}}</span>
|
||||
<div class="frame">
|
||||
<ul>
|
||||
<li class="save-all disabled" onclick="wide.saveAllFiles()">
|
||||
<li class="save-all disabled" onclick="menu.saveAllFiles()">
|
||||
<span>{{.i18n.save_all_files}}</span>
|
||||
</li>
|
||||
<li class="close-all" onclick="wide.closeAllFiles()">
|
||||
<li class="close-all" onclick="menu.closeAllFiles()">
|
||||
<span>{{.i18n.close_all_files}}</span>
|
||||
</li>
|
||||
<li class="hr"></li>
|
||||
<li onclick="wide.exit()">
|
||||
<li onclick="menu.exit()">
|
||||
<span>{{.i18n.exit}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -48,21 +48,21 @@
|
|||
<span>{{.i18n.run}}</span>
|
||||
<div class="frame">
|
||||
<ul>
|
||||
<li class="build disabled" onclick="wide.build()">
|
||||
<li class="build disabled" onclick="menu.build()">
|
||||
<span>{{.i18n.build}}</span>
|
||||
</li>
|
||||
<li class="run disabled" onclick="wide.run()">
|
||||
<li class="run disabled" onclick="menu.run()">
|
||||
<span>{{.i18n.build_n_run}}</span>
|
||||
</li>
|
||||
<li class="hr"></li>
|
||||
<li class="go-test disabled" onclick="wide.test()">
|
||||
<li class="go-test disabled" onclick="menu.test()">
|
||||
<span>{{.i18n.test}}</span>
|
||||
</li>
|
||||
<li class="hr"></li>
|
||||
<li class="go-get disabled" onclick="wide.goget()">
|
||||
<li class="go-get disabled" onclick="menu.goget()">
|
||||
<span>{{.i18n.goget}}</span>
|
||||
</li>
|
||||
<li class="go-install disabled" onclick="wide.goinstall()">
|
||||
<li class="go-install disabled" onclick="menu.goinstall()">
|
||||
<span>{{.i18n.goinstall}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -90,6 +90,9 @@
|
|||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li onclick="menu.openPreference()">
|
||||
<span>{{.i18n.perference}}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>{{.i18n.help}}</span>
|
||||
<div class="frame">
|
||||
|
@ -108,7 +111,7 @@
|
|||
<li onclick="editors.openStartPage()">
|
||||
<span>{{.i18n.start_page}}</span>
|
||||
</li>
|
||||
<li onclick="wide.openAbout()">
|
||||
<li onclick="menu.openAbout()">
|
||||
<span>{{.i18n.about}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -134,6 +137,10 @@
|
|||
<li class="create" onclick="tree.newFile(this);">{{.i18n.create_file}}</li>
|
||||
<li class="create" onclick="tree.newDir(this);">{{.i18n.create_dir}}</li>
|
||||
<li class="remove" onclick="tree.removeIt(this);">{{.i18n.delete}}</li>
|
||||
<li class="rename" onclick="tree.rename(this);">{{.i18n.rename}}</li>
|
||||
<li class="hr"></li>
|
||||
<li class="refresh" onclick="tree.refresh(this);">{{.i18n.refresh}}</li>
|
||||
<li class="export" onclick="tree.export(this);">{{.i18n.export}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
@ -141,6 +148,9 @@
|
|||
<div id="fileRMenu" class="frame">
|
||||
<ul>
|
||||
<li class="remove" onclick="tree.removeIt(this);">{{.i18n.delete}}</li>
|
||||
<li class="rename" onclick="tree.rename(this);">{{.i18n.rename}}</li>
|
||||
<li class="hr"></li>
|
||||
<li class="export" onclick="tree.export(this);">{{.i18n.export}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -149,7 +159,7 @@
|
|||
|
||||
<div class="edit-panel">
|
||||
<div class="toolbars fn-none">
|
||||
<span onclick="wide.run()" class="font-ico ico-buildrun" title="{{.i18n.build_n_run}}"></span>
|
||||
<span onclick="menu.run()" class="font-ico ico-buildrun" title="{{.i18n.build_n_run}}"></span>
|
||||
<span onclick="wide.saveFile()" title="{{.i18n.save}}" class="font-ico ico-save"></span>
|
||||
<span onclick="wide.fmt(editors.getCurrentPath(), wide.curEditor)" class="ico-format font-ico" title="{{.i18n.format}}"></span>
|
||||
<span class="font-ico ico-max" onclick="windows.maxEditor()" title="{{.i18n.max_editor}}"></span>
|
||||
|
@ -159,7 +169,7 @@
|
|||
<li onclick="editors.close()" title="{{.i18n.close}}">
|
||||
<span>{{.i18n.close}}</span>
|
||||
</li>
|
||||
<li onclick="wide.closeAllFiles()" title="{{.i18n.close_all_files}}">
|
||||
<li onclick="menu.closeAllFiles()" title="{{.i18n.close_all_files}}">
|
||||
<span>{{.i18n.close_all_files}}</span>
|
||||
</li>
|
||||
<li onclick="editors.closeOther()" title="{{.i18n.close_other}}">
|
||||
|
@ -230,15 +240,23 @@
|
|||
</div>
|
||||
<div id="dialogAlert" class="fn-none"></div>
|
||||
<div id="dialogAbout" class="fn-none"></div>
|
||||
<div id="dialogPreference" class="fn-none"></div>
|
||||
<div id="dialogNewFilePrompt" class="dialog-prompt fn-none">
|
||||
<input/>
|
||||
</div>
|
||||
<div id="dialogRenamePrompt" class="dialog-prompt fn-none">
|
||||
<input/>
|
||||
</div>
|
||||
<div id="dialogNewDirPrompt" class="dialog-prompt fn-none">
|
||||
<input/>
|
||||
</div>
|
||||
<div id="dialogGoLinePrompt" class="dialog-prompt fn-none">
|
||||
<input/>
|
||||
</div>
|
||||
<div id="dialogGoFilePrompt" class="dialog-prompt fn-none">
|
||||
<input/>
|
||||
<ul class="list" tabindex="-1"></ul>
|
||||
</div>
|
||||
<div id="dialogSearchForm" class="dialog-form fn-none">
|
||||
<input placeholder="{{.i18n.keyword}}" />
|
||||
<input placeholder="{{.i18n.file_format}}" />
|
||||
|
@ -259,8 +277,10 @@
|
|||
"restore_editor": "{{.i18n.restore_editor}}",
|
||||
"max_editor": "{{.i18n.max_editor}}",
|
||||
"delete": "{{.i18n.delete}}",
|
||||
"rename": "{{.i18n.rename}}",
|
||||
"cancel": "{{.i18n.cancel}}",
|
||||
"goto_line": "{{.i18n.goto_line}}",
|
||||
"goto_file": "{{.i18n.goto_file}}",
|
||||
"go": "{{.i18n.go}}",
|
||||
"create": "{{.i18n.create}}",
|
||||
"create_file": "{{.i18n.create_file}}",
|
||||
|
@ -279,7 +299,9 @@
|
|||
"new_version_available": "{{.i18n.new_version_available}}",
|
||||
"colon": "{{.i18n.colon}}",
|
||||
"file": "{{.i18n.file}}",
|
||||
"uptodate": "{{.i18n.uptodate}}"
|
||||
"uptodate": "{{.i18n.uptodate}}",
|
||||
"perference": "{{.i18n.perference}}",
|
||||
"apply": "{{.i18n.apply}}"
|
||||
},
|
||||
"channel": {
|
||||
"editor": '{{.conf.EditorChannel}}',
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
<li>Alt-Shift-F{{.i18n.colon}}{{.i18n.format}}</li>
|
||||
<li>Ctrl-L{{.i18n.colon}}{{.i18n.goto_line}}</li>
|
||||
<li>Ctrl-E{{.i18n.colon}}{{.i18n.delete_line}}</li>
|
||||
<li>Shift-Ctrl-Up{{.i18n.colon}}{{.i18n.copy_line_up}}</li>
|
||||
<li>Shift-Ctrl-Down{{.i18n.colon}}{{.i18n.copy_line_down}}</li>
|
||||
<li>Shift-Ctrl-Up{{.i18n.colon}}{{.i18n.copy_lines_up}}</li>
|
||||
<li>Shift-Ctrl-Down{{.i18n.colon}}{{.i18n.copy_lines_down}}</li>
|
||||
<li>Shift-Alt-Up{{.i18n.colon}}{{.i18n.move_lines_up}}</li>
|
||||
<li>Shift-Alt-Down{{.i18n.colon}}{{.i18n.move_lines_down}}</li>
|
||||
<li>Ctrl-S{{.i18n.colon}}{{.i18n.save_editor_file}}</li>
|
||||
<li>Shift-Ctrl-S{{.i18n.colon}}{{.i18n.save_all_editors_files}}</li>
|
||||
<li>Ctrl-Q{{.i18n.colon}}{{.i18n.close_editor}}</li>
|
||||
|
@ -27,6 +29,7 @@
|
|||
</ul>
|
||||
<h2>{{.i18n.search}}</h2>
|
||||
<ul>
|
||||
<li>Shift-Alt-O{{.i18n.colon}}{{.i18n.goto_file}}</li>
|
||||
<li>Ctrl-F{{.i18n.colon}}{{.i18n.search}}/{{.i18n.find}}</li>
|
||||
<li>Ctrl-G{{.i18n.colon}}{{.i18n.find_next}}</li>
|
||||
<li>Shift-Ctrl-G{{.i18n.colon}}{{.i18n.find_previous}}</li>
|
||||
|
@ -44,7 +47,9 @@
|
|||
</ul>
|
||||
<h2>{{.i18n.run}}</h2>
|
||||
<ul>
|
||||
<li>F5{{.i18n.colon}}{{.i18n.build}}</li>
|
||||
<li>F6{{.i18n.colon}}{{.i18n.build_n_run}}</li>
|
||||
<li>Ctrl-C{{.i18n.colon}}{{.i18n.clearOutput}}</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<li><a href="https://github.com/b3log/wide" target="_blank">GitHub</a></li>
|
||||
<li><a href="https://www.gitbook.io/book/88250/wide-user-guide" target="_blank">{{.i18n.help}}</a></li>
|
||||
<li><a href="https://github.com/b3log/wide/issues/new" target="_blank">{{.i18n.report_issues}}</a></li>
|
||||
<li><a href="/signup">{{.i18n.sign_up}}</a></li>
|
||||
<li><a class="button" href="/signup">{{.i18n.sign_up}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<div class="preference">
|
||||
<div class="tabs">
|
||||
<div class="current" data-index="appearence">
|
||||
<span title="{{.i18n.appearence}}">{{.i18n.appearence}}</span>
|
||||
</div>
|
||||
<div data-index="editor">
|
||||
<span title="{{.i18n.editor}}">{{.i18n.editor}}</span>
|
||||
</div>
|
||||
<div data-index="gotool">
|
||||
<span title="{{.i18n.gotool}}">{{.i18n.gotool}}</span>
|
||||
</div>
|
||||
<div data-index="user">
|
||||
<span title="{{.i18n.user}}">{{.i18n.user}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs-panel">
|
||||
<div data-index="appearence">
|
||||
<label>
|
||||
{{.i18n.font}}{{.i18n.colon}}
|
||||
<input data-value="{{.user.FontFamily}}" value="{{.user.FontFamily}}" name="fontFamily"/>
|
||||
</label>
|
||||
<label>
|
||||
{{.i18n.font_size}}{{.i18n.colon}}
|
||||
<input data-value="{{.user.FontSize}}" value="{{.user.FontSize}}" name="fontSize"/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="fn-none" data-index="editor">
|
||||
<label>
|
||||
{{.i18n.font}}{{.i18n.colon}}
|
||||
<input data-value="{{.user.Editor.FontFamily}}" value="{{.user.Editor.FontFamily}}" name="editorFontFamily"/>
|
||||
</label>
|
||||
<label>
|
||||
{{.i18n.font_size}}{{.i18n.colon}}
|
||||
<input data-value="{{.user.Editor.FontSize}}" value="{{.user.Editor.FontSize}}" name="editorFontSize"/>
|
||||
</label>
|
||||
<label>
|
||||
{{.i18n.line_height}}{{.i18n.colon}}
|
||||
<input data-value="{{.user.Editor.LineHeight}}" value="{{.user.Editor.LineHeight}}" name="editorLineHeight"/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="fn-none" data-index="gotool">
|
||||
<label>
|
||||
{{.i18n.go_format}}{{.i18n.colon}}
|
||||
<br/>
|
||||
<select class="select" id="goFmtSelect">
|
||||
{{range $index, $gofmt := .gofmts }}
|
||||
<option name="{{$gofmt}}" value="{{$gofmt}}" {{if eq $.user.GoFormat $gofmt}}selected="selected"{{end}}>{{$gofmt}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
<br/>
|
||||
</label>
|
||||
<input data-value="{{.user.GoFormat}}" value="{{.user.GoFormat}}" name="goFmt" hidden="hidden" />
|
||||
</div>
|
||||
<div class="fn-none" data-index="user">
|
||||
<label>
|
||||
{{.i18n.locale}}{{.i18n.colon}}
|
||||
<br/>
|
||||
<select class="select" id="localeSelect">
|
||||
{{range $index, $localeName := .locales }}
|
||||
<option name="{{$localeName}}" value="{{$localeName}}" {{if eq $.user.Locale $localeName}}selected="selected"{{end}}>{{$localeName}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
<br/>
|
||||
</label>
|
||||
<input data-value="{{.user.Locale}}" value="{{.user.Locale}}" name="locale" hidden="hidden" />
|
||||
<label>
|
||||
{{.i18n.workspace}}{{.i18n.colon}}
|
||||
<input data-value="{{.user.Workspace}}" value="{{.user.Workspace}}" name="workspace"/>
|
||||
</label>
|
||||
<label>
|
||||
{{.i18n.password}}{{.i18n.colon}}
|
||||
<input data-value="{{.user.Password}}" value="{{.user.Password}}" name="password" type="password"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -36,6 +36,7 @@
|
|||
<input id="username" placeholder="Username"/><br/>
|
||||
<input id="password" type="password" placeholder="Password"/><br/>
|
||||
<input id="confirmPassword" type="password" placeholder="Confirm your password"/>
|
||||
<button id="signUpBtn" class="button">{{.i18n.sign_up}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -134,6 +135,10 @@
|
|||
$("#msg").hide();
|
||||
}
|
||||
});
|
||||
|
||||
$("#signUpBtn").click(function () {
|
||||
signUp();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
|
Loading…
Reference in New Issue