1
0
mirror of https://github.com/schollz/cowyo.git synced 2023-08-10 21:13:00 +03:00

Start again from scratch

This commit is contained in:
Zack Scholl 2017-03-22 20:49:55 -06:00
parent c0d87c752c
commit 52abf02847
767 changed files with 0 additions and 247038 deletions

View File

@ -1,32 +0,0 @@
# sudo docker build -t cowyo .
# sudo docker run -it -p 8003:8003 -v `pwd`/data:/data cowyo bash
FROM ubuntu:16.04
# Get basics
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get install -y golang git wget curl vim
RUN mkdir /usr/local/work
ENV GOPATH /usr/local/work
# Install cowyo
WORKDIR "/root"
RUN go get github.com/schollz/cowyo
RUN git clone https://github.com/schollz/cowyo.git
WORKDIR "/root/cowyo"
RUN git pull
RUN go build
# Setup supervisor
RUN apt-get update && apt-get install -y supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Add Tini
ENV TINI_VERSION v0.9.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]
# Startup
CMD ["/usr/bin/supervisord"]

156
Godeps/Godeps.json generated
View File

@ -1,156 +0,0 @@
{
"ImportPath": "github.com/schollz/cowyo",
"GoVersion": "go1.7",
"GodepVersion": "v79",
"Deps": [
{
"ImportPath": "github.com/boj/redistore",
"Comment": "v1.2",
"Rev": "fc113767cd6b051980f260d6dbe84b2740c46ab0"
},
{
"ImportPath": "github.com/boltdb/bolt",
"Comment": "v1.3.0-58-ge9cf4fa",
"Rev": "e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd"
},
{
"ImportPath": "github.com/garyburd/redigo/internal",
"Comment": "v1.0.0-17-g908534c",
"Rev": "908534c8b97586a4597e3fa195875d2d26502b97"
},
{
"ImportPath": "github.com/garyburd/redigo/redis",
"Comment": "v1.0.0-17-g908534c",
"Rev": "908534c8b97586a4597e3fa195875d2d26502b97"
},
{
"ImportPath": "github.com/gin-gonic/contrib/sessions",
"Rev": "4d2dccc9a4541014fec054e483cc76609b97fb16"
},
{
"ImportPath": "github.com/gin-gonic/gin",
"Comment": "v1.1-65-g049da60",
"Rev": "049da60f5114a479f04a668f3d8a6f3b44fe6658"
},
{
"ImportPath": "github.com/gin-gonic/gin/binding",
"Comment": "v1.1-65-g049da60",
"Rev": "049da60f5114a479f04a668f3d8a6f3b44fe6658"
},
{
"ImportPath": "github.com/gin-gonic/gin/render",
"Comment": "v1.1-65-g049da60",
"Rev": "049da60f5114a479f04a668f3d8a6f3b44fe6658"
},
{
"ImportPath": "github.com/golang/protobuf/proto",
"Rev": "8ee79997227bf9b34611aee7946ae64735e6fd93"
},
{
"ImportPath": "github.com/gorilla/context",
"Comment": "v1.1-7-g08b5f42",
"Rev": "08b5f424b9271eedf6f9f0ce86cb9396ed337a42"
},
{
"ImportPath": "github.com/gorilla/securecookie",
"Comment": "v1.1-5-gfa5329f",
"Rev": "fa5329f913702981df43dcb2a380bac429c810b5"
},
{
"ImportPath": "github.com/gorilla/sessions",
"Comment": "v1.1-2-g83c8db3",
"Rev": "83c8db3bdc9be789e57e3756ffbcffd2d7d40176"
},
{
"ImportPath": "github.com/gorilla/websocket",
"Comment": "v1.1.0-6-gadf16b3",
"Rev": "adf16b31781325cbd41085c5be901d95b4d1f33d"
},
{
"ImportPath": "github.com/jcelliott/lumber",
"Rev": "dd349441af25132d146d7095c6693a15431fc9b1"
},
{
"ImportPath": "github.com/mattn/go-isatty",
"Rev": "30a891c33c7cde7b02a981314b4228ec99380cca"
},
{
"ImportPath": "github.com/microcosm-cc/bluemonday",
"Rev": "e79763773ab6222ca1d5a7cbd9d62d83c1f77081"
},
{
"ImportPath": "github.com/russross/blackfriday",
"Comment": "v1.4-40-g5f33e7b",
"Rev": "5f33e7b7878355cd2b7e6b8eefc48a5472c69f70"
},
{
"ImportPath": "github.com/sergi/go-diff/diffmatchpatch",
"Rev": "24e2351369ec4949b2ed0dc5c477afdd4c4034e8"
},
{
"ImportPath": "github.com/shurcooL/sanitized_anchor_name",
"Rev": "1dba4b3954bc059efc3991ec364f9f9a35f597d2"
},
{
"ImportPath": "golang.org/x/crypto/bcrypt",
"Rev": "2e74c773682f59dc50a56475f7918dd8fa6dcaf8"
},
{
"ImportPath": "golang.org/x/crypto/blowfish",
"Rev": "2e74c773682f59dc50a56475f7918dd8fa6dcaf8"
},
{
"ImportPath": "golang.org/x/crypto/cast5",
"Rev": "2e74c773682f59dc50a56475f7918dd8fa6dcaf8"
},
{
"ImportPath": "golang.org/x/crypto/openpgp",
"Rev": "2e74c773682f59dc50a56475f7918dd8fa6dcaf8"
},
{
"ImportPath": "golang.org/x/crypto/openpgp/armor",
"Rev": "2e74c773682f59dc50a56475f7918dd8fa6dcaf8"
},
{
"ImportPath": "golang.org/x/crypto/openpgp/elgamal",
"Rev": "2e74c773682f59dc50a56475f7918dd8fa6dcaf8"
},
{
"ImportPath": "golang.org/x/crypto/openpgp/errors",
"Rev": "2e74c773682f59dc50a56475f7918dd8fa6dcaf8"
},
{
"ImportPath": "golang.org/x/crypto/openpgp/packet",
"Rev": "2e74c773682f59dc50a56475f7918dd8fa6dcaf8"
},
{
"ImportPath": "golang.org/x/crypto/openpgp/s2k",
"Rev": "2e74c773682f59dc50a56475f7918dd8fa6dcaf8"
},
{
"ImportPath": "golang.org/x/net/html",
"Rev": "007e530097ad7f954752df63046b4036f98ba6a6"
},
{
"ImportPath": "golang.org/x/net/html/atom",
"Rev": "007e530097ad7f954752df63046b4036f98ba6a6"
},
{
"ImportPath": "golang.org/x/sys/unix",
"Rev": "d75a52659825e75fff6158388dddc6a5b04f9ba5"
},
{
"ImportPath": "gopkg.in/gin-contrib/sse.v0",
"Rev": "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae"
},
{
"ImportPath": "gopkg.in/go-playground/validator.v8",
"Comment": "v8.18.1",
"Rev": "5f57d2222ad794d0dffb07e664ea05e2ee07d60c"
},
{
"ImportPath": "gopkg.in/yaml.v2",
"Rev": "4c78c975fe7c825c6d1466c42be594d1d6f3aba6"
}
]
}

5
Godeps/Readme generated
View File

@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

