commit
3602d8a987
|
@ -1,12 +1,7 @@
|
||||||
/wide.exe
|
/wide.exe
|
||||||
/wide
|
/wide
|
||||||
/main
|
|
||||||
/packer
|
|
||||||
|
|
||||||
/data/workspace/bin/
|
/static/user/admin/style.css
|
||||||
/data/workspace/pkg/
|
|
||||||
/data/workspace/src/
|
|
||||||
|
|
||||||
/data/user_workspaces/*/bin/
|
/header
|
||||||
/data/user_workspaces/*/pkg/
|
/header.exe
|
||||||
/data/user_workspaces/*/src/**/*.exe
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
author: DL88250@gmail.com
|
author: DL88250@gmail.com
|
||||||
description: A Web IDE for Teams using Golang.
|
description: A Web-based IDE for Teams using Golang.
|
||||||
|
|
||||||
filesets:
|
filesets:
|
||||||
depth: 10
|
depth: 10
|
||||||
includes:
|
includes:
|
||||||
- conf
|
- conf
|
||||||
- data
|
|
||||||
- doc
|
- doc
|
||||||
- i18n
|
- i18n
|
||||||
- static
|
- 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
|
22
README.md
22
README.md
|
@ -1,4 +1,6 @@
|
||||||
# Wide [![Build Status](https://drone.io/github.com/b3log/wide/status.png)](https://drone.io/github.com/b3log/wide/latest)
|
# 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
|
## Intro
|
||||||
|
|
||||||
|
@ -98,17 +100,22 @@ We have provided OS-specific executable binary as follows:
|
||||||
* windows-amd64/386
|
* windows-amd64/386
|
||||||
* darwin-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
|
### 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
|
2. Get dependencies with
|
||||||
* `go get -u`
|
* `go get`
|
||||||
* `go get -u github.com/88250/ide_stub`
|
* `go get github.com/88250/ide_stub`
|
||||||
* `go get -u github.com/nsf/gocode`
|
* `go get github.com/nsf/gocode`
|
||||||
3. Compile wide with `go build`
|
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
|
## Known Issues
|
||||||
|
|
||||||
* [Shell is not available on Windows](https://github.com/b3log/wide/issues/32)
|
* [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)
|
* [LiteIDE](https://github.com/visualfc/liteide)
|
||||||
* [gocode](https://github.com/nsf/gocode)
|
* [gocode](https://github.com/nsf/gocode)
|
||||||
* [Gorilla](https://github.com/gorilla)
|
* [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" />
|
<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.
|
// Configurations manipulations, all configurations (including user configurations) are stored in wide.json.
|
||||||
package conf
|
package conf
|
||||||
|
|
||||||
|
@ -7,7 +21,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
@ -51,23 +65,24 @@ type User struct {
|
||||||
type Editor struct {
|
type Editor struct {
|
||||||
FontFamily string
|
FontFamily string
|
||||||
FontSize string
|
FontSize string
|
||||||
|
LineHeight string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration.
|
// Configuration.
|
||||||
type conf struct {
|
type conf struct {
|
||||||
IP string // server ip, ${ip}
|
IP string // server ip, ${ip}
|
||||||
Server string // server host and port ({IP}:7070)
|
Port string // server port
|
||||||
StaticServer string // static resources server scheme, host and port (http://{IP}:7070)
|
Server string // server host and port ({IP}:{Port})
|
||||||
EditorChannel string // editor channel (ws://{IP}:7070)
|
StaticServer string // static resources server scheme, host and port (http://{IP}:{Port})
|
||||||
OutputChannel string // output channel (ws://{IP}:7070)
|
EditorChannel string // editor channel (ws://{IP}:{Port})
|
||||||
ShellChannel string // shell channel(ws://{IP}:7070)
|
OutputChannel string // output channel (ws://{IP}:{Port})
|
||||||
SessionChannel string // wide session channel (ws://{IP}:7070)
|
ShellChannel string // shell channel(ws://{IP}:{Port})
|
||||||
|
SessionChannel string // wide session channel (ws://{IP}:{Port})
|
||||||
HTTPSessionMaxAge int // HTTP session max age (in seciond)
|
HTTPSessionMaxAge int // HTTP session max age (in seciond)
|
||||||
StaticResourceVersion string // version of static resources
|
StaticResourceVersion string // version of static resources
|
||||||
MaxProcs int // Go max procs
|
MaxProcs int // Go max procs
|
||||||
RuntimeMode string // runtime mode (dev/prod)
|
RuntimeMode string // runtime mode (dev/prod)
|
||||||
WD string // current working direcitory, ${pwd}
|
WD string // current working direcitory, ${pwd}
|
||||||
Workspace string // path of master workspace
|
|
||||||
Locale string // default locale
|
Locale string // default locale
|
||||||
Users []*User // configurations of users
|
Users []*User // configurations of users
|
||||||
}
|
}
|
||||||
|
@ -95,28 +110,31 @@ func FixedTimeCheckEnv() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkEnv() {
|
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") {
|
if "" == os.Getenv("GOPATH") {
|
||||||
glog.Fatal("Not found $GOPATH, please configure it before running Wide")
|
glog.Fatal("Not found $GOPATH, please configure it before running Wide")
|
||||||
|
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if "" == os.Getenv("GOROOT") {
|
gocode := util.Go.GetExecutableInGOBIN("gocode")
|
||||||
glog.Fatal("Not found $GOROOT, please configure it before running Wide")
|
cmd = exec.Command(gocode, "close")
|
||||||
|
_, err = cmd.Output()
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
gocode := Wide.GetExecutableInGOBIN("gocode")
|
|
||||||
cmd := exec.Command(gocode, "close")
|
|
||||||
_, err := cmd.Output()
|
|
||||||
if nil != err {
|
if nil != err {
|
||||||
event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}
|
event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}
|
||||||
|
|
||||||
glog.Warningf("Not found gocode [%s]", gocode)
|
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")
|
cmd = exec.Command(ide_stub, "version")
|
||||||
_, err = cmd.Output()
|
_, err = cmd.Output()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
|
@ -141,25 +159,14 @@ func FixedTimeSave() {
|
||||||
func (c *conf) GetUserWorkspace(username string) string {
|
func (c *conf) GetUserWorkspace(username string) string {
|
||||||
for _, user := range c.Users {
|
for _, user := range c.Users {
|
||||||
if user.Name == username {
|
if user.Name == username {
|
||||||
ret := strings.Replace(user.Workspace, "{WD}", c.WD, 1)
|
return user.GetWorkspace()
|
||||||
|
|
||||||
return filepath.FromSlash(ret)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWorkspace gets the master workspace path.
|
// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found "goimports".
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
func (c *conf) GetGoFmt(username string) string {
|
func (c *conf) GetGoFmt(username string) string {
|
||||||
for _, user := range c.Users {
|
for _, user := range c.Users {
|
||||||
if user.Name == username {
|
if user.Name == username {
|
||||||
|
@ -167,7 +174,7 @@ func (c *conf) GetGoFmt(username string) string {
|
||||||
case "gofmt":
|
case "gofmt":
|
||||||
return "gofmt"
|
return "gofmt"
|
||||||
case "goimports":
|
case "goimports":
|
||||||
return c.GetExecutableInGOBIN("goimports")
|
return util.Go.GetExecutableInGOBIN("goimports")
|
||||||
default:
|
default:
|
||||||
glog.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
|
glog.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
|
||||||
return "gofmt"
|
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:
|
// Compared to the use of Wide.Workspace, this function will be processed as follows:
|
||||||
// 1. Replace {WD} variable with the actual directory path
|
// 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 {
|
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.
|
// 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
|
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.
|
// Save saves Wide configurations.
|
||||||
func Save() bool {
|
func Save() bool {
|
||||||
// just the Users field are volatile
|
// just the Users field are volatile
|
||||||
|
@ -258,12 +233,12 @@ func Save() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the configurations from wide.json.
|
// Load loads the configurations from wide.json.
|
||||||
func Load() {
|
func Load(confPath, confIP, confPort, confServer, confChannel string, confDocker bool) {
|
||||||
bytes, _ := ioutil.ReadFile("conf/wide.json")
|
bytes, _ := ioutil.ReadFile(confPath)
|
||||||
|
|
||||||
err := json.Unmarshal(bytes, &Wide)
|
err := json.Unmarshal(bytes, &Wide)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error(err)
|
glog.Error("Parses wide.json error: ", err)
|
||||||
|
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
@ -271,6 +246,11 @@ func Load() {
|
||||||
// keep the raw content
|
// keep the raw content
|
||||||
json.Unmarshal(bytes, &rawWide)
|
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()
|
ip, err := util.Net.LocalIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
|
@ -280,17 +260,54 @@ func Load() {
|
||||||
|
|
||||||
glog.V(5).Infof("${ip} [%s]", ip)
|
glog.V(5).Infof("${ip} [%s]", ip)
|
||||||
|
|
||||||
Wide.WD = util.OS.Pwd()
|
if confDocker {
|
||||||
glog.V(5).Infof("${pwd} [%s]", Wide.WD)
|
// TODO: may be we need to do something here
|
||||||
|
}
|
||||||
|
|
||||||
|
if "" != confIP {
|
||||||
|
ip = confIP
|
||||||
|
}
|
||||||
|
|
||||||
Wide.IP = strings.Replace(Wide.IP, "${ip}", ip, 1)
|
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)
|
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.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)
|
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)
|
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)
|
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)
|
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))
|
glog.V(5).Info("Conf: \n" + string(bytes))
|
||||||
|
|
||||||
|
@ -331,7 +348,7 @@ func UpdateCustomizedConf(username string) {
|
||||||
|
|
||||||
wd := util.OS.Pwd()
|
wd := util.OS.Pwd()
|
||||||
dir := filepath.Clean(wd + "/static/user/" + u.Name)
|
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)
|
glog.Error(err)
|
||||||
|
|
||||||
os.Exit(-1)
|
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.
|
// Creates directories if not found on path of workspace.
|
||||||
func initWorkspaceDirs() {
|
func initWorkspaceDirs() {
|
||||||
paths := filepath.SplitList(Wide.GetWorkspace())
|
paths := []string{}
|
||||||
|
|
||||||
for _, user := range Wide.Users {
|
for _, user := range Wide.Users {
|
||||||
paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
|
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.
|
// createDir creates a directory on the path if it not exists.
|
||||||
func createDir(path string) {
|
func createDir(path string) {
|
||||||
if !isExist(path) {
|
if !util.File.IsExist(path) {
|
||||||
if err := os.MkdirAll(path, 0775); nil != err {
|
if err := os.MkdirAll(path, 0775); nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
|
|
||||||
|
@ -393,10 +410,3 @@ func createDir(path string) {
|
||||||
glog.V(7).Infof("Created a directory [%s]", path)
|
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}",
|
"IP": "${ip}",
|
||||||
"Server": "{IP}:7070",
|
"Port": "7070",
|
||||||
"StaticServer": "http://{IP}:7070",
|
"Server": "{IP}:{Port}",
|
||||||
"EditorChannel": "ws://{IP}:7070",
|
"StaticServer": "",
|
||||||
"OutputChannel": "ws://{IP}:7070",
|
"EditorChannel": "ws://{IP}:{Port}",
|
||||||
"ShellChannel": "ws://{IP}:7070",
|
"OutputChannel": "ws://{IP}:{Port}",
|
||||||
"SessionChannel": "ws://{IP}:7070",
|
"ShellChannel": "ws://{IP}:{Port}",
|
||||||
|
"SessionChannel": "ws://{IP}:{Port}",
|
||||||
"HTTPSessionMaxAge": 86400,
|
"HTTPSessionMaxAge": 86400,
|
||||||
"StaticResourceVersion": "201411062300",
|
"StaticResourceVersion": "${time}",
|
||||||
"MaxProcs": 4,
|
"MaxProcs": 4,
|
||||||
"RuntimeMode": "dev",
|
"RuntimeMode": "dev",
|
||||||
"WD": "${pwd}",
|
"WD": "${pwd}",
|
||||||
"Workspace": "{WD}/data/workspace",
|
|
||||||
"Locale": "en_US",
|
"Locale": "en_US",
|
||||||
"Users": [
|
"Users": [
|
||||||
{
|
{
|
||||||
"Name": "admin",
|
"Name": "admin",
|
||||||
"Password": "admin",
|
"Password": "admin",
|
||||||
"Workspace": "{WD}/data/user_workspaces/admin",
|
"Workspace": "${GOPATH}",
|
||||||
"Locale": "en_US",
|
"Locale": "en_US",
|
||||||
"GoFormat": "gofmt",
|
"GoFormat": "gofmt",
|
||||||
"FontFamily": "Helvetica",
|
"FontFamily": "Helvetica",
|
||||||
"FontSize": "13px",
|
"FontSize": "13px",
|
||||||
"Editor": {
|
"Editor": {
|
||||||
"FontFamily": "Consolas, 'Courier New', monospace",
|
"FontFamily": "Consolas, 'Courier New', monospace",
|
||||||
"FontSize": "inherit"
|
"FontSize": "13px",
|
||||||
|
"LineHeight": "17px"
|
||||||
},
|
},
|
||||||
"LatestSessionContent": {
|
"LatestSessionContent": {
|
||||||
"FileTree": [
|
"FileTree": [],
|
||||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src",
|
"Files": [],
|
||||||
"D:\\GoGoGo\\src\\github.com\\b3log\\wide\\data\\user_workspaces\\admin\\src\\time"
|
"CurrentFile": ""
|
||||||
],
|
|
||||||
"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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -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.
|
// Editor manipulations.
|
||||||
package editor
|
package editor
|
||||||
|
|
||||||
|
@ -24,30 +38,30 @@ import (
|
||||||
// WSHandler handles request of creating editor channel.
|
// WSHandler handles request of creating editor channel.
|
||||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if httpSession.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
sid := httpSession.Values["id"].(string)
|
sid := httpSession.Values["id"].(string)
|
||||||
|
|
||||||
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||||
editorChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
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"}
|
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))
|
glog.Infof("Open a new [Editor] with session [%s], %d", sid, len(session.EditorWS))
|
||||||
|
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
for {
|
for {
|
||||||
if err := session.EditorWS[sid].Conn.ReadJSON(&args); err != nil {
|
if err := session.EditorWS[sid].ReadJSON(&args); err != nil {
|
||||||
if err.Error() == "EOF" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err.Error() == "unexpected EOF" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.Error("Editor WS ERROR: " + err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +73,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// glog.Infof("offset: %d", offset)
|
// glog.Infof("offset: %d", offset)
|
||||||
|
|
||||||
gocode := conf.Wide.GetExecutableInGOBIN("gocode")
|
gocode := util.Go.GetExecutableInGOBIN("gocode")
|
||||||
argv := []string{"-f=json", "autocomplete", strconv.Itoa(offset)}
|
argv := []string{"-f=json", "autocomplete", strconv.Itoa(offset)}
|
||||||
|
|
||||||
var output bytes.Buffer
|
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"}
|
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())
|
glog.Error("Editor WS ERROR: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -94,6 +108,11 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if session.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := session.Values["username"].(string)
|
username := session.Values["username"].(string)
|
||||||
|
|
||||||
path := args["path"].(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)
|
glog.V(5).Infof("gocode set lib-path %s", libPath)
|
||||||
|
|
||||||
// FIXME: using gocode set lib-path has some issues while accrossing workspaces
|
// 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}
|
argv := []string{"set", "lib-path", libPath}
|
||||||
exec.Command(gocode, argv...).Run()
|
exec.Command(gocode, argv...).Run()
|
||||||
|
|
||||||
|
@ -205,7 +224,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// glog.Infof("offset [%d]", offset)
|
// 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", "."}
|
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-info", "."}
|
||||||
cmd := exec.Command(ide_stub, argv...)
|
cmd := exec.Command(ide_stub, argv...)
|
||||||
cmd.Dir = curDir
|
cmd.Dir = curDir
|
||||||
|
@ -236,6 +255,11 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
|
||||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if session.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := session.Values["username"].(string)
|
username := session.Values["username"].(string)
|
||||||
|
|
||||||
var args map[string]interface{}
|
var args map[string]interface{}
|
||||||
|
@ -276,7 +300,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// glog.Infof("offset [%d]", offset)
|
// 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", "."}
|
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-def", "."}
|
||||||
cmd := exec.Command(ide_stub, argv...)
|
cmd := exec.Command(ide_stub, argv...)
|
||||||
cmd.Dir = curDir
|
cmd.Dir = curDir
|
||||||
|
@ -315,6 +339,11 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
|
||||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if session.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := session.Values["username"].(string)
|
username := session.Values["username"].(string)
|
||||||
|
|
||||||
var args map[string]interface{}
|
var args map[string]interface{}
|
||||||
|
@ -355,7 +384,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
offset := getCursorOffset(code, line, ch)
|
offset := getCursorOffset(code, line, ch)
|
||||||
// glog.Infof("offset [%d]", offset)
|
// 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", "."}
|
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-use", "."}
|
||||||
cmd := exec.Command(ide_stub, argv...)
|
cmd := exec.Command(ide_stub, argv...)
|
||||||
cmd.Dir = curDir
|
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
|
package editor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -5,8 +19,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/b3log/wide/conf"
|
"github.com/b3log/wide/conf"
|
||||||
"github.com/b3log/wide/session"
|
"github.com/b3log/wide/session"
|
||||||
|
@ -24,6 +36,11 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
|
||||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if session.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := session.Values["username"].(string)
|
username := session.Values["username"].(string)
|
||||||
|
|
||||||
var args map[string]interface{}
|
var args map[string]interface{}
|
||||||
|
@ -37,8 +54,7 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
filePath := args["file"].(string)
|
filePath := args["file"].(string)
|
||||||
|
|
||||||
apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg"
|
if util.Go.IsAPI(filePath) {
|
||||||
if strings.HasPrefix(filePath, apiPath) { // if it is Go API source code
|
|
||||||
// ignore it
|
// ignore it
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -70,7 +86,9 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
bytes, _ := cmd.Output()
|
bytes, _ := cmd.Output()
|
||||||
output := string(bytes)
|
output := string(bytes)
|
||||||
if "" == output {
|
if "" == output {
|
||||||
data["succ"] = false
|
// format error, returns the original content
|
||||||
|
data["succ"] = true
|
||||||
|
data["code"] = code
|
||||||
|
|
||||||
return
|
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.
|
// Event manipulations.
|
||||||
package event
|
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.
|
// File tree manipulations.
|
||||||
package file
|
package file
|
||||||
|
|
||||||
|
@ -7,7 +21,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -21,7 +34,6 @@ import (
|
||||||
// File node, used to construct the file tree.
|
// File node, used to construct the file tree.
|
||||||
type FileNode struct {
|
type FileNode struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Title string `json:"title"`
|
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
IconSkin string `json:"iconSkin"` // Value should be end with a space
|
IconSkin string `json:"iconSkin"` // Value should be end with a space
|
||||||
Type string `json:"type"` // "f": file, "d": directory
|
Type string `json:"type"` // "f": file, "d": directory
|
||||||
|
@ -39,28 +51,49 @@ type Snippet struct {
|
||||||
Contents []string `json:"contents"` // lines nearby
|
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.
|
// GetFiles handles request of constructing user workspace file tree.
|
||||||
//
|
//
|
||||||
// The Go API source code package ($GOROOT/src/pkg) also as a child node,
|
// The Go API source code package also as a child node,
|
||||||
// so that users can easily view the Go API source code.
|
// so that users can easily view the Go API source code in file tree.
|
||||||
func GetFiles(w http.ResponseWriter, r *http.Request) {
|
func GetFiles(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
|
||||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if session.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := session.Values["username"].(string)
|
username := session.Values["username"].(string)
|
||||||
|
|
||||||
userWorkspace := conf.Wide.GetUserWorkspace(username)
|
userWorkspace := conf.Wide.GetUserWorkspace(username)
|
||||||
workspaces := filepath.SplitList(userWorkspace)
|
workspaces := filepath.SplitList(userWorkspace)
|
||||||
|
|
||||||
root := FileNode{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", FileNodes: []*FileNode{}}
|
root := FileNode{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", FileNodes: []*FileNode{}}
|
||||||
|
|
||||||
|
if nil == apiNode { // lazy init
|
||||||
|
initAPINode()
|
||||||
|
}
|
||||||
|
|
||||||
// workspace node process
|
// workspace node process
|
||||||
for _, workspace := range workspaces {
|
for _, workspace := range workspaces {
|
||||||
workspacePath := workspace + conf.PathSeparator + "src"
|
workspacePath := workspace + conf.PathSeparator + "src"
|
||||||
|
|
||||||
workspaceNode := FileNode{Name: workspace[strings.LastIndex(workspace, conf.PathSeparator)+1:],
|
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{}}
|
Creatable: true, Removable: false, FileNodes: []*FileNode{}}
|
||||||
|
|
||||||
walk(workspacePath, &workspaceNode, true, true)
|
walk(workspacePath, &workspaceNode, true, true)
|
||||||
|
@ -69,28 +102,31 @@ func GetFiles(w http.ResponseWriter, r *http.Request) {
|
||||||
root.FileNodes = append(root.FileNodes, &workspaceNode)
|
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
|
// add Go API node
|
||||||
root.FileNodes = append(root.FileNodes, &apiNode)
|
root.FileNodes = append(root.FileNodes, apiNode)
|
||||||
|
|
||||||
data["root"] = root
|
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.
|
// GetFile handles request of opening file by editor.
|
||||||
func GetFile(w http.ResponseWriter, r *http.Request) {
|
func GetFile(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
|
@ -106,25 +142,40 @@ func GetFile(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
path := args["path"].(string)
|
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)
|
buf, _ := ioutil.ReadFile(path)
|
||||||
|
|
||||||
extension := filepath.Ext(path)
|
extension := filepath.Ext(path)
|
||||||
|
|
||||||
if isImg(extension) {
|
if util.File.IsImg(extension) {
|
||||||
// image file will be open in a browser tab
|
// image file will be open in a browser tab
|
||||||
|
|
||||||
data["mode"] = "img"
|
data["mode"] = "img"
|
||||||
|
|
||||||
path2 := strings.Replace(path, "\\", "/", -1)
|
user := GetUsre(path)
|
||||||
idx := strings.Index(path2, "/data/user_workspaces")
|
if nil == user {
|
||||||
data["path"] = path2[idx:]
|
glog.Warningf("The path [%s] has no owner")
|
||||||
|
data["path"] = ""
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data["path"] = "/workspace/" + user.Name + "/" + strings.Replace(path, user.GetWorkspace(), "", 1)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
content := string(buf)
|
content := string(buf)
|
||||||
|
|
||||||
if isBinary(content) {
|
if util.File.IsBinary(content) {
|
||||||
data["succ"] = false
|
data["succ"] = false
|
||||||
data["msg"] = "Can't open a binary file :("
|
data["msg"] = "Can't open a binary file :("
|
||||||
} else {
|
} else {
|
||||||
|
@ -262,10 +313,70 @@ func RenameFile(w http.ResponseWriter, r *http.Request) {
|
||||||
data["succ"] = false
|
data["succ"] = false
|
||||||
|
|
||||||
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
|
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.
|
// SearchText handles request of searching files under the specified directory with the specified keyword.
|
||||||
func SearchText(w http.ResponseWriter, r *http.Request) {
|
func SearchText(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
|
@ -298,7 +409,7 @@ func walk(path string, node *FileNode, creatable, removable bool) {
|
||||||
|
|
||||||
fio, _ := os.Lstat(fpath)
|
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)
|
node.FileNodes = append(node.FileNodes, &child)
|
||||||
|
|
||||||
if nil == fio {
|
if nil == fio {
|
||||||
|
@ -340,7 +451,14 @@ func listFiles(dirname string) []string {
|
||||||
|
|
||||||
// sort: directories in front of files
|
// sort: directories in front of files
|
||||||
for _, name := range names {
|
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() {
|
if fio.IsDir() {
|
||||||
// exclude the .git direcitory
|
// exclude the .git direcitory
|
||||||
|
@ -361,7 +479,7 @@ func listFiles(dirname string) []string {
|
||||||
//
|
//
|
||||||
// Refers to the zTree document for CSS class names.
|
// Refers to the zTree document for CSS class names.
|
||||||
func getIconSkin(filenameExtension string) string {
|
func getIconSkin(filenameExtension string) string {
|
||||||
if isImg(filenameExtension) {
|
if util.File.IsImg(filenameExtension) {
|
||||||
return "ico-ztree-img "
|
return "ico-ztree-img "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,7 +544,7 @@ func getEditorMode(filenameExtension string) string {
|
||||||
func createFile(path, fileType string) bool {
|
func createFile(path, fileType string) bool {
|
||||||
switch fileType {
|
switch fileType {
|
||||||
case "f":
|
case "f":
|
||||||
file, err := os.OpenFile(path, os.O_CREATE, 0664)
|
file, err := os.OpenFile(path, os.O_CREATE, 0775)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
|
|
||||||
|
@ -465,7 +583,7 @@ func removeFile(path string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.Infof("Removed [%s]", path)
|
glog.V(5).Infof("Removed [%s]", path)
|
||||||
|
|
||||||
return true
|
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.
|
// renameFile renames (moves) a file from the specified old path to the specified new path.
|
||||||
func renameFile(oldPath, newPath string) bool {
|
func renameFile(oldPath, newPath string) bool {
|
||||||
if err := os.Rename(oldPath, newPath); nil != err {
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.Infof("Renamed [%s] to [%s]", oldPath, newPath)
|
glog.V(5).Infof("Renamed [%s] to [%s]", oldPath, newPath)
|
||||||
|
|
||||||
return true
|
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 {
|
func search(dir, extension, text string, snippets []*Snippet) []*Snippet {
|
||||||
if !strings.HasSuffix(dir, conf.PathSeparator) {
|
if !strings.HasSuffix(dir, conf.PathSeparator) {
|
||||||
dir += conf.PathSeparator
|
dir += conf.PathSeparator
|
||||||
|
@ -528,7 +698,7 @@ func searchInFile(path string, text string) []*Snippet {
|
||||||
}
|
}
|
||||||
|
|
||||||
content := string(bytes)
|
content := string(bytes)
|
||||||
if isBinary(content) {
|
if util.File.IsBinary(content) {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,25 +717,13 @@ func searchInFile(path string, text string) []*Snippet {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// isBinary determines whether the specified content is a binary file content.
|
// GetUsre gets the user the specified path belongs to. Returns nil if not found.
|
||||||
func isBinary(content string) bool {
|
func GetUsre(path string) *conf.User {
|
||||||
for _, b := range content {
|
for _, user := range conf.Wide.Users {
|
||||||
if 0 == b {
|
if strings.HasPrefix(path, user.GetWorkspace()) {
|
||||||
return true
|
return user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"create_dir": "Create Dir",
|
"create_dir": "Create Dir",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
"rename": "Rename",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"exit": "Exit",
|
"exit": "Exit",
|
||||||
"close_all_files": "Close All",
|
"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_3": "Not found [ide_stub], thereby [Jump to Decl], [Find Usages] will not work",
|
||||||
"notification_4": "Server Internal Error",
|
"notification_4": "Server Internal Error",
|
||||||
"goto_line": "Goto Line",
|
"goto_line": "Goto Line",
|
||||||
|
"goto_file": "Goto File",
|
||||||
"go": "Go",
|
"go": "Go",
|
||||||
"tip": "Tip",
|
"tip": "Tip",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
|
@ -73,8 +75,10 @@
|
||||||
"show_expr_info": "Show Expression Info",
|
"show_expr_info": "Show Expression Info",
|
||||||
"find_usages": "Find Usages",
|
"find_usages": "Find Usages",
|
||||||
"delete_line": "Delete Line",
|
"delete_line": "Delete Line",
|
||||||
"copy_line_up": "Copy Line Up",
|
"copy_lines_up": "Copy Lines Up",
|
||||||
"copy_line_down": "Copy Line Down",
|
"copy_lines_down": "Copy Lines Down",
|
||||||
|
"move_lines_up": "Move Lines Up",
|
||||||
|
"move_lines_down": "Move Lines Down",
|
||||||
"save_editor_file": "Save File",
|
"save_editor_file": "Save File",
|
||||||
"save_all_editors_files": "Save All",
|
"save_all_editors_files": "Save All",
|
||||||
"close_editor": "Close File",
|
"close_editor": "Close File",
|
||||||
|
@ -118,5 +122,18 @@
|
||||||
"discard": "Discard",
|
"discard": "Discard",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"close_other": "Close Other",
|
"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": "作成",
|
||||||
"create_dir": "新規ディレクトリ",
|
"create_dir": "新規ディレクトリ",
|
||||||
"delete": "削除",
|
"delete": "削除",
|
||||||
|
"rename": "名前の変更",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"exit": "終了",
|
"exit": "終了",
|
||||||
"close_all_files": "全てのファイルを閉じる",
|
"close_all_files": "全てのファイルを閉じる",
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
"notification_3": "[ide_stub] が見つかりません。[Jump to Decl]、[Find Usages] は動作しません。",
|
"notification_3": "[ide_stub] が見つかりません。[Jump to Decl]、[Find Usages] は動作しません。",
|
||||||
"notification_4": "内部サーバーエラー",
|
"notification_4": "内部サーバーエラー",
|
||||||
"goto_line": "指定行にジャンプ",
|
"goto_line": "指定行にジャンプ",
|
||||||
|
"goto_file": "ファイルをオープンする",
|
||||||
"go": "Go",
|
"go": "Go",
|
||||||
"tip": "ヒント",
|
"tip": "ヒント",
|
||||||
"confirm": "確認",
|
"confirm": "確認",
|
||||||
|
@ -73,8 +75,10 @@
|
||||||
"show_expr_info": "式の情報を表示",
|
"show_expr_info": "式の情報を表示",
|
||||||
"find_usages": "使用方法を検索する",
|
"find_usages": "使用方法を検索する",
|
||||||
"delete_line": "行を削除",
|
"delete_line": "行を削除",
|
||||||
"copy_line_up": "前行にコピー",
|
"copy_lines_up": "フロントへのコピー",
|
||||||
"copy_line_down": "次行にコピー",
|
"copy_lines_down": "一番下にコピー",
|
||||||
|
"move_lines_up": "前面に移動します",
|
||||||
|
"move_lines_down": "以下に移動",
|
||||||
"save_editor_file": "保存",
|
"save_editor_file": "保存",
|
||||||
"save_all_editors_files": "全てを保存",
|
"save_all_editors_files": "全てを保存",
|
||||||
"close_editor": "エディタを閉じる",
|
"close_editor": "エディタを閉じる",
|
||||||
|
@ -118,5 +122,18 @@
|
||||||
"discard": "あきらめる",
|
"discard": "あきらめる",
|
||||||
"close": "クローズ",
|
"close": "クローズ",
|
||||||
"close_other": "閉じるその他",
|
"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.
|
// Internationalization manipulations.
|
||||||
package i18n
|
package i18n
|
||||||
|
|
||||||
|
@ -67,3 +81,14 @@ func Get(locale, key string) interface{} {
|
||||||
func GetAll(locale string) map[string]interface{} {
|
func GetAll(locale string) map[string]interface{} {
|
||||||
return Locales[locale].Langs
|
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": "创建",
|
||||||
"create_dir": "创建目录",
|
"create_dir": "创建目录",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
|
"rename": "重命名",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"exit": "退出",
|
"exit": "退出",
|
||||||
"close_all_files": "关闭所有文件",
|
"close_all_files": "关闭所有文件",
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
"notification_3": "没有检查到 ide_stub,这将会导致 [跳转到声明]、[查找使用] 失效",
|
"notification_3": "没有检查到 ide_stub,这将会导致 [跳转到声明]、[查找使用] 失效",
|
||||||
"notification_4": "服务器内部错误",
|
"notification_4": "服务器内部错误",
|
||||||
"goto_line": "跳转到行",
|
"goto_line": "跳转到行",
|
||||||
|
"goto_file": "打开文件",
|
||||||
"go": "跳转",
|
"go": "跳转",
|
||||||
"tip": "提示",
|
"tip": "提示",
|
||||||
"confirm": "确定",
|
"confirm": "确定",
|
||||||
|
@ -73,8 +75,10 @@
|
||||||
"show_expr_info": "查看表达式信息",
|
"show_expr_info": "查看表达式信息",
|
||||||
"find_usages": "查找使用",
|
"find_usages": "查找使用",
|
||||||
"delete_line": "删除当前行",
|
"delete_line": "删除当前行",
|
||||||
"copy_line_up": "复制当前行到上一行",
|
"copy_lines_up": "复制到上方",
|
||||||
"copy_line_down": "复制当前行到下一行",
|
"copy_lines_down": "复制到下方",
|
||||||
|
"move_lines_up": "移动到上方",
|
||||||
|
"move_lines_down": "移动到下方",
|
||||||
"save_editor_file": "保存当前编辑器文件",
|
"save_editor_file": "保存当前编辑器文件",
|
||||||
"save_all_editors_files": "保存所有编辑器文件",
|
"save_all_editors_files": "保存所有编辑器文件",
|
||||||
"close_editor": "关闭当前编辑器",
|
"close_editor": "关闭当前编辑器",
|
||||||
|
@ -118,5 +122,18 @@
|
||||||
"discard": "放弃",
|
"discard": "放弃",
|
||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
"close_other": "关闭其它",
|
"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": "建立",
|
||||||
"create_dir": "建立目錄",
|
"create_dir": "建立目錄",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
|
"rename": "重命名",
|
||||||
"save": "儲存",
|
"save": "儲存",
|
||||||
"exit": "退出",
|
"exit": "退出",
|
||||||
"close_all_files": "關閉所有文件",
|
"close_all_files": "關閉所有文件",
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
"notification_3": "没有檢查到 ide_stub,這將會導致 [跳轉到聲明]、[查找使用] 失效",
|
"notification_3": "没有檢查到 ide_stub,這將會導致 [跳轉到聲明]、[查找使用] 失效",
|
||||||
"notification_4": "服務器內部錯誤",
|
"notification_4": "服務器內部錯誤",
|
||||||
"goto_line": "跳轉到行",
|
"goto_line": "跳轉到行",
|
||||||
|
"goto_file": "打開文件",
|
||||||
"go": "跳到",
|
"go": "跳到",
|
||||||
"tip": "提示",
|
"tip": "提示",
|
||||||
"confirm": "確定",
|
"confirm": "確定",
|
||||||
|
@ -73,8 +75,10 @@
|
||||||
"show_expr_info": "查看表達式信息",
|
"show_expr_info": "查看表達式信息",
|
||||||
"find_usages": "尋找使用",
|
"find_usages": "尋找使用",
|
||||||
"delete_line": "删除當前行",
|
"delete_line": "删除當前行",
|
||||||
"copy_line_up": "插入當前行到上一行",
|
"copy_lines_up": "複製到上方",
|
||||||
"copy_line_down": "插入當前行到下一行",
|
"copy_lines_down": "複製到下方",
|
||||||
|
"move_lines_up": "移動到上方",
|
||||||
|
"move_lines_down": "移動到下方",
|
||||||
"save_editor_file": "保存當前編輯器文件",
|
"save_editor_file": "保存當前編輯器文件",
|
||||||
"save_all_editors_files": "保存所有編輯器文件",
|
"save_all_editors_files": "保存所有編輯器文件",
|
||||||
"close_editor": "關閉當前編輯器",
|
"close_editor": "關閉當前編輯器",
|
||||||
|
@ -118,5 +122,18 @@
|
||||||
"discard": "放棄",
|
"discard": "放棄",
|
||||||
"close": "關閉",
|
"close": "關閉",
|
||||||
"close_other": "關閉其它",
|
"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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -6,8 +20,10 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/b3log/wide/conf"
|
"github.com/b3log/wide/conf"
|
||||||
|
@ -25,26 +41,46 @@ import (
|
||||||
|
|
||||||
// The only one init function in Wide.
|
// The only one init function in Wide.
|
||||||
func init() {
|
func init() {
|
||||||
// TODO: args
|
confPath := flag.String("conf", "conf/wide.json", "path of wide.json")
|
||||||
flag.Set("logtostderr", "true")
|
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.Set("v", "3")
|
||||||
|
|
||||||
flag.Parse()
|
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()
|
i18n.Load()
|
||||||
|
|
||||||
event.Load()
|
event.Load()
|
||||||
|
|
||||||
conf.Load()
|
conf.Load(*confPath, *confIP, *confPort, *confServer, *confChannel, *confDocker)
|
||||||
|
|
||||||
conf.FixedTimeCheckEnv()
|
conf.FixedTimeCheckEnv()
|
||||||
conf.FixedTimeSave()
|
conf.FixedTimeSave()
|
||||||
|
|
||||||
session.FixedTimeRelease()
|
session.FixedTimeRelease()
|
||||||
|
|
||||||
|
if *confStat {
|
||||||
|
session.FixedTimeReport()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// indexHandler handles request of Wide index.
|
// indexHandler handles request of Wide index.
|
||||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
|
||||||
if httpSession.IsNew {
|
if httpSession.IsNew {
|
||||||
http.Redirect(w, r, "/login", http.StatusFound)
|
http.Redirect(w, r, "/login", http.StatusFound)
|
||||||
|
|
||||||
|
@ -72,10 +108,9 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
locale := user.Locale
|
locale := user.Locale
|
||||||
|
|
||||||
wideSessions := session.WideSessions.GetByUsername(username)
|
wideSessions := session.WideSessions.GetByUsername(username)
|
||||||
userConf := conf.Wide.GetUser(username)
|
|
||||||
|
|
||||||
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
|
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}
|
"pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer}
|
||||||
|
|
||||||
glog.V(3).Infof("User [%s] has [%d] sessions", username, len(wideSessions))
|
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.
|
// startHandler handles request of start page.
|
||||||
func startHandler(w http.ResponseWriter, r *http.Request) {
|
func startHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
|
||||||
if httpSession.IsNew {
|
if httpSession.IsNew {
|
||||||
http.Redirect(w, r, "/login", http.StatusFound)
|
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.
|
// keyboardShortcutsHandler handles request of keyboard shortcuts page.
|
||||||
func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
|
func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
|
||||||
if httpSession.IsNew {
|
if httpSession.IsNew {
|
||||||
http.Redirect(w, r, "/login", http.StatusFound)
|
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.
|
// aboutHandle handles request of about page.
|
||||||
func aboutHandler(w http.ResponseWriter, r *http.Request) {
|
func aboutHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
|
||||||
if httpSession.IsNew {
|
if httpSession.IsNew {
|
||||||
http.Redirect(w, r, "/login", http.StatusFound)
|
http.Redirect(w, r, "/login", http.StatusFound)
|
||||||
|
|
||||||
|
@ -217,7 +249,10 @@ func main() {
|
||||||
serveSingle("/favicon.ico", "./static/favicon.ico")
|
serveSingle("/favicon.ico", "./static/favicon.ico")
|
||||||
|
|
||||||
// workspaces
|
// 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
|
// session
|
||||||
http.HandleFunc("/session/ws", handlerWrapper(session.WSHandler))
|
http.HandleFunc("/session/ws", handlerWrapper(session.WSHandler))
|
||||||
|
@ -234,12 +269,18 @@ func main() {
|
||||||
|
|
||||||
// file tree
|
// file tree
|
||||||
http.HandleFunc("/files", handlerWrapper(file.GetFiles))
|
http.HandleFunc("/files", handlerWrapper(file.GetFiles))
|
||||||
|
http.HandleFunc("/file/refresh", handlerWrapper(file.RefreshDirectory))
|
||||||
http.HandleFunc("/file", handlerWrapper(file.GetFile))
|
http.HandleFunc("/file", handlerWrapper(file.GetFile))
|
||||||
http.HandleFunc("/file/save", handlerWrapper(file.SaveFile))
|
http.HandleFunc("/file/save", handlerWrapper(file.SaveFile))
|
||||||
http.HandleFunc("/file/new", handlerWrapper(file.NewFile))
|
http.HandleFunc("/file/new", handlerWrapper(file.NewFile))
|
||||||
http.HandleFunc("/file/remove", handlerWrapper(file.RemoveFile))
|
http.HandleFunc("/file/remove", handlerWrapper(file.RemoveFile))
|
||||||
http.HandleFunc("/file/rename", handlerWrapper(file.RenameFile))
|
http.HandleFunc("/file/rename", handlerWrapper(file.RenameFile))
|
||||||
http.HandleFunc("/file/search/text", handlerWrapper(file.SearchText))
|
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
|
// editor
|
||||||
http.HandleFunc("/editor/ws", handlerWrapper(editor.WSHandler))
|
http.HandleFunc("/editor/ws", handlerWrapper(editor.WSHandler))
|
||||||
|
@ -260,8 +301,9 @@ func main() {
|
||||||
http.HandleFunc("/login", handlerWrapper(session.LoginHandler))
|
http.HandleFunc("/login", handlerWrapper(session.LoginHandler))
|
||||||
http.HandleFunc("/logout", handlerWrapper(session.LogoutHandler))
|
http.HandleFunc("/logout", handlerWrapper(session.LogoutHandler))
|
||||||
http.HandleFunc("/signup", handlerWrapper(session.SignUpUser))
|
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)
|
err := http.ListenAndServe(conf.Wide.Server, nil)
|
||||||
if err != 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.
|
// Notification manipulations.
|
||||||
package notification
|
package notification
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"strconv"
|
|
||||||
"github.com/b3log/wide/conf"
|
"github.com/b3log/wide/conf"
|
||||||
"github.com/b3log/wide/event"
|
"github.com/b3log/wide/event"
|
||||||
"github.com/b3log/wide/i18n"
|
"github.com/b3log/wide/i18n"
|
||||||
|
@ -65,7 +79,7 @@ func event2Notification(e *event.Event) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wsChannel.Conn.WriteJSON(notification)
|
wsChannel.WriteJSON(notification)
|
||||||
|
|
||||||
wsChannel.Refresh()
|
wsChannel.Refresh()
|
||||||
}
|
}
|
||||||
|
@ -83,6 +97,12 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||||
wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
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
|
session.NotificationWS[sid] = &wsChan
|
||||||
|
|
||||||
glog.V(4).Infof("Open a new [Notification] with session [%s], %d", sid, len(session.NotificationWS))
|
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{}{}
|
input := map[string]interface{}{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := wsChan.Conn.ReadJSON(&input); err != nil {
|
if err := wsChan.ReadJSON(&input); err != nil {
|
||||||
if err.Error() == "EOF" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err.Error() == "unexpected EOF" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.Error("Notification WS ERROR: " + err.Error())
|
|
||||||
|
|
||||||
return
|
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.
|
// Build, run and go tool manipulations.
|
||||||
package output
|
package output
|
||||||
|
|
||||||
|
@ -44,10 +58,13 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||||
wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
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"}
|
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))
|
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["cmd"] = "run-done"
|
||||||
channelRet["output"] = ""
|
channelRet["output"] = ""
|
||||||
|
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -127,13 +144,13 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.Recover()
|
defer util.Recover()
|
||||||
defer cmd.Wait()
|
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
|
// push once for front-end to get the 'run' state and pid
|
||||||
if nil != wsChannel {
|
if nil != wsChannel {
|
||||||
channelRet["cmd"] = "run"
|
channelRet["cmd"] = "run"
|
||||||
channelRet["output"] = ""
|
channelRet["output"] = ""
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -155,12 +172,12 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// remove the exited process from user process set
|
// remove the exited process from user process set
|
||||||
processes.remove(wSession, cmd.Process)
|
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 {
|
if nil != wsChannel {
|
||||||
channelRet["cmd"] = "run-done"
|
channelRet["cmd"] = "run-done"
|
||||||
channelRet["output"] = "<pre>" + buf + "</pre>"
|
channelRet["output"] = buf
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
break
|
break
|
||||||
|
@ -173,8 +190,8 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
} else {
|
} else {
|
||||||
if nil != wsChannel {
|
if nil != wsChannel {
|
||||||
channelRet["cmd"] = "run"
|
channelRet["cmd"] = "run"
|
||||||
channelRet["output"] = "<pre>" + buf + "</pre>"
|
channelRet["output"] = buf
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
break
|
break
|
||||||
|
@ -195,14 +212,14 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// remove the exited process from user process set
|
// remove the exited process from user process set
|
||||||
processes.remove(wSession, cmd.Process)
|
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] {
|
if nil != session.OutputWS[sid] {
|
||||||
wsChannel := session.OutputWS[sid]
|
wsChannel := session.OutputWS[sid]
|
||||||
|
|
||||||
channelRet["cmd"] = "run-done"
|
channelRet["cmd"] = "run-done"
|
||||||
channelRet["output"] = "<pre>" + buf + "</pre>"
|
channelRet["output"] = buf
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
break
|
break
|
||||||
|
@ -217,8 +234,8 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
wsChannel := session.OutputWS[sid]
|
wsChannel := session.OutputWS[sid]
|
||||||
|
|
||||||
channelRet["cmd"] = "run"
|
channelRet["cmd"] = "run"
|
||||||
channelRet["output"] = "<pre>" + buf + "</pre>"
|
channelRet["output"] = buf
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
break
|
break
|
||||||
|
@ -237,6 +254,11 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if httpSession.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := httpSession.Values["username"].(string)
|
username := httpSession.Values["username"].(string)
|
||||||
locale := conf.Wide.GetUser(username).Locale
|
locale := conf.Wide.GetUser(username).Locale
|
||||||
|
|
||||||
|
@ -319,7 +341,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
wsChannel := session.OutputWS[sid]
|
wsChannel := session.OutputWS[sid]
|
||||||
|
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -341,7 +363,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.Recover()
|
defer util.Recover()
|
||||||
defer cmd.Wait()
|
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
|
// read all
|
||||||
buf, _ := ioutil.ReadAll(reader)
|
buf, _ := ioutil.ReadAll(reader)
|
||||||
|
@ -419,10 +441,10 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if nil != session.OutputWS[sid] {
|
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]
|
wsChannel := session.OutputWS[sid]
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -439,6 +461,11 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if httpSession.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := httpSession.Values["username"].(string)
|
username := httpSession.Values["username"].(string)
|
||||||
locale := conf.Wide.GetUser(username).Locale
|
locale := conf.Wide.GetUser(username).Locale
|
||||||
|
|
||||||
|
@ -491,7 +518,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
wsChannel := session.OutputWS[sid]
|
wsChannel := session.OutputWS[sid]
|
||||||
|
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -512,7 +539,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
go func(runningId int) {
|
go func(runningId int) {
|
||||||
defer util.Recover()
|
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 := map[string]interface{}{}
|
||||||
channelRet["cmd"] = "go test"
|
channelRet["cmd"] = "go test"
|
||||||
|
@ -524,11 +551,11 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
|
|
||||||
if !cmd.ProcessState.Success() {
|
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)
|
channelRet["output"] = "<span class='test-error'>" + i18n.Get(locale, "test-error").(string) + "</span>\n" + string(buf)
|
||||||
} else {
|
} 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)
|
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] {
|
if nil != session.OutputWS[sid] {
|
||||||
wsChannel := session.OutputWS[sid]
|
wsChannel := session.OutputWS[sid]
|
||||||
|
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -552,6 +579,11 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if httpSession.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := httpSession.Values["username"].(string)
|
username := httpSession.Values["username"].(string)
|
||||||
locale := conf.Wide.GetUser(username).Locale
|
locale := conf.Wide.GetUser(username).Locale
|
||||||
|
|
||||||
|
@ -606,7 +638,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
wsChannel := session.OutputWS[sid]
|
wsChannel := session.OutputWS[sid]
|
||||||
|
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -628,7 +660,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.Recover()
|
defer util.Recover()
|
||||||
defer cmd.Wait()
|
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
|
// read all
|
||||||
buf, _ := ioutil.ReadAll(reader)
|
buf, _ := ioutil.ReadAll(reader)
|
||||||
|
@ -692,10 +724,10 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if nil != session.OutputWS[sid] {
|
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]
|
wsChannel := session.OutputWS[sid]
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -712,6 +744,11 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if httpSession.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := httpSession.Values["username"].(string)
|
username := httpSession.Values["username"].(string)
|
||||||
locale := conf.Wide.GetUser(username).Locale
|
locale := conf.Wide.GetUser(username).Locale
|
||||||
|
|
||||||
|
@ -764,7 +801,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
wsChannel := session.OutputWS[sid]
|
wsChannel := session.OutputWS[sid]
|
||||||
|
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -786,7 +823,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
defer util.Recover()
|
defer util.Recover()
|
||||||
defer cmd.Wait()
|
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 := map[string]interface{}{}
|
||||||
channelRet["cmd"] = "go get"
|
channelRet["cmd"] = "go get"
|
||||||
|
@ -795,11 +832,11 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
buf, _ := ioutil.ReadAll(reader)
|
buf, _ := ioutil.ReadAll(reader)
|
||||||
|
|
||||||
if 0 != len(buf) {
|
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)
|
channelRet["output"] = "<span class='get-error'>" + i18n.Get(locale, "get-error").(string) + "</span>\n" + string(buf)
|
||||||
} else {
|
} 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"
|
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] {
|
if nil != session.OutputWS[sid] {
|
||||||
wsChannel := session.OutputWS[sid]
|
wsChannel := session.OutputWS[sid]
|
||||||
|
|
||||||
err := wsChannel.Conn.WriteJSON(&channelRet)
|
err := wsChannel.WriteJSON(&channelRet)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -846,10 +883,9 @@ func StopHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func setCmdEnv(cmd *exec.Cmd, username string) {
|
func setCmdEnv(cmd *exec.Cmd, username string) {
|
||||||
userWorkspace := conf.Wide.GetUserWorkspace(username)
|
userWorkspace := conf.Wide.GetUserWorkspace(username)
|
||||||
masterWorkspace := conf.Wide.GetWorkspace()
|
|
||||||
|
|
||||||
cmd.Env = append(cmd.Env,
|
cmd.Env = append(cmd.Env,
|
||||||
"GOPATH="+userWorkspace+conf.PathListSeparator+masterWorkspace,
|
"GOPATH="+userWorkspace,
|
||||||
"GOOS="+runtime.GOOS,
|
"GOOS="+runtime.GOOS,
|
||||||
"GOARCH="+runtime.GOARCH,
|
"GOARCH="+runtime.GOARCH,
|
||||||
"GOROOT="+runtime.GOROOT(),
|
"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
|
package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -33,7 +47,7 @@ func (procs *procs) add(wSession *session.WideSession, proc *os.Process) {
|
||||||
// bind process with wide session
|
// bind process with wide session
|
||||||
wSession.SetProcesses(userProcesses)
|
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.
|
// 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
|
// bind process with wide session
|
||||||
wSession.SetProcesses(newProcesses)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
@ -83,7 +97,7 @@ func (procs *procs) kill(wSession *session.WideSession, pid int) {
|
||||||
// bind process with wide session
|
// bind process with wide session
|
||||||
wSession.SetProcesses(newProcesses)
|
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
|
return
|
||||||
|
|
2
pkg.sh
2
pkg.sh
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
ver=$1
|
ver=$1
|
||||||
target=$2
|
target=$2
|
||||||
list="conf data doc i18n static views README.md LICENSE"
|
list="conf doc i18n static views README.md LICENSE"
|
||||||
|
|
||||||
mkdir -p ${target}
|
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.
|
// Session manipulations.
|
||||||
//
|
//
|
||||||
// Wide server side needs maintain two kinds of sessions:
|
// Wide server side needs maintain two kinds of sessions:
|
||||||
|
@ -9,9 +23,11 @@
|
||||||
package session
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -79,7 +95,7 @@ func FixedTimeRelease() {
|
||||||
|
|
||||||
for _, s := range WideSessions {
|
for _, s := range WideSessions {
|
||||||
if s.Updated.Before(threshold) {
|
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)
|
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.
|
// WSHandler handles request of creating session channel.
|
||||||
//
|
//
|
||||||
// When a channel closed, releases all resources associated with it.
|
// 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.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
|
||||||
httpSession.Save(r, w)
|
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)
|
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||||
wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
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"}
|
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))
|
glog.V(4).Infof("Open a new [Session Channel] with session [%s], %d", sid, len(SessionWS))
|
||||||
|
|
||||||
input := map[string]interface{}{}
|
input := map[string]interface{}{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := wsChan.Conn.ReadJSON(&input); err != nil {
|
if err := wsChan.ReadJSON(&input); err != nil {
|
||||||
glog.V(3).Infof("[Session Channel] of session [%s] disconnected, releases all resources with it", sid)
|
glog.V(5).Infof("[Session Channel] of session [%s] disconnected, releases all resources with it, user [%s]",
|
||||||
|
sid, wSession.Username)
|
||||||
|
|
||||||
WideSessions.Remove(sid)
|
WideSessions.Remove(sid)
|
||||||
|
|
||||||
|
@ -132,8 +196,9 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
ret = map[string]interface{}{"output": "", "cmd": "session-output"}
|
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())
|
glog.Error("Session WS ERROR: " + err.Error())
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,9 +318,9 @@ func (sessions *Sessions) Remove(sid string) {
|
||||||
// kill processes
|
// kill processes
|
||||||
for _, p := range s.Processes {
|
for _, p := range s.Processes {
|
||||||
if err := p.Kill(); nil != err {
|
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 {
|
} 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)
|
delete(SessionWS, sid)
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.V(3).Infof("Removed a session [%s]", s.Id)
|
|
||||||
|
|
||||||
cnt := 0 // count wide sessions associated with HTTP session
|
cnt := 0 // count wide sessions associated with HTTP session
|
||||||
for _, s := range *sessions {
|
for _, s := range *sessions {
|
||||||
if s.HTTPSession.ID == s.HTTPSession.ID {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
154
session/users.go
154
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
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/b3log/wide/conf"
|
"github.com/b3log/wide/conf"
|
||||||
"github.com/b3log/wide/i18n"
|
"github.com/b3log/wide/i18n"
|
||||||
|
@ -20,6 +37,85 @@ const (
|
||||||
UserCreateError = "user create error"
|
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.
|
// LoginHandler handles request of user login.
|
||||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if "GET" == r.Method {
|
if "GET" == r.Method {
|
||||||
|
@ -44,7 +140,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// non-GET request as login request
|
// non-GET request as login request
|
||||||
|
|
||||||
succ := false
|
succ := true
|
||||||
data := map[string]interface{}{"succ": &succ}
|
data := map[string]interface{}{"succ": &succ}
|
||||||
defer util.RetJSON(w, r, data)
|
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 {
|
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
succ = true
|
succ = false
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
succ = false
|
||||||
for _, user := range conf.Wide.Users {
|
for _, user := range conf.Wide.Users {
|
||||||
if user.Name == args.Username && user.Password == args.Password {
|
if user.Name == args.Username && user.Password == args.Password {
|
||||||
succ = true
|
succ = true
|
||||||
|
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,9 +241,16 @@ 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 {
|
func addUser(username, password string) string {
|
||||||
// XXX: validate
|
addUserMutex.Lock()
|
||||||
|
defer addUserMutex.Unlock()
|
||||||
|
|
||||||
for _, user := range conf.Wide.Users {
|
for _, user := range conf.Wide.Users {
|
||||||
if user.Name == username {
|
if user.Name == username {
|
||||||
return UserExists
|
return UserExists
|
||||||
|
@ -155,19 +261,51 @@ func addUser(username, password string) string {
|
||||||
dir := filepath.Dir(firstUserWorkspace)
|
dir := filepath.Dir(firstUserWorkspace)
|
||||||
workspace := filepath.Join(dir, username)
|
workspace := filepath.Join(dir, username)
|
||||||
|
|
||||||
conf.Wide.Users = append(conf.Wide.Users,
|
newUser := &conf.User{Name: username, Password: password, Workspace: workspace,
|
||||||
&conf.User{Name: username, Password: password, Workspace: workspace,
|
Locale: conf.Wide.Locale, GoFormat: "gofmt", FontFamily: "Helvetica", FontSize: "13px",
|
||||||
Locale: conf.Wide.Locale, GoFormat: "gofmt", FontFamily: "Helvetica", FontSize: "inherit",
|
Editor: &conf.Editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit"}}
|
||||||
Editor: &conf.Editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit"}})
|
conf.Wide.Users = append(conf.Wide.Users, newUser)
|
||||||
|
|
||||||
if !conf.Save() {
|
if !conf.Save() {
|
||||||
return UserCreateError
|
return UserCreateError
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.CreateWorkspaceDir(workspace)
|
conf.CreateWorkspaceDir(workspace)
|
||||||
|
helloWorld(workspace)
|
||||||
conf.UpdateCustomizedConf(username)
|
conf.UpdateCustomizedConf(username)
|
||||||
|
|
||||||
|
http.Handle("/workspace/"+username+"/",
|
||||||
|
http.StripPrefix("/workspace/"+username+"/", http.FileServer(http.Dir(newUser.GetWorkspace()))))
|
||||||
|
|
||||||
glog.Infof("Created a user [%s]", username)
|
glog.Infof("Created a user [%s]", username)
|
||||||
|
|
||||||
return UserCreated
|
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.
|
// Shell.
|
||||||
package shell
|
package shell
|
||||||
|
|
||||||
|
@ -28,7 +42,6 @@ var ShellWS = map[string]*util.WSChannel{}
|
||||||
// IndexHandler handles request of Shell index.
|
// IndexHandler handles request of Shell index.
|
||||||
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
|
||||||
if httpSession.IsNew {
|
if httpSession.IsNew {
|
||||||
http.Redirect(w, r, "/login", http.StatusFound)
|
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.
|
// WSHandler handles request of creating Shell channel.
|
||||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
if httpSession.IsNew {
|
||||||
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
username := httpSession.Values["username"].(string)
|
username := httpSession.Values["username"].(string)
|
||||||
|
|
||||||
sid := r.URL.Query()["sid"][0]
|
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)
|
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||||
wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
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"}
|
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))
|
glog.V(4).Infof("Open a new [Shell] with session [%s], %d", sid, len(ShellWS))
|
||||||
|
|
||||||
input := map[string]interface{}{}
|
input := map[string]interface{}{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := wsChan.Conn.ReadJSON(&input); err != nil {
|
if err := wsChan.ReadJSON(&input); err != nil {
|
||||||
if err.Error() == "EOF" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err.Error() == "unexpected EOF" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.Error("Shell WS ERROR: " + err.Error())
|
glog.Error("Shell WS ERROR: " + err.Error())
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +135,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
ret = map[string]interface{}{"output": output, "cmd": "shell-output"}
|
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())
|
glog.Error("Shell WS ERROR: " + err.Error())
|
||||||
return
|
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 {
|
#dialogAboutDialog .dialog-main {
|
||||||
background-color: #FFF;
|
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 */
|
/* start reset & function */
|
||||||
body {
|
body {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
@ -44,3 +60,32 @@ button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
/* end reset & function */
|
/* 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
|
* dialig style
|
||||||
*
|
*
|
||||||
|
@ -80,4 +96,28 @@
|
||||||
.dialog-main input {
|
.dialog-main input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 2px auto;
|
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 {
|
#shellOutput, #shellInput {
|
||||||
width: 100%;
|
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 */
|
/* start side */
|
||||||
.side {
|
.side {
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
|
@ -29,6 +45,7 @@
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ztree li a:hover {
|
.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 {
|
.wrapper {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: 980px;
|
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);
|
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 {
|
#msg {
|
||||||
background-color: #fcdede;
|
background-color: #fcdede;
|
||||||
border: 1px solid #d2b2b2;
|
border: 1px solid #d2b2b2;
|
||||||
|
@ -112,6 +155,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.form.sign-up {
|
.form.sign-up {
|
||||||
margin-top: -20px;
|
margin-top: -71px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#signUpBtn {
|
||||||
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
/* end sign up */
|
/* 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 {
|
#startPage {
|
||||||
padding: 50px 70px;
|
padding: 50px 70px;
|
||||||
line-height: 28px;
|
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 */
|
/* start icon */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'icomoon';
|
font-family: 'icomoon';
|
||||||
|
@ -193,7 +209,8 @@
|
||||||
top: 1px;
|
top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-panel .tabs .ico {
|
.edit-panel .tabs .ico,
|
||||||
|
#dialogGoFilePrompt .ico {
|
||||||
background-image: url("../images/ico-file.png");
|
background-image: url("../images/ico-file.png");
|
||||||
float: left;
|
float: left;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
@ -243,6 +260,10 @@
|
||||||
background: #08f;
|
background: #08f;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.CodeMirror div.CodeMirror-cursor {
|
||||||
|
border-left: 2px solid #333;
|
||||||
|
}
|
||||||
/* end editor */
|
/* end editor */
|
||||||
|
|
||||||
/* start bottom-window-group */
|
/* start bottom-window-group */
|
||||||
|
@ -315,31 +336,6 @@
|
||||||
width: 50px;
|
width: 50px;
|
||||||
padding: 0 5px;
|
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 */
|
/* end bottom-window-group */
|
||||||
|
|
||||||
/* start footer */
|
/* 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 = {
|
var bottomGroup = {
|
||||||
tabs: undefined,
|
tabs: undefined,
|
||||||
searchTab: undefined,
|
searchTab: undefined,
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2011, Liyuan Li
|
* Copyright (c) 2014, B3log
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
$.fn.extend({
|
$.fn.extend({
|
||||||
dialog: {
|
dialog: {
|
||||||
|
@ -143,6 +144,9 @@
|
||||||
} else {
|
} else {
|
||||||
// 20(footer) + 23(header)
|
// 20(footer) + 23(header)
|
||||||
top = parseInt((windowH - dialogH - 43) / 2);
|
top = parseInt((windowH - dialogH - 43) / 2);
|
||||||
|
if (top < 0) {
|
||||||
|
top = 0;
|
||||||
|
}
|
||||||
left = parseInt((windowW - dialogW) / 2);
|
left = parseInt((windowW - dialogW) / 2);
|
||||||
}
|
}
|
||||||
$dialog.css({
|
$dialog.css({
|
||||||
|
@ -174,6 +178,10 @@
|
||||||
$.dialog._close(id, settings);
|
$.dialog._close(id, settings);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(window).resize(function () {
|
||||||
|
$(".dialog-background").height($("body").height());
|
||||||
|
});
|
||||||
|
|
||||||
if (typeof settings.afterInit === "function") {
|
if (typeof settings.afterInit === "function") {
|
||||||
settings.afterInit();
|
settings.afterInit();
|
||||||
|
@ -210,12 +218,12 @@
|
||||||
if (positionX > $(window).width() - $(dialog).width()) {
|
if (positionX > $(window).width() - $(dialog).width()) {
|
||||||
positionX = $(window).width() - $(dialog).width();
|
positionX = $(window).width() - $(dialog).width();
|
||||||
}
|
}
|
||||||
if (positionY < 0) {
|
|
||||||
positionY = 0;
|
|
||||||
}
|
|
||||||
if (positionY > $(window).height() - $(dialog).height()) {
|
if (positionY > $(window).height() - $(dialog).height()) {
|
||||||
positionY = $(window).height() - $(dialog).height();
|
positionY = $(window).height() - $(dialog).height();
|
||||||
}
|
}
|
||||||
|
if (positionY < 0) {
|
||||||
|
positionY = 0;
|
||||||
|
}
|
||||||
dialog.style.left = positionX + "px";
|
dialog.style.left = positionX + "px";
|
||||||
dialog.style.top = positionY + "px";
|
dialog.style.top = positionY + "px";
|
||||||
};
|
};
|
||||||
|
@ -231,7 +239,7 @@
|
||||||
_document.ondragstart = null;
|
_document.ondragstart = null;
|
||||||
_document.onselectstart = null;
|
_document.onselectstart = null;
|
||||||
_document.onselect = null;
|
_document.onselect = null;
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
_close: function (id, settings) {
|
_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 = {
|
var editors = {
|
||||||
data: [],
|
data: [],
|
||||||
tabs: {},
|
tabs: {},
|
||||||
|
@ -94,6 +110,7 @@ var editors = {
|
||||||
id: ".edit-panel",
|
id: ".edit-panel",
|
||||||
clickAfter: function (id) {
|
clickAfter: function (id) {
|
||||||
if (id === 'startPage') {
|
if (id === 'startPage') {
|
||||||
|
$(".footer .cursor").text('');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +126,11 @@ var editors = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cursor = wide.curEditor.getCursor();
|
||||||
|
wide.curEditor.setCursor(cursor);
|
||||||
wide.curEditor.focus();
|
wide.curEditor.focus();
|
||||||
|
|
||||||
|
$(".footer .cursor").text('| ' + (cursor.line + 1) + ':' + (cursor.ch + 1) + ' |');
|
||||||
},
|
},
|
||||||
removeBefore: function (id) {
|
removeBefore: function (id) {
|
||||||
if (id === 'startPage') { // 当前关闭的 tab 是起始页
|
if (id === 'startPage') { // 当前关闭的 tab 是起始页
|
||||||
|
@ -117,7 +138,6 @@ var editors = {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除编辑器
|
|
||||||
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
||||||
if (editors.data[i].id === id) {
|
if (editors.data[i].id === id) {
|
||||||
if (editors.data[i].editor.doc.isClean()) {
|
if (editors.data[i].editor.doc.isClean()) {
|
||||||
|
@ -163,6 +183,7 @@ var editors = {
|
||||||
tree.fileTree.cancelSelectedNode();
|
tree.fileTree.cancelSelectedNode();
|
||||||
wide.curNode = undefined;
|
wide.curNode = undefined;
|
||||||
wide.curEditor = undefined;
|
wide.curEditor = undefined;
|
||||||
|
$(".footer .cursor").text('');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +203,9 @@ var editors = {
|
||||||
break;
|
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) {
|
CodeMirror.commands.autocompleteAfterDot = function (cm) {
|
||||||
|
var token = cm.getTokenAt(cm.getCursor());
|
||||||
|
if ("comment" === token.type) {
|
||||||
|
return CodeMirror.Pass;
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
if (!cm.state.completionActive) {
|
if (!cm.state.completionActive) {
|
||||||
cm.showHint({hint: CodeMirror.hint.go, completeSingle: false});
|
cm.showHint({hint: CodeMirror.hint.go, completeSingle: false});
|
||||||
|
@ -413,33 +442,11 @@ var editors = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cursorLine = data.cursorLine;
|
var tId = tree.getTIdByPath(data.path);
|
||||||
var cursorCh = data.cursorCh;
|
wide.curNode = tree.fileTree.getNodeByTId(tId);
|
||||||
|
tree.fileTree.selectNode(wide.curNode);
|
||||||
|
|
||||||
var request = newWideRequest();
|
tree.openFile(wide.curNode, CodeMirror.Pos(data.cursorLine - 1, data.cursorCh - 1));
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -469,7 +476,7 @@ var editors = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
appendSearch: function (data, type, key) {
|
appendSearch: function (data, type, key) {
|
||||||
var searcHTML = '<ul>';
|
var searcHTML = '<ul class="list">';
|
||||||
|
|
||||||
for (var i = 0, ii = data.length; i < ii; i++) {
|
for (var i = 0, ii = data.length; i < ii; i++) {
|
||||||
var contents = data[i].contents[0],
|
var contents = data[i].contents[0],
|
||||||
|
@ -479,7 +486,7 @@ var editors = {
|
||||||
+ contents.substring(index + key.length);
|
+ contents.substring(index + key.length);
|
||||||
|
|
||||||
searcHTML += '<li title="' + data[i].path + '">'
|
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="'
|
+ '<i class="position" data-line="'
|
||||||
+ data[i].line + '" data-ch="' + data[i].ch + '"> (' + data[i].line + ':'
|
+ data[i].line + '" data-ch="' + data[i].ch + '"> (' + data[i].line + ':'
|
||||||
+ data[i].ch + ')</i></span></li>';
|
+ data[i].ch + ')</i></span></li>';
|
||||||
|
@ -544,33 +551,10 @@ var editors = {
|
||||||
$(".bottom-window-group .search").focus();
|
$(".bottom-window-group .search").focus();
|
||||||
},
|
},
|
||||||
// 新建一个编辑器 Tab,如果已经存在 Tab 则切换到该 Tab.
|
// 新建一个编辑器 Tab,如果已经存在 Tab 则切换到该 Tab.
|
||||||
newEditor: function (data) {
|
newEditor: function (data, cursor) {
|
||||||
$(".toolbars").show();
|
$(".toolbars").show();
|
||||||
var id = wide.curNode.tId;
|
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({
|
editors.tabs.add({
|
||||||
id: id,
|
id: id,
|
||||||
title: '<span title="' + wide.curNode.path + '"><span class="'
|
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']);
|
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);
|
var textArea = document.getElementById("editor" + id);
|
||||||
textArea.value = data.content;
|
textArea.value = data.content;
|
||||||
|
|
||||||
|
@ -592,11 +573,12 @@ var editors = {
|
||||||
autoCloseBrackets: true,
|
autoCloseBrackets: true,
|
||||||
matchBrackets: true,
|
matchBrackets: true,
|
||||||
highlightSelectionMatches: {showToken: /\w/},
|
highlightSelectionMatches: {showToken: /\w/},
|
||||||
rulers: rulers,
|
rulers: [{color: "#ccc", column: 120, lineStyle: "dashed"}],
|
||||||
styleActiveLine: true,
|
styleActiveLine: true,
|
||||||
theme: 'wide',
|
theme: 'wide',
|
||||||
indentUnit: 4,
|
indentUnit: 4,
|
||||||
foldGutter: true,
|
foldGutter: true,
|
||||||
|
cursorHeight: 1,
|
||||||
path: data.path,
|
path: data.path,
|
||||||
extraKeys: {
|
extraKeys: {
|
||||||
"Ctrl-\\": "autocompleteAnyWord",
|
"Ctrl-\\": "autocompleteAnyWord",
|
||||||
|
@ -610,7 +592,7 @@ var editors = {
|
||||||
wide.saveFile();
|
wide.saveFile();
|
||||||
},
|
},
|
||||||
"Shift-Ctrl-S": function () {
|
"Shift-Ctrl-S": function () {
|
||||||
wide.saveAllFiles();
|
menu.saveAllFiles();
|
||||||
},
|
},
|
||||||
"Shift-Alt-F": function () {
|
"Shift-Alt-F": function () {
|
||||||
var currentPath = editors.getCurrentPath();
|
var currentPath = editors.getCurrentPath();
|
||||||
|
@ -628,26 +610,121 @@ var editors = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Shift-Ctrl-Up": function (cm) {
|
"Shift-Ctrl-Up": function (cm) {
|
||||||
var cursor = cm.getCursor();
|
var content = '',
|
||||||
var line = cursor.line;
|
selectoion = cm.listSelections()[0];
|
||||||
var content = cm.getLine(line);
|
|
||||||
|
|
||||||
if (0 === line) {
|
var from = selectoion.anchor,
|
||||||
cm.replaceRange("", CodeMirror.Pos(0));
|
to = selectoion.head;
|
||||||
line++;
|
if (from.line > to.line) {
|
||||||
|
from = selectoion.head;
|
||||||
|
to = selectoion.anchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
cm.replaceRange("\n" + content, CodeMirror.Pos(line - 1));
|
for (var i = from.line, max = to.line; i <= max; i++) {
|
||||||
cm.setCursor(cursor);
|
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) {
|
"Shift-Ctrl-Down": function (cm) {
|
||||||
var cursor = cm.getCursor();
|
var content = '',
|
||||||
var line = cursor.line;
|
selectoion = cm.listSelections()[0];
|
||||||
var content = cm.getLine(line);
|
|
||||||
|
|
||||||
cm.replaceRange("\n", CodeMirror.Pos(line));
|
var from = selectoion.anchor,
|
||||||
cm.replaceRange(content, CodeMirror.Pos(line + 1));
|
to = selectoion.head;
|
||||||
cm.setCursor(CodeMirror.Pos(line + 1, cursor.ch));
|
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();
|
var cursor = cm.getCursor();
|
||||||
|
|
||||||
$(".footer .cursor").text('| ' + (cursor.line + 1) + ':' + (cursor.ch + 1) + ' |');
|
$(".footer .cursor").text('| ' + (cursor.line + 1) + ':' + (cursor.ch + 1) + ' |');
|
||||||
// TODO: 关闭 tab 的时候要重置
|
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.on('focus', function (cm) {
|
editor.on('focus', function (cm) {
|
||||||
|
@ -700,16 +776,19 @@ var editors = {
|
||||||
editor.setOption("autoCloseTags", true);
|
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;
|
wide.curEditor = editor;
|
||||||
editors.data.push({
|
editors.data.push({
|
||||||
"editor": editor,
|
"editor": editor,
|
||||||
"id": id
|
"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 = {
|
var hotkeys = {
|
||||||
defaultKeyMap: {
|
defaultKeyMap: {
|
||||||
// Ctrl+0 焦点切换到当前编辑器
|
// Ctrl+0 焦点切换到当前编辑器
|
||||||
|
@ -15,7 +31,7 @@ var hotkeys = {
|
||||||
which: 49
|
which: 49
|
||||||
},
|
},
|
||||||
// Ctrl+4 焦点切换到输出窗口
|
// Ctrl+4 焦点切换到输出窗口
|
||||||
goOutPut: {
|
goOutput: {
|
||||||
ctrlKey: true,
|
ctrlKey: true,
|
||||||
altKey: false,
|
altKey: false,
|
||||||
shiftKey: false,
|
shiftKey: false,
|
||||||
|
@ -63,7 +79,21 @@ var hotkeys = {
|
||||||
shiftKey: false,
|
shiftKey: false,
|
||||||
which: 81
|
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: {
|
buildRun: {
|
||||||
ctrlKey: false,
|
ctrlKey: false,
|
||||||
altKey: false,
|
altKey: false,
|
||||||
|
@ -71,6 +101,60 @@ var hotkeys = {
|
||||||
which: 117
|
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 () {
|
_bindOutput: function () {
|
||||||
$(".bottom-window-group .output").keydown(function (event) {
|
$(".bottom-window-group .output").keydown(function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -241,8 +325,8 @@ var hotkeys = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.ctrlKey === hotKeys.goOutPut.ctrlKey
|
if (event.ctrlKey === hotKeys.goOutput.ctrlKey
|
||||||
&& event.which === hotKeys.goOutPut.which) { // Ctrl+4 焦点切换到输出窗口
|
&& event.which === hotKeys.goOutput.which) { // Ctrl+4 焦点切换到输出窗口
|
||||||
bottomGroup.tabs.setCurrent("output");
|
bottomGroup.tabs.setCurrent("output");
|
||||||
|
|
||||||
windows.flowBottom();
|
windows.flowBottom();
|
||||||
|
@ -251,7 +335,7 @@ var hotkeys = {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.ctrlKey === hotKeys.goSearch.ctrlKey
|
if (event.ctrlKey === hotKeys.goSearch.ctrlKey
|
||||||
&& event.which === hotKeys.goSearch.which) { // Ctrl+5 焦点切换到搜索窗口
|
&& event.which === hotKeys.goSearch.which) { // Ctrl+5 焦点切换到搜索窗口
|
||||||
bottomGroup.tabs.setCurrent("search");
|
bottomGroup.tabs.setCurrent("search");
|
||||||
|
@ -339,13 +423,27 @@ var hotkeys = {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.which === hotKeys.buildRun.which) { // F6 构建并运行
|
if (event.which === hotKeys.build.which) { // F5 Build
|
||||||
wide.run();
|
menu.build();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
return;
|
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 () {
|
init: function () {
|
||||||
this._bindFileTree();
|
this._bindFileTree();
|
||||||
|
|
|
@ -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 = {
|
var menu = {
|
||||||
init: function () {
|
init: function () {
|
||||||
this.subMenu();
|
this.subMenu();
|
||||||
|
this._initPreference();
|
||||||
|
this._initAbout();
|
||||||
|
|
||||||
// 点击子菜单后消失
|
// 点击子菜单后消失
|
||||||
$(".frame li").click(function () {
|
$(".frame li").click(function () {
|
||||||
|
@ -8,6 +26,34 @@ var menu = {
|
||||||
$(".menu > ul > li > a, .menu > ul> li > span").removeClass("selected");
|
$(".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) {
|
disabled: function (list) {
|
||||||
for (var i = 0, max = list.length; i < max; i++) {
|
for (var i = 0, max = list.length; i < max; i++) {
|
||||||
$(".menu li." + list[i]).addClass("disabled");
|
$(".menu li." + list[i]).addClass("disabled");
|
||||||
|
@ -35,5 +81,321 @@ var menu = {
|
||||||
$(this).addClass("selected");
|
$(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 = {
|
var notification = {
|
||||||
init: function () {
|
init: function () {
|
||||||
$(".notification-count").click(function () {
|
$(".notification-count").click(function () {
|
||||||
|
@ -19,6 +35,12 @@ var notification = {
|
||||||
var data = JSON.parse(e.data),
|
var data = JSON.parse(e.data),
|
||||||
$notification = $('.bottom-window-group .notification > table'),
|
$notification = $('.bottom-window-group .notification > table'),
|
||||||
notificationHTML = '';
|
notificationHTML = '';
|
||||||
|
|
||||||
|
if (data.cmd && "init-notification" === data.cmd) {
|
||||||
|
console.log('[notification onmessage]' + e.data);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
notificationHTML += '<tr><td class="severity">' + data.severity
|
notificationHTML += '<tr><td class="severity">' + data.severity
|
||||||
+ '</td><td class="message">' + data.message
|
+ '</td><td class="message">' + data.message
|
||||||
|
|
|
@ -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
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
// 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-meta {color: rgb(98,143,181);}
|
||||||
.cm-s-wide span.cm-keyword { color: rgb(0,0,230); }
|
.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-number {color: #B35E4D;}
|
||||||
.cm-s-wide span.cm-def {color: #00f;}
|
.cm-s-wide span.cm-def {color: #00f;}
|
||||||
.cm-s-wide span.cm-variable {color: black;}
|
.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 = {
|
var session = {
|
||||||
init: function () {
|
init: function () {
|
||||||
this._initWS();
|
this._initWS();
|
||||||
|
@ -47,7 +63,20 @@ var session = {
|
||||||
// expand tree
|
// expand tree
|
||||||
for (var j = 0, jj = fileTree.length; j < jj; j++) {
|
for (var j = 0, jj = fileTree.length; j < jj; j++) {
|
||||||
if (nodes[i].path === fileTree[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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +91,7 @@ var session = {
|
||||||
|
|
||||||
if (nodes[i].path === currentFile) {
|
if (nodes[i].path === currentFile) {
|
||||||
id = nodes[i].tId;
|
id = nodes[i].tId;
|
||||||
|
|
||||||
// FIXME: 上面的展开是异步进行的,所以执行到这里的时候可能还没有展开完,导致定位不了可视区域
|
// FIXME: 上面的展开是异步进行的,所以执行到这里的时候可能还没有展开完,导致定位不了可视区域
|
||||||
tree.fileTree.selectNode(nodes[i]);
|
tree.fileTree.selectNode(nodes[i]);
|
||||||
wide.curNode = nodes[i];
|
wide.curNode = nodes[i];
|
||||||
|
|
|
@ -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 = {
|
var shell = {
|
||||||
_shellWS: undefined,
|
_shellWS: undefined,
|
||||||
_initWS: function () {
|
_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) {
|
var Tabs = function (obj) {
|
||||||
obj._$tabsPanel = $(obj.id + " > .tabs-panel");
|
obj._$tabsPanel = $(obj.id + " > .tabs-panel");
|
||||||
obj._$tabs = $(obj.id + " > .tabs");
|
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 = {
|
var tree = {
|
||||||
fileTree: undefined,
|
fileTree: undefined,
|
||||||
// 递归获取当前节点展开中的最后一个节点
|
// 递归获取当前节点展开中的最后一个节点
|
||||||
|
@ -61,7 +77,19 @@ var tree = {
|
||||||
|
|
||||||
return paths;
|
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);
|
var node = tree.fileTree.getNodeByTId(tId);
|
||||||
if (!node || !node.parentTId) {
|
if (!node || !node.parentTId) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -69,7 +97,7 @@ var tree = {
|
||||||
if (node.parentTId === parentTId) {
|
if (node.parentTId === parentTId) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return tree._isParents(node.parentTId, parentTId);
|
return tree.isParents(node.parentTId, parentTId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -111,6 +139,58 @@ var tree = {
|
||||||
$("#fileRMenu").hide();
|
$("#fileRMenu").hide();
|
||||||
$("#dialogRemoveConfirm").dialog("open");
|
$("#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 () {
|
init: function () {
|
||||||
$("#file").click(function () {
|
$("#file").click(function () {
|
||||||
$(this).focus();
|
$(this).focus();
|
||||||
|
@ -130,13 +210,18 @@ var tree = {
|
||||||
var setting = {
|
var setting = {
|
||||||
data: {
|
data: {
|
||||||
key: {
|
key: {
|
||||||
title: "title"
|
title: "path"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
view: {
|
view: {
|
||||||
showTitle: true,
|
showTitle: true,
|
||||||
selectedMulti: false
|
selectedMulti: false
|
||||||
},
|
},
|
||||||
|
async: {
|
||||||
|
enable: true,
|
||||||
|
url: "/file/refresh",
|
||||||
|
autoParam: ["path"]
|
||||||
|
},
|
||||||
callback: {
|
callback: {
|
||||||
onDblClick: function (event, treeId, treeNode) {
|
onDblClick: function (event, treeId, treeNode) {
|
||||||
if (treeNode) {
|
if (treeNode) {
|
||||||
|
@ -154,7 +239,7 @@ var tree = {
|
||||||
} else {
|
} else {
|
||||||
$("#fileRMenu .remove").addClass("disabled");
|
$("#fileRMenu .remove").addClass("disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#fileRMenu").show();
|
$("#fileRMenu").show();
|
||||||
|
|
||||||
fileRMenu.css({
|
fileRMenu.css({
|
||||||
|
@ -164,9 +249,9 @@ var tree = {
|
||||||
});
|
});
|
||||||
} else { // 右击了目录
|
} else { // 右击了目录
|
||||||
if (wide.curNode.removable) {
|
if (wide.curNode.removable) {
|
||||||
$("#dirRMenu .remove").removeClass("disabled");
|
$("#dirRMenu .remove, #dirRMenu .rename").removeClass("disabled");
|
||||||
} else {
|
} else {
|
||||||
$("#dirRMenu .remove").addClass("disabled");
|
$("#dirRMenu .remove, #dirRMenu .rename").addClass("disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wide.curNode.creatable) {
|
if (wide.curNode.creatable) {
|
||||||
|
@ -200,17 +285,30 @@ var tree = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._initSearch();
|
||||||
},
|
},
|
||||||
openFile: function (treeNode) {
|
openFile: function (treeNode, cursor) {
|
||||||
wide.curNode = treeNode;
|
wide.curNode = treeNode;
|
||||||
|
var tempCursor = cursor;
|
||||||
|
|
||||||
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
||||||
// 该节点文件已经打开
|
// 该节点文件已经打开
|
||||||
if (editors.data[i].id === treeNode.tId) {
|
if (editors.data[i].id === treeNode.tId) {
|
||||||
editors.tabs.setCurrent(treeNode.tId);
|
editors.tabs.setCurrent(treeNode.tId);
|
||||||
wide.curNode = treeNode;
|
|
||||||
wide.curEditor = editors.data[i].editor;
|
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();
|
wide.curEditor.focus();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,9 +336,68 @@ var tree = {
|
||||||
return false;
|
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 = {
|
var wide = {
|
||||||
curNode: undefined,
|
curNode: undefined,
|
||||||
curEditor: undefined,
|
curEditor: undefined,
|
||||||
|
@ -50,6 +66,10 @@ var wide = {
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
if (!data.succ) {
|
if (!data.succ) {
|
||||||
|
$("#dialogRemoveConfirm").dialog("close");
|
||||||
|
bottomGroup.tabs.setCurrent("notification");
|
||||||
|
windows.flowBottom();
|
||||||
|
$(".bottom-window-group .notification").focus();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +86,7 @@ var wide = {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0, ii = editors.data.length; i < ii; i++) {
|
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();
|
$(".edit-panel .tabs > div[data-index=" + editors.data[i].id + "]").find(".ico-close").click();
|
||||||
i--;
|
i--;
|
||||||
ii--;
|
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({
|
$("#dialogNewFilePrompt").dialog({
|
||||||
"modal": true,
|
"modal": true,
|
||||||
"height": 52,
|
"height": 52,
|
||||||
|
@ -103,43 +182,15 @@ var wide = {
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
if (!data.succ) {
|
if (!data.succ) {
|
||||||
|
$("#dialogNewFilePrompt").dialog("close");
|
||||||
|
bottomGroup.tabs.setCurrent("notification");
|
||||||
|
windows.flowBottom();
|
||||||
|
$(".bottom-window-group .notification").focus();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#dialogNewFilePrompt").dialog("close");
|
$("#dialogNewFilePrompt").dialog("close");
|
||||||
var suffix = name.split(".")[1],
|
var iconSkin = wide.getClassBySuffix(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
tree.fileTree.addNodes(wide.curNode, [{
|
tree.fileTree.addNodes(wide.curNode, [{
|
||||||
"name": name,
|
"name": name,
|
||||||
|
@ -179,6 +230,10 @@ var wide = {
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
if (!data.succ) {
|
if (!data.succ) {
|
||||||
|
$("#dialogNewDirPrompt").dialog("close");
|
||||||
|
bottomGroup.tabs.setCurrent("notification");
|
||||||
|
windows.flowBottom();
|
||||||
|
$(".bottom-window-group .notification").focus();
|
||||||
return false;
|
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({
|
$("#dialogGoLinePrompt").dialog({
|
||||||
"modal": true,
|
"modal": true,
|
||||||
"height": 52,
|
"height": 52,
|
||||||
|
@ -210,7 +351,7 @@ var wide = {
|
||||||
"ok": function () {
|
"ok": function () {
|
||||||
var line = parseInt($("#dialogGoLinePrompt > input").val()) - 1;
|
var line = parseInt($("#dialogGoLinePrompt > input").val()) - 1;
|
||||||
$("#dialogGoLinePrompt").dialog("close");
|
$("#dialogGoLinePrompt").dialog("close");
|
||||||
|
|
||||||
var editor = wide.curEditor;
|
var editor = wide.curEditor;
|
||||||
var cursor = editor.getCursor();
|
var cursor = editor.getCursor();
|
||||||
|
|
||||||
|
@ -223,88 +364,6 @@ var wide = {
|
||||||
editor.focus();
|
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 () {
|
_initLayout: function () {
|
||||||
var mainH = $(window).height() - $(".menu").height() - $(".footer").height(),
|
var mainH = $(window).height() - $(".menu").height() - $(".footer").height(),
|
||||||
|
@ -343,7 +402,7 @@ var wide = {
|
||||||
|
|
||||||
switch (data.cmd) {
|
switch (data.cmd) {
|
||||||
case 'run': // 正在运行
|
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;
|
wide.curProcessId = data.pid;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -358,17 +417,17 @@ var wide = {
|
||||||
case 'start-test':
|
case 'start-test':
|
||||||
case 'start-install':
|
case 'start-install':
|
||||||
case 'start-get':
|
case 'start-get':
|
||||||
bottomGroup.fillOutput(data.output);
|
bottomGroup.fillOutput('<pre>' + data.output + '</pre>');
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'go test':
|
case 'go test':
|
||||||
case 'go install':
|
case 'go install':
|
||||||
case 'go get':
|
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;
|
break;
|
||||||
case 'build':
|
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) { // 说明编译有错误输出
|
if (data.lints) { // 说明编译有错误输出
|
||||||
for (var i = 0; i < data.lints.length; i++) {
|
for (var i = 0; i < data.lints.length; i++) {
|
||||||
|
@ -495,55 +554,9 @@ var wide = {
|
||||||
|
|
||||||
wide._save(path, wide.curEditor);
|
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 () {
|
stop: function () {
|
||||||
if ($(".toolbars .ico-buildrun").length === 1) {
|
if ($(".toolbars .ico-buildrun").length === 1) {
|
||||||
wide.run();
|
menu.run();
|
||||||
return false;
|
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) {
|
gofmt: function (path, editor) {
|
||||||
var cursor = editor.getCursor();
|
var cursor = editor.getCursor();
|
||||||
var scrollInfo = editor.getScrollInfo();
|
var scrollInfo = editor.getScrollInfo();
|
||||||
|
@ -793,8 +657,42 @@ var wide = {
|
||||||
wide._save(path, editor);
|
wide._save(path, editor);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openAbout: function () {
|
getClassBySuffix: function (suffix) {
|
||||||
$("#dialogAbout").dialog("open");
|
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 = {
|
var windows = {
|
||||||
isMaxEditor: false,
|
isMaxEditor: false,
|
||||||
init: function () {
|
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 {
|
.CodeMirror-hints {
|
||||||
font-family: {{.user.Editor.FontFamily}};
|
font-family: {{.user.Editor.FontFamily}};
|
||||||
font-size: {{.user.Editor.FontSize}};
|
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.
|
// Utilities.
|
||||||
package util
|
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
|
package util
|
||||||
|
|
||||||
import (
|
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
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -8,7 +22,7 @@ import (
|
||||||
|
|
||||||
// Recover recovers a panic.
|
// Recover recovers a panic.
|
||||||
func Recover() {
|
func Recover() {
|
||||||
if re := recover(); re != nil {
|
if re := recover(); nil != re {
|
||||||
glog.Errorf("PANIC RECOVERED:\n %v, %s", re, debug.Stack())
|
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
|
package util
|
||||||
|
|
||||||
import (
|
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
|
package util
|
||||||
|
|
||||||
import (
|
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
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -15,9 +30,41 @@ type WSChannel struct {
|
||||||
Time time.Time // the latest use time
|
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.
|
// Close closed the channel.
|
||||||
func (c *WSChannel) Close() {
|
func (c *WSChannel) Close() {
|
||||||
c.Conn.Close()
|
if nil != c.Conn {
|
||||||
|
c.Conn.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh refreshes the channel by updating its use time.
|
// 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>
|
<span>{{.i18n.file}}</span>
|
||||||
<div class="frame">
|
<div class="frame">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="save-all disabled" onclick="wide.saveAllFiles()">
|
<li class="save-all disabled" onclick="menu.saveAllFiles()">
|
||||||
<span>{{.i18n.save_all_files}}</span>
|
<span>{{.i18n.save_all_files}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="close-all" onclick="wide.closeAllFiles()">
|
<li class="close-all" onclick="menu.closeAllFiles()">
|
||||||
<span>{{.i18n.close_all_files}}</span>
|
<span>{{.i18n.close_all_files}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="hr"></li>
|
<li class="hr"></li>
|
||||||
<li onclick="wide.exit()">
|
<li onclick="menu.exit()">
|
||||||
<span>{{.i18n.exit}}</span>
|
<span>{{.i18n.exit}}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -48,21 +48,21 @@
|
||||||
<span>{{.i18n.run}}</span>
|
<span>{{.i18n.run}}</span>
|
||||||
<div class="frame">
|
<div class="frame">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="build disabled" onclick="wide.build()">
|
<li class="build disabled" onclick="menu.build()">
|
||||||
<span>{{.i18n.build}}</span>
|
<span>{{.i18n.build}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="run disabled" onclick="wide.run()">
|
<li class="run disabled" onclick="menu.run()">
|
||||||
<span>{{.i18n.build_n_run}}</span>
|
<span>{{.i18n.build_n_run}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="hr"></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>
|
<span>{{.i18n.test}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="hr"></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>
|
<span>{{.i18n.goget}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="go-install disabled" onclick="wide.goinstall()">
|
<li class="go-install disabled" onclick="menu.goinstall()">
|
||||||
<span>{{.i18n.goinstall}}</span>
|
<span>{{.i18n.goinstall}}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -90,6 +90,9 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<li onclick="menu.openPreference()">
|
||||||
|
<span>{{.i18n.perference}}</span>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span>{{.i18n.help}}</span>
|
<span>{{.i18n.help}}</span>
|
||||||
<div class="frame">
|
<div class="frame">
|
||||||
|
@ -108,7 +111,7 @@
|
||||||
<li onclick="editors.openStartPage()">
|
<li onclick="editors.openStartPage()">
|
||||||
<span>{{.i18n.start_page}}</span>
|
<span>{{.i18n.start_page}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li onclick="wide.openAbout()">
|
<li onclick="menu.openAbout()">
|
||||||
<span>{{.i18n.about}}</span>
|
<span>{{.i18n.about}}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -134,6 +137,10 @@
|
||||||
<li class="create" onclick="tree.newFile(this);">{{.i18n.create_file}}</li>
|
<li class="create" onclick="tree.newFile(this);">{{.i18n.create_file}}</li>
|
||||||
<li class="create" onclick="tree.newDir(this);">{{.i18n.create_dir}}</li>
|
<li class="create" onclick="tree.newDir(this);">{{.i18n.create_dir}}</li>
|
||||||
<li class="remove" onclick="tree.removeIt(this);">{{.i18n.delete}}</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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -141,6 +148,9 @@
|
||||||
<div id="fileRMenu" class="frame">
|
<div id="fileRMenu" class="frame">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="remove" onclick="tree.removeIt(this);">{{.i18n.delete}}</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="export" onclick="tree.export(this);">{{.i18n.export}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -149,7 +159,7 @@
|
||||||
|
|
||||||
<div class="edit-panel">
|
<div class="edit-panel">
|
||||||
<div class="toolbars fn-none">
|
<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.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 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>
|
<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}}">
|
<li onclick="editors.close()" title="{{.i18n.close}}">
|
||||||
<span>{{.i18n.close}}</span>
|
<span>{{.i18n.close}}</span>
|
||||||
</li>
|
</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>
|
<span>{{.i18n.close_all_files}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li onclick="editors.closeOther()" title="{{.i18n.close_other}}">
|
<li onclick="editors.closeOther()" title="{{.i18n.close_other}}">
|
||||||
|
@ -230,15 +240,23 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="dialogAlert" class="fn-none"></div>
|
<div id="dialogAlert" class="fn-none"></div>
|
||||||
<div id="dialogAbout" 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">
|
<div id="dialogNewFilePrompt" class="dialog-prompt fn-none">
|
||||||
<input/>
|
<input/>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="dialogRenamePrompt" class="dialog-prompt fn-none">
|
||||||
|
<input/>
|
||||||
|
</div>
|
||||||
<div id="dialogNewDirPrompt" class="dialog-prompt fn-none">
|
<div id="dialogNewDirPrompt" class="dialog-prompt fn-none">
|
||||||
<input/>
|
<input/>
|
||||||
</div>
|
</div>
|
||||||
<div id="dialogGoLinePrompt" class="dialog-prompt fn-none">
|
<div id="dialogGoLinePrompt" class="dialog-prompt fn-none">
|
||||||
<input/>
|
<input/>
|
||||||
</div>
|
</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">
|
<div id="dialogSearchForm" class="dialog-form fn-none">
|
||||||
<input placeholder="{{.i18n.keyword}}" />
|
<input placeholder="{{.i18n.keyword}}" />
|
||||||
<input placeholder="{{.i18n.file_format}}" />
|
<input placeholder="{{.i18n.file_format}}" />
|
||||||
|
@ -259,8 +277,10 @@
|
||||||
"restore_editor": "{{.i18n.restore_editor}}",
|
"restore_editor": "{{.i18n.restore_editor}}",
|
||||||
"max_editor": "{{.i18n.max_editor}}",
|
"max_editor": "{{.i18n.max_editor}}",
|
||||||
"delete": "{{.i18n.delete}}",
|
"delete": "{{.i18n.delete}}",
|
||||||
|
"rename": "{{.i18n.rename}}",
|
||||||
"cancel": "{{.i18n.cancel}}",
|
"cancel": "{{.i18n.cancel}}",
|
||||||
"goto_line": "{{.i18n.goto_line}}",
|
"goto_line": "{{.i18n.goto_line}}",
|
||||||
|
"goto_file": "{{.i18n.goto_file}}",
|
||||||
"go": "{{.i18n.go}}",
|
"go": "{{.i18n.go}}",
|
||||||
"create": "{{.i18n.create}}",
|
"create": "{{.i18n.create}}",
|
||||||
"create_file": "{{.i18n.create_file}}",
|
"create_file": "{{.i18n.create_file}}",
|
||||||
|
@ -279,7 +299,9 @@
|
||||||
"new_version_available": "{{.i18n.new_version_available}}",
|
"new_version_available": "{{.i18n.new_version_available}}",
|
||||||
"colon": "{{.i18n.colon}}",
|
"colon": "{{.i18n.colon}}",
|
||||||
"file": "{{.i18n.file}}",
|
"file": "{{.i18n.file}}",
|
||||||
"uptodate": "{{.i18n.uptodate}}"
|
"uptodate": "{{.i18n.uptodate}}",
|
||||||
|
"perference": "{{.i18n.perference}}",
|
||||||
|
"apply": "{{.i18n.apply}}"
|
||||||
},
|
},
|
||||||
"channel": {
|
"channel": {
|
||||||
"editor": '{{.conf.EditorChannel}}',
|
"editor": '{{.conf.EditorChannel}}',
|
||||||
|
|
|
@ -15,8 +15,10 @@
|
||||||
<li>Alt-Shift-F{{.i18n.colon}}{{.i18n.format}}</li>
|
<li>Alt-Shift-F{{.i18n.colon}}{{.i18n.format}}</li>
|
||||||
<li>Ctrl-L{{.i18n.colon}}{{.i18n.goto_line}}</li>
|
<li>Ctrl-L{{.i18n.colon}}{{.i18n.goto_line}}</li>
|
||||||
<li>Ctrl-E{{.i18n.colon}}{{.i18n.delete_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-Up{{.i18n.colon}}{{.i18n.copy_lines_up}}</li>
|
||||||
<li>Shift-Ctrl-Down{{.i18n.colon}}{{.i18n.copy_line_down}}</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>Ctrl-S{{.i18n.colon}}{{.i18n.save_editor_file}}</li>
|
||||||
<li>Shift-Ctrl-S{{.i18n.colon}}{{.i18n.save_all_editors_files}}</li>
|
<li>Shift-Ctrl-S{{.i18n.colon}}{{.i18n.save_all_editors_files}}</li>
|
||||||
<li>Ctrl-Q{{.i18n.colon}}{{.i18n.close_editor}}</li>
|
<li>Ctrl-Q{{.i18n.colon}}{{.i18n.close_editor}}</li>
|
||||||
|
@ -27,6 +29,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
<h2>{{.i18n.search}}</h2>
|
<h2>{{.i18n.search}}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>Shift-Alt-O{{.i18n.colon}}{{.i18n.goto_file}}</li>
|
||||||
<li>Ctrl-F{{.i18n.colon}}{{.i18n.search}}/{{.i18n.find}}</li>
|
<li>Ctrl-F{{.i18n.colon}}{{.i18n.search}}/{{.i18n.find}}</li>
|
||||||
<li>Ctrl-G{{.i18n.colon}}{{.i18n.find_next}}</li>
|
<li>Ctrl-G{{.i18n.colon}}{{.i18n.find_next}}</li>
|
||||||
<li>Shift-Ctrl-G{{.i18n.colon}}{{.i18n.find_previous}}</li>
|
<li>Shift-Ctrl-G{{.i18n.colon}}{{.i18n.find_previous}}</li>
|
||||||
|
@ -44,7 +47,9 @@
|
||||||
</ul>
|
</ul>
|
||||||
<h2>{{.i18n.run}}</h2>
|
<h2>{{.i18n.run}}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>F5{{.i18n.colon}}{{.i18n.build}}</li>
|
||||||
<li>F6{{.i18n.colon}}{{.i18n.build_n_run}}</li>
|
<li>F6{{.i18n.colon}}{{.i18n.build_n_run}}</li>
|
||||||
|
<li>Ctrl-C{{.i18n.colon}}{{.i18n.clearOutput}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<li><a href="https://github.com/b3log/wide" target="_blank">GitHub</a></li>
|
<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://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="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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</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="username" placeholder="Username"/><br/>
|
||||||
<input id="password" type="password" placeholder="Password"/><br/>
|
<input id="password" type="password" placeholder="Password"/><br/>
|
||||||
<input id="confirmPassword" type="password" placeholder="Confirm your password"/>
|
<input id="confirmPassword" type="password" placeholder="Confirm your password"/>
|
||||||
|
<button id="signUpBtn" class="button">{{.i18n.sign_up}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -134,6 +135,10 @@
|
||||||
$("#msg").hide();
|
$("#msg").hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#signUpBtn").click(function () {
|
||||||
|
signUp();
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in New Issue