21
LICENSE
View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 Zack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,47 +0,0 @@
SOURCEDIR=.
SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
BINARY=cowyo
VERSION=1.1.2
BUILD_TIME=`date +%FT%T%z`
BUILD=`git rev-parse HEAD`
LDFLAGS=-ldflags "-X main.VersionNum=${VERSION} -X main.Build=${BUILD} -X main.BuildTime=${BUILD_TIME}"
.DEFAULT_GOAL: $(BINARY)
$(BINARY): $(SOURCES)
go get github.com/boltdb/bolt
go get github.com/gin-gonic/contrib/sessions
go get github.com/gin-gonic/gin
go get github.com/gorilla/websocket
go get github.com/microcosm-cc/bluemonday
go get github.com/russross/blackfriday
go get github.com/sergi/go-diff/diffmatchpatch
go get github.com/jcelliott/lumber
go build ${LDFLAGS} -o ${BINARY} ${SOURCES}
.PHONY: clean
clean:
if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi
rm -rf binaries
.PHONY: binaries
binaries:
rm -rf binaries
rm -f cowyo
mkdir binaries
env GOOS=linux GOARCH=amd64 go build ${LDFLAGS} -o ${BINARY} ${SOURCES}
zip -9 -r cowyo-linux-64bit.zip cowyo static/* templates/*
rm -f cowyo
env GOOS=windows GOARCH=amd64 go build ${LDFLAGS} -o ${BINARY}.exe ${SOURCES}
zip -9 -r cowyo-windows-64bit.zip cowyo.exe static/* templates/*
rm -f cowyo.exe
env GOOS=linux GOARCH=arm go build ${LDFLAGS} -o ${BINARY} ${SOURCES}
zip -9 -r cowyo-raspberrypi.zip cowyo static/* templates/*
rm -f cowyo
env GOOS=darwin GOARCH=amd64 go build ${LDFLAGS} -o ${BINARY} ${SOURCES}
zip -9 -r cowyo-macosx-64bit.zip cowyo static/* templates/*
rm -f cowyo
mv *.zip binaries/

134
README.md
View File

@ -1,134 +0,0 @@
![Logo](/static/img/cowyo.png)
# [cowyo.com](http://cowyo.com/)
[![Version 1.1.1](https://img.shields.io/badge/version-1.1.1-brightgreen.svg)]() [![Go Report Card](https://goreportcard.com/badge/github.com/schollz/cowyo)](https://goreportcard.com/report/github.com/schollz/cowyo) [![Join the chat at https://gitter.im/schollz/cowyo](https://badges.gitter.im/schollz/cowyo.svg)](https://gitter.im/schollz/cowyo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
This is a self-contained notepad webserver that makes sharing easy and _fast_. The most important feature here is _simplicity_. There are many other features as well including versioning, page locking, self-destructing messages, encryption, math support, syntax highlighting, command line support, content-delivery, and listifying. Read on to learn more about the features.
# Features
**Simplicity**. The philosophy here is to _just type_. To jot a note, simply load the page at [`/`](http://cowyo.com/) and just start typing. No need to press edit, the browser will already be focused on the text. No need to press save - it will automatically save when you stop writing. The URL at [`/`](http://cowyo.com/) will redirect to an easy-to-remember name that you can use to reload the page at anytime, anywhere. But, you can also use any URL you want, e.g. [`/AnythingYouWant`](http://cowyo.com/AnythingYouWant). All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](http://cowyo.com/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](http://cowyo.com/AnythingYouWant/view). You can write in HTML or [Markdown](https://daringfireball.net/projects/markdown/) for page rendering. To quickly link to `/view` pages, just use `[[AnythingYouWant]]`.
![Simply type to edit.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help1.gif)
<br>
**Listifying**. If you are writing a list and you want to tick off things really easily, just add `/list`. For example, after editing [`/grocery`](http://cowyo.com/grocery), goto [`/grocery/list`](http://cowyo.com/grocery/list). In this page, whatever you click on will be struck through and moved to the end. This is helpful if you write a grocery list and then want to easily delete things from it.
![Lists are easy to make.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help2.gif)
<br>
**Page locking**. Pages can be locked by providing a password to prevent further editing. The whole version tree will still be available. _Note_: This is not available for list mode.
![Locking is easy.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help3.gif)
<br>
**Automatic versioning**. All previous versions of all notes are stored and can be accessed by adding `?version=X` onto `/view` or `/edit`. If you are on the `/view` or `/edit` pages the menu below will show the most substantial changes in the history. Note, only the _current_ version can be edited (no branching allowed, yet).
![Versioning is easy.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help4.gif)
<br>
**Self-destructing messages**. You can write a message [that will delete itself](https://github.com/schollz/cowyo/blob/master/routes.go#L550-L553) when a user loads it (in any view). Useful for transmitting sensitive information. To use, simply add a line somewhere that says only "`self-destruct`".
![Mission impossible style self-destruction.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help5.gif)
<br>
**Security**. HTTPS support is provided and everything is sanitized to prevent XSS attacks. Though all URLs are publicly accessible, you are free to obfuscate your website by using an obscure/random address (read: the site is still publicly accessible, just hard to find!). In addition to TLS support, you can PGP-encrypt your messages using a passphrase (_Note: This will delete the version tree_).
![Security and encryption baked in.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help6.gif)
<br>
**Syntax highlighting**. If you use a coding extension (e.g. .py, .md, .txt, .js, ...) then you'll automatically see syntax highlighting and line numbers.
![Coding syntax is provided if you use an extension](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help7.gif)
<br>
**CLI tools**. Want to upload/download from the command line? Its super easy. Upload/download files using `curl` with a simple command:
```bash
$ echo "Hello, world!" > hi.txt
$ curl -L --upload-file hi.txt cowyo.com
File uploaded to http://cowyo.com/hi.txt
$ curl -L cowyo.com/test.txt
Hello, world!
```
or just skip the file-creation step and let `cowyo` figure out a name for you:
```bash
$ echo "Wow, so easy" | curl -L --upload-file "-" cowyo.com
File uploaded to http://cowyo.com/CautiousCommonLoon
$ curl -L cowyo.com/CautiousCommonLoon
Wow, so easy
```
<br>
**Content Delivery**. Want use a script on your own domain? Just use the extension `/raw` with optional versioning (e.g. `/raw?version=1`). `cowyo` will serve these files with a wildcard Access-Control-Allow-Origin so they will work anywhere. Check out a live example [on this JSFiddle](https://jsfiddle.net/9mm3afao/).
**Keyboard Shortcuts**. Quickly transition between Edit/View/List by using `Ctl+Shift+E` to Edit, `Ctl+Shift+Z` to View, and `Ctl+Shift+L` to Listify.
**Admin controls**. The Admin can view/delete all the documents by setting the `-a YourAdminKey` when starting the program. Then the admin has access to the `/ls/YourAdminKey` to view and delete any of the pages.
**Math support**. Math is supported with [Katex](https://github.com/Khan/KaTeX) using `$\frac{1}{2}$` for inline equations and `$$\frac{1}{2}$$` for regular equations.
# Install
## From release
Just [download the latest release](https://github.com/schollz/cowyo/releases/tag/1.1.0), unzip and run. Then open your browser to the specified address.
## From source
First [install Go 1.6+](https://golang.org/doc/install).
```
$ go get github.com/schollz/cowyo
$ cd $GOPATH/src/github.com/schollz/cowyo
$ go build
$ ./cowyo
```
Then open your browser to the specified address.
## From Docker
```
$ docker pull schollz/cowyo
$ docker run -it -p 8003:8003 -v /local/dir/to/store/data:/data schollz/cowyo
```
Then open your browser to 127.0.0.1:8003.
# Contact
If you'd like help or you find a bug, please submit [an issue](https://github.com/schollz/cowyo/issues). Any other comments, questions or anything at all, just [tweet me @zack_118](https://twitter.com/intent/tweet?screen_name=zack_118)
# Acknowledgements
Thanks to [tscholl2](https://github.com/tscholl2) and [sjsafranek](https://github.com/sjsafranek).
Icons made by [Freepik](http://www.freepik.com) from [www.flaticon.com](http://www.flaticon.com), licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/ "Creative Commons BY 3.0").
File uploading from [transfer.sh](https://github.com/dutchcoders/transfer.sh/blob/98399c91dd86682077cf9542badbf1658fd9a8c1/transfersh-server/handlers.go#L293-L369), licensed by [MIT license](https://github.com/dutchcoders/transfer.sh/blob/40c9bf7675fb84e78d9a011052b9d0900ec7dde1/LICENSE).
# License
The MIT License (MIT)
Copyright (c) 2016 Zack
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,9 +0,0 @@
# Release 1.0
New features:
- New route /raw to use as a content delivery system
- Coding support provided through [CodeMirror](https://codemirror.net/)
- CLI support for uploading/downloading files with `curl`
- New route to force WSS sockets (for use with Caddy)
- Changed name, improved documentation

186
db.go
View File

@ -1,186 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"log"
"strings"
"time"
"github.com/boltdb/bolt"
"github.com/sergi/go-diff/diffmatchpatch"
)
var db *bolt.DB
var open bool
// Open to create the database and open
func Open(filename string) error {
var err error
config := &bolt.Options{Timeout: 30 * time.Second}
db, err = bolt.Open(filename, 0600, config)
if err != nil {
fmt.Println("Opening BoltDB timed out")
log.Fatal(err)
}
open = true
return nil
}
// Close database
func Close() {
open = false
db.Close()
}
// WikiData is data for storing in DB
type WikiData struct {
Title string
CurrentText string
Diffs []string
Timestamps []string
Encrypted bool
Locked string
}
func getCurrentText(title string, version int) (string, []versionsInfo, bool, time.Duration, bool, string, int) {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
title = strings.ToLower(title)
var vi []versionsInfo
totalTime := time.Now().Sub(time.Now())
isCurrent := true
currentText := ""
encrypted := false
locked := ""
currentVersionNum := -1
if !open {
return currentText, vi, isCurrent, totalTime, encrypted, locked, currentVersionNum
}
err := db.View(func(tx *bolt.Tx) error {
var err error
b := tx.Bucket([]byte("datas"))
if b == nil {
return fmt.Errorf("db must be opened before loading")
}
k := []byte(title)
val := b.Get(k)
if val == nil {
return nil
}
var p WikiData
err = p.decode(val)
if err != nil {
return err
}
currentText = p.CurrentText
encrypted = p.Encrypted
locked = p.Locked
currentVersionNum = len(p.Diffs) - 1
if version > -1 && version < len(p.Diffs) {
// get that version of text instead
currentText = rebuildTextsToDiffN(p, version)
isCurrent = false
}
vi, totalTime = getImportantVersions(p)
return nil
})
if err != nil {
fmt.Printf("Could not get WikiData: %s", err)
}
return currentText, vi, isCurrent, totalTime, encrypted, locked, currentVersionNum
}
func (p *WikiData) load(title string) error {
title = strings.ToLower(title)
if !open {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
}
err := db.View(func(tx *bolt.Tx) error {
var err error
b := tx.Bucket([]byte("datas"))
if b == nil {
return nil
}
k := []byte(title)
val := b.Get(k)
if val == nil {
// make new one
p.Title = title
p.CurrentText = ""
p.Diffs = []string{}
p.Timestamps = []string{}
return nil
}
err = p.decode(val)
if err != nil {
return err
}
return nil
})
if err != nil {
fmt.Printf("Could not get WikiData: %s", err)
return err
}
return nil
}
func (p *WikiData) save(newText string) error {
if !open {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
}
err := db.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte("datas"))
if err != nil {
return fmt.Errorf("create bucket: %s", err)
}
// find diffs
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(p.CurrentText, newText, true)
delta := dmp.DiffToDelta(diffs)
p.CurrentText = newText
p.Timestamps = append(p.Timestamps, time.Now().Format(time.ANSIC))
p.Diffs = append(p.Diffs, delta)
enc, err := p.encode()
if err != nil {
return fmt.Errorf("could not encode WikiData: %s", err)
}
p.Title = strings.ToLower(p.Title)
err = bucket.Put([]byte(p.Title), enc)
if err != nil {
return fmt.Errorf("could add to bucket: %s", err)
}
return err
})
// // Add the new name to the programdata so its not randomly generated
// if err == nil && len(p.Timestamps) > 0 && len(p.CurrentText) > 0 {
// err2 := db.Update(func(tx *bolt.Tx) error {
// b := tx.Bucket([]byte("programdata"))
// id, _ := b.NextSequence()
// idInt := int(id)
// return b.Put(itob(idInt), []byte(p.Title))
// })
// if err2 != nil {
// return fmt.Errorf("could not add to programdata: %s", err)
// }
// }
return err
}
func (p *WikiData) encode() ([]byte, error) {
enc, err := json.Marshal(p)
if err != nil {
return nil, err
}
return enc, nil
}
func (p *WikiData) decode(data []byte) error {
err := json.Unmarshal(data, &p)
if err != nil {
return err
}
return nil
}

View File

@ -1,73 +0,0 @@
package main
import (
"bytes"
"errors"
"io/ioutil"
"log"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
)
var encryptionType string
func init() {
encryptionType = "PGP SIGNATURE"
}
func encryptString(encryptionText string, encryptionPassphraseString string) string {
encryptionPassphrase := []byte(encryptionPassphraseString)
encbuf := bytes.NewBuffer(nil)
w, err := armor.Encode(encbuf, encryptionType, nil)
if err != nil {
log.Fatal(err)
}
plaintext, err := openpgp.SymmetricallyEncrypt(w, encryptionPassphrase, nil, nil)
if err != nil {
log.Fatal(err)
}
message := []byte(encryptionText)
_, err = plaintext.Write(message)
plaintext.Close()
w.Close()
return encbuf.String()
}
func decryptString(decryptionString string, encryptionPassphraseString string) (string, error) {
encryptionPassphrase := []byte(encryptionPassphraseString)
decbuf := bytes.NewBuffer([]byte(decryptionString))
result, err := armor.Decode(decbuf)
if err != nil {
return "", err
}
alreadyPrompted := false
md, err := openpgp.ReadMessage(result.Body, nil, func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
if alreadyPrompted {
return nil, errors.New("Could not decrypt using passphrase")
} else {
alreadyPrompted = true
}
return encryptionPassphrase, nil
}, nil)
if err != nil {
return "", err
}
bytes, err := ioutil.ReadAll(md.UnverifiedBody)
if err != nil {
return "", err
}
return string(bytes), nil
}
// func main() {
// test := encryptString("This is some string", "golang")
// fmt.Println(test)
// testD := decryptString(test, "golang")
// fmt.Println(testD)
//
// }

View File

@ -1,101 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: awwkoala
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
# This should be placed in /etc/init.d directory
# start with
# sudo /etc/init.d/awwkoala start
# stop with
# sudo /etc/init.d/awwkoala start
name="cowyo"
dir="CUR_DIR"
user="USERCUR"
cmd="./$name -p :PORT EXT_ADDRESS"
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps `get_pid` > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
cd "$dir"
sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill `get_pid`
for i in {1..10}
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0

View File

@ -1,41 +0,0 @@
server {
# SERVER BLOCK FOR cowyo
listen 80; ## listen for ipv4; this line is default and implied
access_log /etc/nginx/logs/access-cowyo.log;
error_log /etc/nginx/logs/error-cowyo.log info;
root CUR_DIR;
server_name ADDRESS;
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# CSS and Javascript
location ~* \.(?:css|js)$ {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
location ^~ /static {
try_files $uri $uri/ =404;
}
location ~ ^/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:PORT;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

View File

@ -1,86 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/gorilla/websocket"
)
func get(key string) interface{} {
c, _, err := websocket.DefaultDialer.Dial("wss://cowyo.com/ws", nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
done := make(chan struct{})
var value interface{}
go func() {
defer c.Close()
defer close(done)
for {
_, message, err := c.ReadMessage()
if err != nil {
// log.Println("read:", err)
return
}
var m struct {
TextData string
}
json.Unmarshal([]byte(message), &m)
json.Unmarshal([]byte(m.TextData), &value)
done <- struct{}{}
}
}()
// ask for something
c.WriteMessage(websocket.TextMessage, []byte(`{"Title":"`+key+`", "UpdateClient":true}`))
<-done
return value
}
func set(key string, message interface{}) interface{} {
c, _, err := websocket.DefaultDialer.Dial("wss://cowyo.com/ws", nil)
if err != nil {
log.Fatal("dial:", err)
}
defer c.Close()
done := make(chan struct{})
var value interface{}
go func() {
defer c.Close()
defer close(done)
for {
_, message, err := c.ReadMessage()
if err != nil {
// log.Println("read:", err)
return
}
var m struct {
TextData string
}
json.Unmarshal([]byte(message), &m)
json.Unmarshal([]byte(m.TextData), &value)
done <- struct{}{}
}
}()
// ask for something
bJson, _ := json.Marshal(message)
aa, _ := json.Marshal(map[string]interface{}{
"Title": key,
"UpdateServer": true,
"TextData": string(bJson),
})
c.WriteMessage(websocket.TextMessage, aa)
<-done
return value
}
func main() {
fmt.Println(get("data"))
fmt.Println(get("data"))
m := make(map[string]int)
m["some string"] = 29
fmt.Println(set("data2", m))
fmt.Println(get("data2"))
}

View File

@ -1,42 +0,0 @@
function save(key, value) {
c = new WebSocket('wss://cowyo.com/ws');
return new Promise(function (resolve, reject) {
try {
c.onopen = function (_) {
c.send(JSON.stringify({
TextData: JSON.stringify(value),
Title: `${key}`,
UpdateServer: true,
UpdateClient: false,
}));
return resolve(true);
}
} catch(e) {
return reject(e);
}
});
}
// save('hello2', 'world');
function get(key) {
c = new WebSocket('wss://cowyo.com/ws');
return new Promise(function (resolve, reject) {
try {
c.onmessage = function(evt) {
return resolve(JSON.parse(JSON.parse(evt.data).TextData));
}
c.onopen = function (_) {
c.send(JSON.stringify({
Title: `${key}`,
UpdateClient: true,
}));
};
} catch(e) {
return reject(e);
}
});
}

View File

@ -1,31 +0,0 @@
ADDRESS = yourserver.com
PORT = 8003
CUR_DIR = $(shell bash -c 'pwd')
USERCUR = $(shell bash -c 'whoami')
make:
go build
install:
rm -rf jinstall
mkdir jinstall
cp cowyo.ssl.nginx cowyo.ssl.nginx.temp
sed -i 's/PORT/$(PORT)/g' cowyo.ssl.nginx.temp
sed -i 's/ADDRESS/$(ADDRESS)/g' cowyo.ssl.nginx.temp
sed -i 's^CUR_DIR^$(CUR_DIR)^g' cowyo.ssl.nginx.temp
cp cowyo.init cowyo.init.temp
sed -i 's/EXT_ADDRESS/$(ADDRESS)/g' cowyo.init.temp
sed -i 's^CUR_DIR^$(CUR_DIR)^g' cowyo.init.temp
sed -i 's^USERCUR^$(USERCUR)^g' cowyo.init.temp
sed -i 's^PORT^$(PORT)^g' cowyo.init.temp
cp cowyo.init.temp /etc/init.d/cowyo.init
chmod +x /etc/init.d/cowyo.init
cp cowyo.ssl.nginx.temp /etc/nginx/sites-available/cowyo.nginx
ln -fs /etc/nginx/sites-available/cowyo.nginx /etc/nginx/sites-enabled/cowyo.nginx
/etc/init.d/nginx reload
/etc/init.d/nginx restart
/etc/init.d/cowyo.init restart
rm -rf *.temp
.PHONY: install

View File

@ -1,17 +0,0 @@
First install the NGINX block in this directory. (There is an experimental Makefile that will do this, just try `sudo make install`.
To use letsencrypt follow these steps:
```
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
sudo service nginx stop
sudo ./letsencrypt-auto certonly --standalone --email youremail@somewhere.com -d yourserver.com
sudo service nginx start
```
Then startup `cowyo` with
```bash
sudo ./cowyo -p :8001 -key /etc/letsencrypt/live/yourserver.com/privkey.pem -crt /etc/letsencrypt/live/yourserver.com/cert.pem yourserver.com
```

View File

@ -1,101 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: cowyo
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
# This should be placed in /etc/init.d directory
# start with
# sudo /etc/init.d/cowyo start
# stop with
# sudo /etc/init.d/cowyo start
dir="CUR_DIR"
user="USERCUR"
cmd="./cowyo -p :PORT -key /etc/letsencrypt/live/EXT_ADDRESS/privkey.pem -crt /etc/letsencrypt/live/EXT_ADDRESS/cert.pem yourserver.com./cowyo EXT_ADDRESS"
name="cowyo"
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps `get_pid` > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
cd "$dir"
sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill `get_pid`
for i in {1..10}
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0

View File

@ -1,50 +0,0 @@
server {
listen 80;
server_name ADDRESS;
rewrite ^ https://$server_name$request_uri? permanent;
}
server {
# SERVER BLOCK FOR ADDRESS
listen 443 ssl;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_certificate /etc/letsencrypt/live/ADDRESS/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ADDRESS/privkey.pem;
access_log /etc/nginx/logs/access-ADDRESS.log;
error_log /etc/nginx/logs/error-ADDRESS.log info;
root CUR_DIR;
server_name ADDRESS;
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# CSS and Javascript
location ~* \.(?:css|js)$ {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
location ^~ /static {
try_files $uri $uri/ =404;
}
location ~ ^/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass https://127.0.0.1:PORT;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

181
main.go
View File

@ -1,181 +0,0 @@
package main
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"github.com/boltdb/bolt"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
)
// AllowedIPs is a white/black list of
// IP addresses allowed to access cowyo
var AllowedIPs = map[string]bool{
"192.168.1.13": true,
"192.168.1.12": true,
"192.168.1.2": true,
}
// RuntimeArgs contains all runtime
// arguments available
var RuntimeArgs struct {
WikiName string
ExternalIP string
Port string
DatabaseLocation string
ServerCRT string
ServerKey string
SourcePath string
AdminKey string
Socket string
ForceWss bool
DumpDataset string
RestoreDataset string
Debug bool
}
var VersionNum string
func init() {
gin.SetMode(gin.ReleaseMode)
}
func main() {
// _, executableFile, _, _ := runtime.Caller(0) // get full path of this file
cwd, _ := os.Getwd()
databaseFile := path.Join(cwd, "data.db")
flag.StringVar(&RuntimeArgs.Port, "p", ":8003", "port to bind")
flag.StringVar(&RuntimeArgs.DatabaseLocation, "db", databaseFile, "location of database file")
flag.StringVar(&RuntimeArgs.AdminKey, "a", "", "key to access admin privaleges")
flag.StringVar(&RuntimeArgs.ServerCRT, "crt", "", "location of SSL certificate")
flag.StringVar(&RuntimeArgs.ServerKey, "key", "", "location of SSL key")
flag.StringVar(&RuntimeArgs.WikiName, "w", "cowyo", "custom name for wiki")
flag.BoolVar(&RuntimeArgs.ForceWss, "e", false, "force encrypted sockets (use if using Caddy auto HTTPS)")
flag.BoolVar(&RuntimeArgs.Debug, "d", false, "debugging mode")
flag.StringVar(&RuntimeArgs.DumpDataset, "dump", "", "directory to dump all data to")
flag.StringVar(&RuntimeArgs.RestoreDataset, "restore", "", "directory to restore all data from")
flag.CommandLine.Usage = func() {
fmt.Println(`cowyo (version ` + VersionNum + `)
Usage: cowyo [options] [address]
If address is not provided then cowyo
will determine the best internal IP address.
Example: 'cowyo'
Example: 'cowyo yourserver.com'
Example: 'cowyo -p :8080 localhost:8080'
Example: 'cowyo -p :8080 -crt ssl/server.crt -key ssl/server.key localhost:8080'
Options:`)
flag.CommandLine.PrintDefaults()
}
flag.Parse()
// Set the log level
if RuntimeArgs.Debug == false {
logger.Level(2)
} else {
logger.Level(0)
}
if len(RuntimeArgs.DumpDataset) > 0 {
fmt.Println("Dumping data to '" + RuntimeArgs.DumpDataset + "' folder...")
dumpEverything(RuntimeArgs.DumpDataset)
os.Exit(1)
}
RuntimeArgs.ExternalIP = flag.Arg(0)
if RuntimeArgs.ExternalIP == "" {
logger.Debug("Getting external ip...")
RuntimeArgs.ExternalIP = GetLocalIP() + RuntimeArgs.Port
logger.Debug("Using ip: %s and port %s", GetLocalIP(), RuntimeArgs.Port)
}
RuntimeArgs.SourcePath = cwd
if len(RuntimeArgs.AdminKey) == 0 {
RuntimeArgs.AdminKey = RandStringBytesMaskImprSrc(50)
}
// create programdata bucket
Open(RuntimeArgs.DatabaseLocation)
err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte("programdata"))
if err != nil {
return fmt.Errorf("create bucket: %s", err)
}
return err
})
if err != nil {
panic(err)
}
Close()
// Default page
defaultPage, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "templates/aboutpage.md"))
p := WikiData{"help", "", []string{}, []string{}, false, "zzz"}
p.save(string(defaultPage))
defaultPage, _ = ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "templates/privacypolicy.md"))
p = WikiData{"privacypolicy", "", []string{}, []string{}, false, "zzz"}
p.save(string(defaultPage))
if len(RuntimeArgs.RestoreDataset) > 0 {
fmt.Println("Restoring data from '" + RuntimeArgs.RestoreDataset + "' folder...")
filepath.Walk(RuntimeArgs.RestoreDataset, restoreFile)
os.Exit(1)
}
// var q WikiData
// q.load("about")
// fmt.Println(getImportantVersions(q))
r := gin.Default()
r.LoadHTMLGlob(path.Join(RuntimeArgs.SourcePath, "templates/*"))
store := sessions.NewCookieStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
r.GET("/", newNote)
r.HEAD("/", func(c *gin.Context) { c.Status(200) })
r.GET("/:title", editNote)
r.PUT("/:title", putFile)
r.PUT("/", putFile)
r.GET("/:title/*option", everythingElse)
r.POST("/:title/*option", encryptionRoute)
r.DELETE("/listitem", deleteListItem)
r.DELETE("/deletepage", deletePage)
if RuntimeArgs.ServerCRT != "" && RuntimeArgs.ServerKey != "" {
RuntimeArgs.Socket = "wss"
fmt.Println("--------------------------")
fmt.Println("cowyo (version " + VersionNum + ") is up and running on https://" + RuntimeArgs.ExternalIP)
fmt.Println("Admin key: " + RuntimeArgs.AdminKey)
fmt.Println("--------------------------")
r.RunTLS(RuntimeArgs.Port, RuntimeArgs.ServerCRT, RuntimeArgs.ServerKey)
} else {
RuntimeArgs.Socket = "ws"
if RuntimeArgs.ForceWss {
RuntimeArgs.Socket = "wss"
}
fmt.Println("--------------------------")
fmt.Println("cowyo (version " + VersionNum + ") is up and running on http://" + RuntimeArgs.ExternalIP)
fmt.Println("Admin key: " + RuntimeArgs.AdminKey)
fmt.Println("--------------------------")
r.Run(RuntimeArgs.Port)
}
}
func restoreFile(path string, f os.FileInfo, err error) error {
fName := filepath.Base(path)
buf := bytes.NewBuffer(nil)
fOpen, _ := os.Open(path) // Error handling elided for brevity.
io.Copy(buf, fOpen) // Error handling elided for brevity.
fOpen.Close()
s := string(buf.Bytes())
fmt.Println(fName)
p := WikiData{fName, "", []string{}, []string{}, false, ""}
p.save(s)
return nil
}

View File

@ -1,59 +0,0 @@
import os
"""DEFUNCT
darwin arm
darwin arm64
dragonfly amd64
freebsd 386
freebsd amd64
freebsd arm
linux 386
linux arm64
linux ppc64le
netbsd 386
netbsd amd64
netbsd arm
openbsd 386
openbsd amd64
openbsd arm
plan9 386
plan9 amd64
solaris amd64
windows 386
darwin 386
darwin amd64
linux arm
linux ppc64
windows amd64"""
arches = """linux amd64
windows amd64
linux arm
darwin amd64"""
arches = arches.split("\n")
version = "1.0"
programName = "awwkoala"
try:
os.system("rm -rf builds")
except:
pass
os.mkdir("builds")
for arch in arches:
goos = arch.split()[0]
goarch = arch.split()[1]
exe = ""
if "windows" in goos:
exe = ".exe"
cmd1 = 'env GOOS=%(goos)s GOARCH=%(goarch)s go build -o builds/%(programName)s%(exe)s' % {'goos':goos,'goarch':goarch,'exe':exe,'programName':programName}
cmd2 = 'zip -r %(programName)s-%(version)s-%(goos)s-%(goarch)s.zip %(programName)s%(exe)s ../templates ../static' % {'goos':goos,'goarch':goarch,'exe':exe,'version':version,'programName':programName}
print(cmd1)
os.system(cmd1)
os.chdir("builds")
print(cmd2)
os.system(cmd2)
cmd3 = 'rm %(programName)s%(exe)s' % {'exe':exe,'programName':programName}
print(cmd3)
os.system(cmd3)
os.chdir("../")

View File

@ -1,25 +0,0 @@
package main
import "time"
var bannedIPs []string
func init() {
go clearBannedIPs()
}
func clearBannedIPs() {
for {
bannedIPs = []string{}
time.Sleep(3 * time.Minute)
}
}
func isIPBanned(ip string) bool {
if stringInSlice(ip, bannedIPs) {
return true
} else {
bannedIPs = append(bannedIPs, ip)
return false
}
}

642
routes.go
View File

@ -1,642 +0,0 @@
package main
import (
"bytes"
"fmt"
"html/template"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
"github.com/boltdb/bolt"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday"
)
const _24K = (1 << 20) * 24
func putFile(c *gin.Context) {
if isIPBanned(c.ClientIP()) {
c.Data(200, "text/plain", []byte("You are rate limited to 20 requests/hour."))
return
}
filename := c.Param("title")
if len(filename) == 0 {
filename = randomAlliterateCombo()
}
contentLength := c.Request.ContentLength
var reader io.Reader
reader = c.Request.Body
if contentLength == -1 {
// queue file to disk, because s3 needs content length
var err error
var f io.Reader
f = reader
var b bytes.Buffer
n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF {
log.Printf("%s", err.Error())
}
if n > _24K {
file, err := ioutil.TempFile("./", "transfer-")
if err != nil {
log.Printf("%s", err.Error())
}
defer file.Close()
n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil {
os.Remove(file.Name())
log.Printf("%s", err.Error())
}
reader, err = os.Open(file.Name())
} else {
reader = bytes.NewReader(b.Bytes())
}
contentLength = n
}
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
// p := WikiData{filename, "", []string{}, []string{}, false, ""}
// p.save(buf.String())
var p WikiData
p.load(strings.ToLower(filename))
p.save(buf.String())
c.Data(200, "text/plain", []byte("File uploaded to http://"+RuntimeArgs.ExternalIP+"/"+filename))
}
type EncryptionPost struct {
Text string `form:"text" json:"text" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
func encryptionRoute(c *gin.Context) {
title := c.Param("title")
option := c.Param("option")
var jsonLoad EncryptionPost
if option == "/decrypt" {
if c.BindJSON(&jsonLoad) == nil {
var err error
currentText, _, _, _, encrypted, _, _ := getCurrentText(title, -1)
if encrypted == true {
currentText, err = decryptString(currentText, jsonLoad.Password)
if err != nil {
c.JSON(200, gin.H{
"status": "Inorrect passphrase.",
"title": title,
"option": option,
"success": false,
})
} else {
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, false, ""}
p.save(currentText)
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": true,
})
}
}
} else {
c.JSON(200, gin.H{
"status": "Could not bind",
"title": title,
"option": option,
"success": false,
})
}
}
if option == "/encrypt" {
if c.BindJSON(&jsonLoad) == nil {
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, true, ""}
p.save(encryptString(jsonLoad.Text, jsonLoad.Password))
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": true,
})
} else {
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": false,
})
}
}
if option == "/lock" {
if c.BindJSON(&jsonLoad) == nil {
var p WikiData
err := p.load(strings.ToLower(title))
if err != nil {
panic(err)
}
hashedPassword, _ := HashPassword([]byte(jsonLoad.Password))
p.Locked = string(hashedPassword)
p.save(p.CurrentText)
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": true,
})
} else {
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": false,
})
}
}
if option == "/unlock" {
if c.BindJSON(&jsonLoad) == nil {
var p WikiData
err := p.load(strings.ToLower(title))
if err != nil {
panic(err)
}
if len(p.Locked) > 0 &&
(p.Locked == jsonLoad.Password ||
CheckPasswordHash([]byte(p.Locked), []byte(jsonLoad.Password)) == nil) {
p.Locked = ""
p.save(p.CurrentText)
c.JSON(200, gin.H{
"status": "Unlocked!",
"title": title,
"option": option,
"success": true,
})
} else {
c.JSON(200, gin.H{
"status": "Incorrect password!",
"title": title,
"option": option,
"success": false,
})
}
} else {
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": false,
})
}
}
}
func newNote(c *gin.Context) {
title := randomAlliterateCombo()
c.Redirect(302, "/"+title)
}
func getCodeType(title string) string {
if strings.Contains(title, ".js") {
return "javascript"
} else if strings.Contains(title, ".py") {
return "python"
} else if strings.Contains(title, ".go") {
return "go"
} else if strings.Contains(title, ".html") {
return "htmlmixed"
} else if strings.Contains(title, ".md") {
return "markdown"
} else if strings.Contains(title, ".sh") {
return "shell"
} else if strings.Contains(title, ".css") {
return "css"
}
return ""
}
func getRecentlyEdited(title string, c *gin.Context) []string {
session := sessions.Default(c)
var recentlyEdited string
v := session.Get("recentlyEdited")
editedThings := []string{}
if v == nil {
recentlyEdited = title
} else {
editedThings = strings.Split(v.(string), "|||")
if !stringInSlice(title, editedThings) {
recentlyEdited = v.(string) + "|||" + title
} else {
recentlyEdited = v.(string)
}
}
session.Set("recentlyEdited", recentlyEdited)
session.Save()
return editedThings
}
func editNote(c *gin.Context) {
title := c.Param("title")
if title == "ws" {
wshandler(c.Writer, c.Request)
} else if title == "robots.txt" {
robotsTxtFile, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "static/text/robots.txt"))
c.Data(200, "text/plain", robotsTxtFile)
} else if title == "sitemap.xml" {
robotsTxtFile, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "static/text/sitemap.xml"))
c.Data(200, "text/plain", robotsTxtFile)
} else if strings.ToLower(title) == "help" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
c.Redirect(302, "/Help/view")
} else {
version := c.DefaultQuery("version", "-1")
versionNum, _ := strconv.Atoi(version)
currentText, versions, currentVersion, totalTime, encrypted, locked, currentVersionNum := getCurrentText(title, versionNum)
if strings.Contains(c.Request.Header.Get("User-Agent"), "curl/") {
c.Data(200, "text/plain", []byte(currentText))
return
}
if encrypted || len(locked) > 0 {
c.Redirect(302, "/"+title+"/view")
return
}
if strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct") {
c.Redirect(302, "/"+title+"/view")
return
}
numRows := len(strings.Split(currentText, "\n")) + 10
totalTimeString := totalTime.String()
if totalTime.Seconds() < 1 {
totalTimeString = "< 1 s"
}
splitStrings := strings.Split(title, ".")
suffix := splitStrings[len(splitStrings)-1]
CodeType := getCodeType(title)
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"ExternalIP": RuntimeArgs.ExternalIP,
"CurrentText": currentText,
"CurrentVersionNum": currentVersionNum,
"NumRows": numRows,
"Versions": versions,
"TotalTime": totalTimeString,
"SocketType": RuntimeArgs.Socket,
"NoEdit": !currentVersion,
"Coding": len(CodeType) > 0,
"CodeType": CodeType,
"Suffix": suffix,
"RecentlyEdited": getRecentlyEdited(title, c),
})
}
}
func everythingElse(c *gin.Context) {
option := c.Param("option")
title := c.Param("title")
if option == "/view" || option == "/v" {
version := c.DefaultQuery("version", "-1")
noprompt := c.DefaultQuery("noprompt", "-1")
versionNum, _ := strconv.Atoi(version)
if strings.ToLower(title) == "help" {
versionNum = -1
}
currentText, versions, _, totalTime, encrypted, locked, _ := getCurrentText(title, versionNum)
if (strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct")) && strings.ToLower(title) != "help" {
currentText = strings.Replace(currentText, "self-destruct\n", `> *This page has been deleted, you cannot return after closing.*`+"\n", 1)
currentText = strings.Replace(currentText, "\nself-destruct", "\n"+`> *This page has been deleted, you cannot return after closing.*`, 1)
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, false, ""}
p.save("")
}
renderMarkdown(c, currentText, title, versions, "", totalTime, encrypted, noprompt == "-1", len(locked) > 0, getRecentlyEdited(title, c))
} else if option == "/raw" {
version := c.DefaultQuery("version", "-1")
versionNum, _ := strconv.Atoi(version)
if strings.ToLower(title) == "help" {
versionNum = -1
}
currentText, _, _, _, _, _, _ := getCurrentText(title, versionNum)
c.Writer.Header().Set("Content-Type", contentType(title))
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Max-Age", "86400")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-Max")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Data(200, contentType(title), []byte(currentText))
} else if title == "ls" && option == "/"+RuntimeArgs.AdminKey && len(RuntimeArgs.AdminKey) > 1 {
renderMarkdown(c, listEverything(), "ls", nil, RuntimeArgs.AdminKey, time.Now().Sub(time.Now()), false, false, false, []string{})
} else if option == "/list" || option == "/l" {
renderList(c, title)
} else if title == "static" {
serveStaticFile(c, option)
} else {
c.Redirect(302, "/"+title)
}
}
func serveStaticFile(c *gin.Context, option string) {
staticFile, err := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "static") + option)
if err != nil {
c.AbortWithStatus(404)
} else {
c.Data(200, contentType(option), []byte(staticFile))
}
}
func renderMarkdown(c *gin.Context, currentText string, title string, versions []versionsInfo, AdminKey string, totalTime time.Duration, encrypted bool, noprompt bool, locked bool, recentlyEdited []string) {
originalText := currentText
CodeType := getCodeType(title)
if CodeType == "markdown" {
CodeType = ""
}
r, _ := regexp.Compile("\\[\\[(.*?)\\]\\]")
for _, s := range r.FindAllString(currentText, -1) {
currentText = strings.Replace(currentText, s, "["+s[2:len(s)-2]+"](/"+s[2:len(s)-2]+"/view)", 1)
}
unsafe := blackfriday.MarkdownCommon([]byte(currentText))
pClean := bluemonday.UGCPolicy()
pClean.AllowElements("img")
pClean.AllowAttrs("alt").OnElements("img")
pClean.AllowAttrs("src").OnElements("img")
pClean.AllowAttrs("class").OnElements("a")
pClean.AllowAttrs("href").OnElements("a")
pClean.AllowAttrs("id").OnElements("a")
pClean.AllowDataURIImages()
html := pClean.SanitizeBytes(unsafe)
html2 := string(html)
r, _ = regexp.Compile("\\$\\$(.*?)\\$\\$")
for _, s := range r.FindAllString(html2, -1) {
html2 = strings.Replace(html2, s, "<span class='texp' data-expr='"+s[2:len(s)-2]+"'></span>", 1)
}
r, _ = regexp.Compile("\\$(.*?)\\$")
for _, s := range r.FindAllString(html2, -1) {
html2 = strings.Replace(html2, s, "<span class='texi' data-expr='"+s[1:len(s)-1]+"'></span>", 1)
}
html2 = strings.Replace(html2, "&amp;#36;", "&#36;", -1)
html2 = strings.Replace(html2, "&amp;#91;", "&#91;", -1)
html2 = strings.Replace(html2, "&amp;#93;", "&#93;", -1)
html2 = strings.Replace(html2, "&amp35;", "&#35;", -1)
totalTimeString := totalTime.String()
if totalTime.Seconds() < 1 {
totalTimeString = "< 1 s"
}
if encrypted {
CodeType = "asciiarmor"
}
c.HTML(http.StatusOK, "view.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"Body": template.HTML([]byte(html2)),
"CurrentText": originalText,
"Versions": versions,
"TotalTime": totalTimeString,
"AdminKey": AdminKey,
"Encrypted": encrypted,
"Locked": locked,
"Prompt": noprompt,
"LockedOrEncrypted": locked || encrypted,
"Coding": len(CodeType) > 0,
"CodeType": CodeType,
"RecentlyEdited": recentlyEdited,
})
}
func reorderList(text string) ([]template.HTML, []string) {
listItemsString := ""
for _, lineString := range strings.Split(text, "\n") {
if len(lineString) > 1 {
if string(lineString[0]) != "-" {
listItemsString += "- " + lineString + "\n"
} else {
listItemsString += lineString + "\n"
}
}
}
// get ordering of template.HTML for rendering
renderedListString := string(blackfriday.MarkdownCommon([]byte(listItemsString)))
listItems := []template.HTML{}
endItems := []template.HTML{}
for _, lineString := range strings.Split(renderedListString, "\n") {
if len(lineString) > 1 {
if strings.Contains(lineString, "<del>") || strings.Contains(lineString, "</ul>") {
endItems = append(endItems, template.HTML(lineString))
} else {
listItems = append(listItems, template.HTML(lineString))
}
}
}
// get ordering of strings for deleting
listItemsStringArray := []string{}
endItemsStringArray := []string{}
for _, lineString := range strings.Split(listItemsString, "\n") {
if len(lineString) > 1 {
if strings.Contains(lineString, "~~") {
endItemsStringArray = append(endItemsStringArray, lineString)
} else {
listItemsStringArray = append(listItemsStringArray, lineString)
}
}
}
return append(listItems, endItems...), append(listItemsStringArray, endItemsStringArray...)
}
func renderList(c *gin.Context, title string) {
if strings.ToLower(title) == "help" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
c.Redirect(302, "/Help/view")
}
var p WikiData
err := p.load(strings.ToLower(title))
if err != nil {
panic(err)
}
currentText := p.CurrentText
if strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct") {
c.Redirect(302, "/"+title+"/view")
}
if p.Encrypted || len(p.Locked) > 0 {
c.Redirect(302, "/"+title+"/view")
}
// Convert [[page]] to [page](/page/view)
r, _ := regexp.Compile("\\[\\[(.*?)\\]\\]")
for _, s := range r.FindAllString(p.CurrentText, -1) {
p.CurrentText = strings.Replace(p.CurrentText, s, "["+s[2:len(s)-2]+"](/"+s[2:len(s)-2]+"/view)", 1)
}
pClean := bluemonday.UGCPolicy()
pClean.AllowElements("img")
pClean.AllowAttrs("alt").OnElements("img")
pClean.AllowAttrs("src").OnElements("img")
pClean.AllowAttrs("class").OnElements("a")
pClean.AllowAttrs("href").OnElements("a")
pClean.AllowAttrs("id").OnElements("a")
pClean.AllowDataURIImages()
text := pClean.SanitizeBytes([]byte(p.CurrentText))
listItems, _ := reorderList(string(text))
for i := range listItems {
newHTML := strings.Replace(string(listItems[i]), "</a>", "</a>"+`<span id="`+strconv.Itoa(i)+`" class="deletable">`, -1)
newHTML = strings.Replace(newHTML, "<a href=", "</span><a href=", -1)
newHTML = strings.Replace(newHTML, "<li>", "<li>"+`<span id="`+strconv.Itoa(i)+`" class="deletable">`, -1)
newHTML = strings.Replace(newHTML, "</li>", "</span></li>", -1)
newHTML = strings.Replace(newHTML, "<li>"+`<span id="`+strconv.Itoa(i)+`" class="deletable"><del>`, "<li><del>"+`<span id="`+strconv.Itoa(i)+`" class="deletable">`, -1)
newHTML = strings.Replace(newHTML, "</del></span></li>", "</span></del></li>", -1)
listItems[i] = template.HTML([]byte(newHTML))
}
c.HTML(http.StatusOK, "list.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"ListItems": listItems,
"RecentlyEdited": getRecentlyEdited(title, c),
})
}
func deleteListItem(c *gin.Context) {
lineNum, err := strconv.Atoi(c.DefaultQuery("lineNum", "None"))
title := c.Query("title") // shortcut for c.Request.URL.Query().Get("lastname")
if err == nil {
var p WikiData
err := p.load(strings.ToLower(title))
if err != nil {
panic(err)
}
_, listItems := reorderList(p.CurrentText)
newText := p.CurrentText
for i, lineString := range listItems {
// fmt.Println(i, lineString, lineNum)
if i+1 == lineNum {
// fmt.Println("MATCHED")
if strings.Contains(lineString, "~~") == false {
// fmt.Println(p.Text, "("+lineString[2:]+"\n"+")", "~~"+lineString[2:]+"~~"+"\n")
newText = strings.Replace(newText+"\n", lineString[2:]+"\n", "~~"+strings.TrimSpace(lineString[2:])+"~~"+"\n", 1)
} else {
newText = strings.Replace(newText+"\n", lineString[2:]+"\n", lineString[4:len(lineString)-2]+"\n", 1)
}
p.save(newText)
break
}
}
c.JSON(200, gin.H{
"message": "Done.",
})
} else {
c.JSON(404, gin.H{
"message": "?",
})
}
}
func deletePage(c *gin.Context) {
deleteName := c.DefaultQuery("DeleteName", "None")
// if adminKey == RuntimeArgs.AdminKey || true == true {
if strings.ToLower(deleteName) != "help" {
p := WikiData{strings.ToLower(deleteName), "", []string{}, []string{}, false, ""}
p.save("")
}
// // remove from program data
// var deleteKey []byte
// foundKey := false
// err := db.View(func(tx *bolt.Tx) error {
// b := tx.Bucket([]byte("programdata"))
// c := b.Cursor()
// for k, v := c.First(); k != nil; k, v = c.Next() {
// if strings.ToLower(string(v)) == strings.ToLower(deleteName) {
// fmt.Println("FOUND " + string(v))
// deleteKey = k
// foundKey = true
// break
// }
// }
// return nil
// })
// if err != nil {
// panic(err)
// }
// if foundKey == true {
// fmt.Println(len([]string{}))
// fmt.Println(deleteKey)
// db.View(func(tx *bolt.Tx) error {
// b := tx.Bucket([]byte("programdata"))
// err := b.Delete(deleteKey)
// return err
// })
// }
// return OKAY
c.JSON(200, gin.H{
"message": "Done.",
})
// } else {
// c.JSON(404, gin.H{
// "message": "?",
// })
// }
}
func listEverything() string {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
everything := `| Title | Current size | Changes | Total Size | |
| --------- |-------------| -----| ------------- | ------------- |
`
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
b := tx.Bucket([]byte("datas"))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
var p WikiData
p.load(string(k))
if len(p.CurrentText) > 1 {
contentSize := strconv.Itoa(len(p.CurrentText))
numChanges := strconv.Itoa(len(p.Diffs))
totalSize := strconv.Itoa(len(v))
everything += "| [" + p.Title + "](/" + p.Title + "/view) | " + contentSize + " | " + numChanges + " | " + totalSize + ` | <a class="deleteable" id="` + p.Title + `">Delete</a> | ` + "\n"
}
}
return nil
})
return everything
}
func dumpEverything(folderpath string) {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
err := os.MkdirAll(folderpath, 0777)
if err != nil {
fmt.Printf("%s folder already exists.", folderpath)
}
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
b := tx.Bucket([]byte("datas"))
c := b.Cursor()
for k, _ := c.First(); k != nil; k, _ = c.Next() {
var p WikiData
p.load(string(k))
if len(p.CurrentText) > 0 {
ioutil.WriteFile(path.Join(folderpath, string(k)), []byte(p.CurrentText), 0644)
}
}
return nil
})
}

View File

@ -1,596 +0,0 @@
/*!
* Bootstrap v3.3.5 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
/*!
* Generated using the Bootstrap Customizer (https://getbootstrap.com/customize/?id=1d3a1642fef02a0b2ddb4ede6cb2cb3c)
* Config saved to config.json and https://gist.github.com/1d3a1642fef02a0b2ddb4ede6cb2cb3c
*/
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
text-shadow: 0 1px 0 #fff;
border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #265a88;
border-color: #245580;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
background-color: #419641;
background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-color: #e8e8e8;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-color: #2e6da4;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 318px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
}

File diff suppressed because one or more lines are too long

6762
static/css/bootstrap.css vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,338 +0,0 @@
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
.CodeMirror-guttermarker { color: black; }
.CodeMirror-guttermarker-subtle { color: #999; }
/* CURSOR */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0;
background: #7e7;
}
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-moz-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@-webkit-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror-overwrite .CodeMirror-cursor {}
.cm-tab { display: inline-block; text-decoration: inherit; }
.CodeMirror-ruler {
border-left: 1px solid #ccc;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
.CodeMirror-composing { border-bottom: 2px solid; }
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
.CodeMirror-scroll {
overflow: scroll !important; /* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
min-height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
/* Hack to make IE7 behave */
*zoom:1;
*display:inline;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
.CodeMirror-gutter-background {
position: absolute;
top: 0; bottom: 0;
z-index: 4;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-gutter-wrapper {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: none;
font-variant-ligatures: none;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {}
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-cursor { position: absolute; }
.CodeMirror-measure pre { position: static; }
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
div.CodeMirror-dragcursors {
visibility: visible;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
.CodeMirror span { *vertical-align: text-bottom; }
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }

Binary file not shown.

Binary file not shown.

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