pax_global_header00006660000000000000000000000064141551374630014523gustar00rootroot0000000000000052 comment=ccb493474debb2a84ee2ab60b292327426c86f8a pat-0.12.1/000077500000000000000000000000001415513746300123705ustar00rootroot00000000000000pat-0.12.1/.appveyor.yml000066400000000000000000000010551415513746300150370ustar00rootroot00000000000000platform: - x64 clone_folder: c:\gopath\src\github.com\la5nta\pat environment: global: GOPATH: C:\gopath GOVERSION: "1.16.5" MSYS_PATH: C:\MinGW\msys\1.0 install: - set PATH=C:\go\bin;%MSYS_PATH%\bin;C:\MinGW\bin;%PATH% - rmdir c:\go /s /q - appveyor DownloadFile https://dl.google.com/go/go%GOVERSION%.windows-386.zip - 7z x go%GOVERSION%.windows-386.zip -y -oC:\ > NUL build_script: - go version - '%MSYS_PATH%\bin\bash -lc "cd /c/gopath/src/github.com/la5nta/pat && ./make.bash"' artifacts: - path: pat.exe name: Pat pat-0.12.1/.gitignore000066400000000000000000000000141415513746300143530ustar00rootroot00000000000000.build/ pat pat-0.12.1/.gitmodules000066400000000000000000000000001415513746300145330ustar00rootroot00000000000000pat-0.12.1/.travis.yml000066400000000000000000000004121415513746300144760ustar00rootroot00000000000000language: go os: - linux - osx go: - 1.16.x - 1.x matrix: exclude: - os: osx go: 1.16.x install: - GO111MODULE=on go mod download - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./make.bash libax25; fi script: - go vet ./... - ./make.bash pat-0.12.1/CONTRIBUTING.md000066400000000000000000000062361415513746300146300ustar00rootroot00000000000000# Contributing to Pat We welcome contributions to Pat of any kind including documentation, tutorials, bug reports, issues, feature requests, feature implementation, pull requests, answering questions on the mailing list, helping to manage issues, etc. If you have any questions about how to contribute or what to contribute, please ask on the [pat-users](https://groups.google.com/group/pat-users) list. ## Issue tracker Guidelines We use github's [issue tracker](https://github.com/la5nta/pat/issues) for keeping track of bugs, features and technical development discussions. To keep the issue tracker nice and tidy, we ask for the following: - Keep one issue per topic: - Don't report multiple bugs in the same issue unless they closely relates to each other. - Open one issue per feature request. - When reporting a bug, please add the following: - Output of pat version (including the SHA). - Operating system and architecture. - What you expected to happen. - What actually happened (including full stack trace and/or error message). - Issues should not be closed until they are either discarded or deployed. This means that code changing issues should not be closed until the changes have been merged to the master branch. ## Code Contribution Guideline We welcome your contributions. To make the process as seamless as possible, we ask for the following: - Go ahead and fork the project and make your changes. We encourage pull requests to discuss code changes. - When you’re ready to create a pull request, be sure to: - Run `go fmt` - Consider squashing your commits into a single commit. `git rebase -i`. It's okay to force update your pull request. - **Write a good commit message.** This [blog article](http://chris.beams.io/posts/git-commit/) is a good resource for learning how to write good commit messages, the most important part being that each commit message should have a title/subject in imperative mood starting with a capital letter and no trailing period: *"Return error on wrong use of the Paginator"*, **NOT** *"returning some error."* Also, if your commit references one or more GitHub issues, always end your commit message body with *See #1234* or *Fixes #1234*. Replace *1234* with the GitHub issue ID. The last example will close the issue when the commit is merged into *master*. - Make sure `go test ./...` passes, and `go build` completes. Our [Travis CI loop](https://travis-ci.org/la5nta/pat) (Linux and OS X) will catch most things that are missing. ## The release process New releases of Pat is done by these steps: 1. All issues targeted by the next release are moved into a milestone with the corresponding version name. 2. A release/*-branch is prepared and VERSION.go is updated. 3. A pull request to *master* is opened. 4. The release-branch is built and tested on *all targeted platforms*. 5. If all status checks (Travis CI) passes, the release-branch is merged into *master* and tagged. 6. Issues in the targeted milestone is either closed or moved to another milestone. The milestone is closed. 7. The various binary packages are built and uploaded to [releases/](https://github.com/la5nta/Pat/releases). pat-0.12.1/LICENSE000066400000000000000000000021121415513746300133710ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2020 Martin Hebnes Pedersen (LA5NTA) 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. pat-0.12.1/README.md000066400000000000000000000070451415513746300136550ustar00rootroot00000000000000 [![Build Status](https://travis-ci.com/la5nta/pat.svg?branch=master)](https://travis-ci.com/la5nta/pat) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/tstq4suxfdmudl5l/branch/master?svg=true)](https://ci.appveyor.com/project/martinhpedersen/pat) [![Go Report Card](https://goreportcard.com/badge/github.com/la5nta/pat)](https://goreportcard.com/report/github.com/la5nta/pat) [![Liberapay Patreons](http://img.shields.io/liberapay/patrons/la5nta.svg?logo=liberapay)](https://liberapay.com/la5nta) ## Overview Pat is a cross platform Winlink client with basic messaging capabilities. It is the primary sandbox/prototype application for the [wl2k-go](https://github.com/la5nta/wl2k-go) project, and provides both a command line interface and a responsive (mobile-friendly) web interface. It is mainly developed for Linux, but is also known to run on OS X, Windows and Android. #### Features * Message composer/reader (basic mailbox functionality). * Auto-shrink image attachments. * Post position reports with location from local GPS, browser location or manual entry. * Rig control (using hamlib) for winmor PTT and QSY. * CRON-like syntax for execution of scheduled commands (e.g. QSY or connect). * Built in http-server with web interface (mobile friendly). * Git style command line interface. * Listen for P2P connections using multiple modes concurrently. * AX.25, telnet, WINMOR and ARDOP support. * Experimental gzip message compression (See "Gzip experiment" below). ##### Example ``` martinhpedersen@duo:~$ pat interactive > listen winmor,telnet-p2p,ax25 2015/02/03 10:33:10 Listening for incoming traffic (winmor,telnet-p2p,ax25)... > connect winmor:///LA3F 2015/02/03 10:34:28 Connecting to winmor:LA3F... 2015/02/03 10:34:33 Connected to WINMOR:LA3F RMS Trimode 1.3.3.0 Follo.SE Oslo. Pactor & Winmor Hybrid Gateway LA5NTA has 117 minutes remaining with LA3F [WL2K-2.8.4.8-B2FWIHJM$] Wien CMS via LA3F > >FF FC EM FOYNU8AKXX59 260 221 0 F> 68 1 proposal(s) received Accepting FOYNU8AKXX59 Receiving [//WL2K test til linux] [offset 0] >FF FQ Waiting for remote node to close the connection... > _ ``` ### Gzip experiment Gzip message compression has been added as an experimental B2F extension. The extension is implemented as a backwards compatible alternative to the ancient LZHUF compression. This experiment is enabled by default and sessions between two Pat nodes (or other software supporting this B2F extension) will use gzip compression when transferring messages. For more information, see . ## Copyright/License Copyright (c) 2020 Martin Hebnes Pedersen LA5NTA ### Contributors (alphabetical) * DL1THM - Torsten Harenberg * HB9GPA - Matthias Renner * K0SWE - Chris Keller * KD8DRX - Will Davidson * KE8HMG - Andrew Huebner * KI7RMJ - Rainer Grosskopf * LA3QMA - Kai Günter Brandt * LA4TTA - Erlend Grimseid * LA5NTA - Martin Hebnes Pedersen * W6IPA - JC Martin * WY2K - Benjamin Seidenberg * VE7GNU - Doug Collinge ## Thanks to The JNOS developers for the properly maintained lzhuf implementation, as well as the original author Haruyasu Yoshizaki. The paclink-unix team (Nicholas S. Castellano N2QZ and others) - reference implementation Amateur Radio Safety Foundation, Inc. - The Winlink 2000 project F6FBB Jean-Paul ROUBELAT - the FBB forwarding protocol _Pat/wl2k-go is not affiliated with The Winlink Development Team nor the Winlink 2000 project [http://winlink.org]._ pat-0.12.1/cfg/000077500000000000000000000000001415513746300131275ustar00rootroot00000000000000pat-0.12.1/cfg/config.go000066400000000000000000000217371415513746300147350ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package cfg import ( "encoding/json" "strings" "github.com/la5nta/wl2k-go/transport/ardop" ) const ( PlaceholderMycall = "{mycall}" ) type AuxAddr struct { Address string Password *string } func (a AuxAddr) MarshalJSON() ([]byte, error) { if a.Password == nil { return json.Marshal(a.Address) } return json.Marshal(a.Address + ":" + *a.Password) } func (a *AuxAddr) UnmarshalJSON(p []byte) error { var str string if err := json.Unmarshal(p, &str); err != nil { return err } parts := strings.SplitN(str, ":", 2) a.Address = parts[0] if len(parts) > 1 { a.Password = &parts[1] } return nil } type Config struct { // This station's callsign. MyCall string `json:"mycall"` // Secure login password used when a secure login challenge is received. // // The user is prompted if this is undefined. SecureLoginPassword string `json:"secure_login_password"` // Auxiliary callsigns to fetch email on behalf of. // // Passwords can optionally be specified by appending :MYPASS (e.g. EMCOMM-1:MyPassw0rd). // If no password is specified, the SecureLoginPassword is used. AuxAddrs []AuxAddr `json:"auxiliary_addresses"` // Maidenhead grid square (e.g. JP20qe). Locator string `json:"locator"` // List of service codes for rmslist (defaults to PUBLIC) ServiceCodes []string `json:"service_codes"` // Default HTTP listen address (for web UI). // // Use ":8080" to listen on any device, port 8080. HTTPAddr string `json:"http_addr"` // Handshake comment lines sent to remote node on incoming connections. // // Example: ["QTH: Hagavik, Norway. Operator: Martin", "Rig: FT-897 with Signalink USB"] MOTD []string `json:"motd"` // Connect aliases // // Example: {"LA1B-10": "ax25:///LD5GU/LA1B-10", "LA1B": "winmor://LA3F?freq=5350"} // Any occurrence of the substring "{mycall}" will be replaced with user's callsign. ConnectAliases map[string]string `json:"connect_aliases"` // Methods to listen for incoming P2P connections by default. // // Example: ["ax25", "winmor", "telnet", "ardop"] Listen []string `json:"listen"` // Hamlib rigs available (with reference name) for ptt and frequency control. HamlibRigs map[string]HamlibConfig `json:"hamlib_rigs"` AX25 AX25Config `json:"ax25"` // See AX25Config. SerialTNC SerialTNCConfig `json:"serial-tnc"` // See SerialTNCConfig. Winmor WinmorConfig `json:"winmor"` // See WinmorConfig. Ardop ArdopConfig `json:"ardop"` // See ArdopConfig. Pactor PactorConfig `json:"pactor"` // See PactorConfig. Telnet TelnetConfig `json:"telnet"` // See TelnetConfig. // See GPSdConfig. GPSd GPSdConfig `json:"gpsd"` // Legacy support for old config files only. This field is deprecated! // Please use "Addr" field in GPSd config struct (GPSd.Addr) GPSdAddrLegacy string `json:"gpsd_addr,omitempty"` // Command schedule (cron-like syntax). // // Examples: // # Connect to telnet once every hour // "@hourly": "connect telnet" // // # Change winmor listen frequency based on hour of day // "00 10 * * *": "freq winmor:7350.000", # 40m from 10:00 // "00 18 * * *": "freq winmor:5347.000", # 60m from 18:00 // "00 22 * * *": "freq winmor:3602.000" # 80m from 22:00 Schedule map[string]string `json:"schedule"` // By default, Pat posts your callsign and running version to the Winlink CMS Web Services // // Set to true if you don't want your information sent. VersionReportingDisabled bool `json:"version_reporting_disabled"` // Path to root of the Winlink Standard_Forms folder. // Unzip after downloading from winlink.org // // Deprecated: in favor of the --forms flag FormsPath string `json:"forms_path,omitempty"` } type HamlibConfig struct { // The network type ("serial" or "tcp"). Use 'tcp' for rigctld. // // (For serial support: build with "-tags libhamlib".) Network string `json:"network,omitempty"` // The rig address. // // For tcp (rigctld): "address:port" (e.g. localhost:4532). // For serial: "/path/to/tty?model=&baudrate=" (e.g. /dev/ttyS0?model=123&baudrate=4800). Address string `json:"address,omitempty"` // The rig's VFO to control ("A" or "B"). If empty, the current active VFO is used. VFO string `json:"VFO"` } type WinmorConfig struct { // Network address of the Winmor TNC (e.g. localhost:8500). Addr string `json:"addr"` // Bandwidth to use when getting an inbound connection (500/1600). InboundBandwidth int `json:"inbound_bandwidth"` // TX audio drive level // // Set to 0 to use WINMOR defaults DriveLevel int `json:"drive_level"` // (optional) Reference name to the Hamlib rig to control frequency and ptt. Rig string `json:"rig"` // Set to true if hamlib should control PTT (SignaLink=false, most rigexpert=true). PTTControl bool `json:"ptt_ctrl"` } type ArdopConfig struct { // Network address of the Ardop TNC (e.g. localhost:8515). Addr string `json:"addr"` // ARQ bandwidth (200/500/1000/2000 MAX/FORCED). ARQBandwidth ardop.Bandwidth `json:"arq_bandwidth"` // (optional) Reference name to the Hamlib rig to control frequency and ptt. Rig string `json:"rig"` // Set to true if hamlib should control PTT (SignaLink=false, most rigexpert=true). PTTControl bool `json:"ptt_ctrl"` // (optional) Send ID frame at a regular interval when the listener is active (unit is seconds) BeaconInterval int `json:"beacon_interval"` // Send FSK CW ID after an ID frame. CWID bool `json:"cwid_enabled"` } type PactorConfig struct { // Path/port to TNC device (e.g. /dev/ttyUSB0 or COM1). Path string `json:"path"` // Baudrate for the serial port (e.g. 57600). Baudrate int `json:"baudrate"` // (optional) Reference name to the Hamlib rig for frequency control. Rig string `json:"rig"` // (optional) Path to custom TNC initialization script. InitScript string `json:"custom_init_script"` } type TelnetConfig struct { // Network address (and port) to listen for telnet-p2p connections (e.g. :8774). ListenAddr string `json:"listen_addr"` // Telnet-p2p password. Password string `json:"password"` } type SerialTNCConfig struct { // Serial port (e.g. /dev/ttyUSB0 or COM1). Path string `json:"path"` // SerialBaud is the serial port's baudrate (e.g. 57600). SerialBaud int `json:"serial_baud"` // HBaud is the the packet connection's baudrate (1200 or 9600). HBaud int `json:"hbaud"` // Baudrate of the packet connection. // Deprecated: Use HBaud instead. BaudrateLegacy int `json:"baudrate,omitempty"` // Type of TNC (currently only 'kenwood'). Type string `json:"type"` } type AX25Config struct { // axport to use (as defined in /etc/ax25/axports). Port string `json:"port"` // Optional beacon when listening for incoming packet-p2p connections. Beacon BeaconConfig `json:"beacon"` // (optional) Reference name to the Hamlib rig for frequency control. Rig string `json:"rig"` } type BeaconConfig struct { // Beacon interval in seconds (e.g. 3600 for once every 1 hour) Every int `json:"every"` // (seconds) // Beacon data/message Message string `json:"message"` // Beacon destination (e.g. IDENT) Destination string `json:"destination"` } type GPSdConfig struct { // enable GPSd support in web interface // WARNING: If you enable GPSd http endpoint (enable_http) you might // expose your current position to anyone who has access to Pat!!! EnableHTTP bool `json:"enable_http"` // Use server time instead of timestamp provided by GPSd (e.g for older GPS // device with week roll-over issue) UseServerTime bool `json:"use_server_time"` // Address and port of GPSd server (e.g. localhost:2947) Addr string `json:"addr"` } var DefaultConfig = Config{ MOTD: []string{"Open source Winlink client - getpat.io"}, AuxAddrs: []AuxAddr{}, ServiceCodes: []string{"PUBLIC"}, ConnectAliases: map[string]string{ "telnet": "telnet://{mycall}:CMSTelnet@cms.winlink.org:8772/wl2k", }, Listen: []string{}, HTTPAddr: "localhost:8080", AX25: AX25Config{ Port: "wl2k", Beacon: BeaconConfig{ Every: 3600, Message: "Winlink P2P", Destination: "IDENT", }, }, SerialTNC: SerialTNCConfig{ Path: "/dev/ttyUSB0", SerialBaud: 9600, HBaud: 1200, Type: "Kenwood", }, Winmor: WinmorConfig{ Addr: "localhost:8500", InboundBandwidth: 1600, }, Ardop: ArdopConfig{ Addr: "localhost:8515", ARQBandwidth: ardop.Bandwidth500Max, CWID: true, }, Pactor: PactorConfig{ Path: "/dev/ttyUSB0", Baudrate: 57600, }, Telnet: TelnetConfig{ ListenAddr: ":8774", Password: "", }, GPSd: GPSdConfig{ EnableHTTP: false, // Default to false to help protect privacy of unknowing users (see github.com//issues/146) UseServerTime: false, Addr: "localhost:2947", // Default listen address for GPSd }, GPSdAddrLegacy: "", Schedule: map[string]string{}, HamlibRigs: map[string]HamlibConfig{}, // Path to root of the unzipped Winlink Standard_Forms folder. FormsPath: "", } pat-0.12.1/cfg/example_config.json000066400000000000000000000022071415513746300170030ustar00rootroot00000000000000{ "mycall": "LA5NTA", "secure_login_password": "my_not_so_secret_password", "auxiliary_addresses": ["LE1OF"], "locator": "JP20qe", "connect_aliases": { "LA1B@60m": "winmor:///LA1B?freq=5310.00", "LA1B-10": "ax25:///LA1B-10" }, "hamlib_rigs": { "ft897": { "network": "tcp", "address": "localhost:4532" } }, "ax25": { "port": "tmd710", "beacon": { "every": 3600, "message": "Winlink P2P", "destination": "IDENT" } }, "serial-tnc": { "path": "/dev/ttyUSB0", "hbaud": 1200, "serial_baud": 9600, "type": "Kenwood" }, "winmor": { "addr": "la5nta.local.mesh:8500", "inbound_bandwidth": 500, "rig": "ft897", "ptt_ctrl": false }, "ardop": { "addr": "localhost:8515", "arq_bandwidth": {"Forced":false, "Max":500}, "beacon_interval": 0, "cwid_enabled": true, "rig": "ft897", "ptt_ctrl": false }, "telnet": { "listen_addr": ":8774", "password": "" }, "schedule": { "*/30 * * * *": "connect offgrid" }, "gpsd": { "enable_http": false, "use_server_time": false, "addr": "localhost:2947" } } pat-0.12.1/cli_composer.go000066400000000000000000000175541415513746300154110ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. // A portable Winlink client for amateur radio email. package main import ( "bufio" "bytes" "fmt" "io" "io/ioutil" "log" "os" "os/exec" "path/filepath" "strings" "time" "github.com/la5nta/pat/internal/buildinfo" "github.com/la5nta/wl2k-go/fbb" "github.com/spf13/pflag" ) func composeMessageHeader(replyMsg *fbb.Message) *fbb.Message { msg := fbb.NewMessage(fbb.Private, fOptions.MyCall) fmt.Printf(`From [%s]: `, fOptions.MyCall) from := readLine() if from == "" { from = fOptions.MyCall } msg.SetFrom(from) fmt.Print(`To`) if replyMsg != nil { fmt.Printf(" [%s]", replyMsg.From()) } fmt.Printf(": ") to := readLine() if to == "" && replyMsg != nil { msg.AddTo(replyMsg.From().String()) } else { for _, addr := range strings.FieldsFunc(to, SplitFunc) { msg.AddTo(addr) } } ccCand := make([]fbb.Address, 0) if replyMsg != nil { for _, addr := range append(replyMsg.To(), replyMsg.Cc()...) { if !addr.EqualString(fOptions.MyCall) { ccCand = append(ccCand, addr) } } } fmt.Printf("Cc") if replyMsg != nil { fmt.Printf(" %s", ccCand) } fmt.Print(`: `) cc := readLine() if cc == "" && replyMsg != nil { for _, addr := range ccCand { msg.AddCc(addr.String()) } } else { for _, addr := range strings.FieldsFunc(cc, SplitFunc) { msg.AddCc(addr) } } switch len(msg.Receivers()) { case 1: fmt.Print("P2P only [y/N]: ") ans := readLine() if strings.EqualFold("y", ans) { msg.Header.Set("X-P2POnly", "true") } case 0: fmt.Println("Message must have at least one recipient") os.Exit(1) } fmt.Print(`Subject: `) if replyMsg != nil { subject := strings.TrimSpace(strings.TrimPrefix(replyMsg.Subject(), "Re:")) subject = fmt.Sprintf("Re:%s", subject) fmt.Println(subject) msg.SetSubject(subject) } else { msg.SetSubject(readLine()) } // A message without subject is not valid, so let's use a sane default if msg.Subject() == "" { msg.SetSubject("") } return msg } func composeMessage(args []string) { set := pflag.NewFlagSet("compose", pflag.ExitOnError) subject := set.StringP("subject", "s", "", "") attachments := set.StringArrayP("attachment", "a", nil, "") ccs := set.StringArrayP("cc", "c", nil, "") set.Parse(args) // Remaining args are recipients recipients := []string{} for _, r := range set.Args() { // Filter out empty args (this actually happens) if strings.TrimSpace(r) == "" { continue } recipients = append(recipients, r) } // Check if any args are set. If so, go non-interactive // Otherwise, interactive if (len(*subject) + len(*attachments) + len(*ccs) + len(recipients)) > 0 { noninteractiveComposeMessage(*subject, *attachments, *ccs, recipients) } else { interactiveComposeMessage(nil) } } func noninteractiveComposeMessage(subject string, attachments []string, ccs []string, recipients []string) { // We have to verify the args here. Follow the same pattern as main() // We'll allow a missing recipient if CC is present (or vice versa) if len(recipients)+len(ccs) <= 0 { fmt.Fprint(os.Stderr, "ERROR: Missing recipients in non-interactive mode!\n") os.Exit(1) } // Subject is optional. Print a mailx style warning if subject == "" { fmt.Fprint(os.Stderr, "Warning: missing subject; hope that's OK\n") } msg := fbb.NewMessage(fbb.Private, fOptions.MyCall) msg.SetFrom(fOptions.MyCall) for _, to := range recipients { msg.AddTo(to) } for _, cc := range ccs { msg.AddCc(cc) } msg.SetSubject(subject) // Handle Attachments. Since we're not interactive, treat errors as fatal so the user can fix for _, filename := range attachments { file, err := readAttachment(filename) if err != nil { fmt.Fprint(os.Stderr, err.Error()+"\nAborting! (Message not posted)\n") os.Exit(1) } msg.AddFile(file) } // Read the message body from stdin body, _ := ioutil.ReadAll(os.Stdin) if len(body) == 0 { // Yeah, I've spent way too much time using mail(1) fmt.Fprint(os.Stderr, "Null message body; hope that's ok\n") } msg.SetBody(string(body)) postMessage(msg) } // This is currently an alias for interactiveComposeMessage but keeping as a seperate // call path for the future func composeReplyMessage(replyMsg *fbb.Message) { interactiveComposeMessage(replyMsg) } func interactiveComposeMessage(replyMsg *fbb.Message) { msg := composeMessageHeader(replyMsg) // Read body fmt.Printf(`Press ENTER to start composing the message body. `) readLine() f, err := ioutil.TempFile("", strings.ToLower(fmt.Sprintf("%s_new_%d.txt", buildinfo.AppName, time.Now().Unix()))) if err != nil { log.Fatalf("Unable to prepare temporary file for body: %s", err) } if replyMsg != nil { fmt.Fprintf(f, "--- %s %s wrote: ---\n", replyMsg.Date(), replyMsg.From().Addr) body, _ := replyMsg.Body() orig := ">" + strings.ReplaceAll( strings.TrimSpace(body), "\n", "\n>", ) + "\n" f.Write([]byte(orig)) f.Sync() } // Windows fix: Avoid 'cannot access the file because it is being used by another process' error. // Close the file before opening the editor. f.Close() cmd := exec.Command(EditorName(), f.Name()) cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr if err := cmd.Run(); err != nil { log.Fatalf("Unable to start body editor: %s", err) } f, err = os.OpenFile(f.Name(), os.O_RDWR, 0o666) if err != nil { log.Fatalf("Unable to read temporary file from editor: %s", err) } var buf bytes.Buffer io.Copy(&buf, f) msg.SetBody(buf.String()) f.Close() os.Remove(f.Name()) // An empty message body is illegal. Let's set a sane default. if msg.BodySize() == 0 { msg.SetBody("\n") } // END Read body fmt.Print("\n") for { fmt.Print(`Attachment [empty when done]: `) path := readLine() if path == "" { break } file, err := readAttachment(path) if err != nil { log.Println(err) continue } msg.AddFile(file) } fmt.Println(msg) postMessage(msg) } func readAttachment(path string) (*fbb.File, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() name := filepath.Base(path) var resizeImage bool if isImageMediaType(name, "") { fmt.Print("This seems to be an image. Auto resize? [Y/n]: ") ans := readLine() resizeImage = ans == "" || strings.EqualFold("y", ans) } var data []byte data, err = ioutil.ReadAll(f) if resizeImage { data, err = convertImage(data) ext := filepath.Ext(name) name = name[:len(name)-len(ext)] + ".jpg" } return fbb.NewFile(name, data), err } var stdin *bufio.Reader func readLine() string { if stdin == nil { stdin = bufio.NewReader(os.Stdin) } str, _ := stdin.ReadString('\n') return strings.TrimSpace(str) } func composeFormReport(args []string) { var tmplPathArg string set := pflag.NewFlagSet("form", pflag.ExitOnError) set.StringVar(&tmplPathArg, "template", "ICS USA Forms/ICS213", "") set.Parse(args) msg := composeMessageHeader(nil) formMsg, err := formsMgr.ComposeForm(tmplPathArg, msg.Subject()) if err != nil { log.Printf("failed to compose message for template %s", tmplPathArg) return } msg.SetSubject(formMsg.Subject) fmt.Println("================================================================") fmt.Print("To: ") fmt.Println(msg.To()) fmt.Print("Cc: ") fmt.Println(msg.Cc()) fmt.Print("From: ") fmt.Println(msg.From()) fmt.Println("Subject: " + msg.Subject()) fmt.Println(formMsg.Body) fmt.Println("================================================================") fmt.Println("Press ENTER to post this message in the outbox, Ctrl-C to abort.") fmt.Println("================================================================") readLine() msg.SetBody(formMsg.Body) attachmentFile := fbb.NewFile(formMsg.AttachmentName, []byte(formMsg.AttachmentXML)) msg.AddFile(attachmentFile) postMessage(msg) } pat-0.12.1/config.go000066400000000000000000000067141415513746300141740ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "bytes" "encoding/json" "fmt" "log" "os" "path" "path/filepath" "strings" "github.com/la5nta/pat/cfg" "github.com/la5nta/pat/internal/debug" ) func LoadConfig(cfgPath string, fallback cfg.Config) (config cfg.Config, err error) { config, err = ReadConfig(cfgPath) if os.IsNotExist(err) { return fallback, WriteConfig(fallback, cfgPath) } else if err != nil { return config, err } // Ensure the alias "telnet" exists if config.ConnectAliases == nil { config.ConnectAliases = make(map[string]string) } if _, exists := config.ConnectAliases["telnet"]; !exists { config.ConnectAliases["telnet"] = cfg.DefaultConfig.ConnectAliases["telnet"] } // Ensure ServiceCodes has a default value if len(config.ServiceCodes) == 0 { config.ServiceCodes = cfg.DefaultConfig.ServiceCodes } // Ensure Pactor has a default value if config.Pactor == (cfg.PactorConfig{}) { config.Pactor = cfg.DefaultConfig.Pactor } // TODO: Remove after some release cycles (2019-09-29) if config.GPSdAddrLegacy != "" { config.GPSd.Addr = config.GPSdAddrLegacy } // TODO: config FormsPath is deprecated in favor of --forms flag homeDir, _ := os.UserHomeDir() formsOldDefault := filepath.Join(homeDir, ".wl2k", "Standard_Forms") // Ignore the old default config value if config.FormsPath == formsOldDefault { config.FormsPath = "" } if config.FormsPath != "" { log.Println("Using deprecated configuration option 'forms_path'. Please use --forms flag instead.") // clean up FormsPath (normalizes trailing slashes, and embedded '.' ) config.FormsPath = filepath.Clean(config.FormsPath) config.FormsPath = strings.ReplaceAll(config.FormsPath, "\\", "/") } // Compatibility for the old baudrate field for serial-tnc if v := config.SerialTNC.BaudrateLegacy; v != 0 && config.SerialTNC.HBaud == 0 { debug.Printf("Legacy serial_tnc.baudrate config detected (%d). Translating to serial_tnc.hbaud.", v) config.SerialTNC.HBaud = v config.SerialTNC.BaudrateLegacy = 0 } return config, nil } func replaceDeprecatedCMSHostname(path string, data []byte) ([]byte, error) { const o = "@server.winlink.org:8772/wl2k" const n = "@cms.winlink.org:8772/wl2k" if !bytes.Contains(data, []byte(o)) { return data, nil } data = bytes.ReplaceAll(data, []byte(o), []byte(n)) f, err := os.Open(path) if err != nil { return data, err } stat, err := f.Stat() f.Close() if err != nil { return data, err } return data, os.WriteFile(path, data, stat.Mode()) } func ReadConfig(path string) (config cfg.Config, err error) { data, err := os.ReadFile(path) if err != nil { return } // TODO: Remove after some release cycles (2017-11-09) data, err = replaceDeprecatedCMSHostname(path, data) if err != nil { fmt.Println("Failed to rewrite deprecated CMS hostname:", err) fmt.Println("Please update your config's 'telnet' connect alias manually to:") fmt.Println(cfg.DefaultConfig.ConnectAliases["telnet"]) fmt.Println("") } err = json.Unmarshal(data, &config) return } func WriteConfig(config cfg.Config, filePath string) error { b, err := json.MarshalIndent(config, "", " ") if err != nil { return err } // Add trailing new-line b = append(b, '\n') // Ensure path dir is available os.Mkdir(path.Dir(filePath), os.ModePerm|os.ModeDir) return os.WriteFile(filePath, b, 0o600) } pat-0.12.1/connect.go000066400000000000000000000160471415513746300143600ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "fmt" "log" "strconv" "strings" "time" "github.com/harenber/ptc-go/v2/pactor" "github.com/la5nta/wl2k-go/transport" "github.com/la5nta/wl2k-go/transport/ardop" "github.com/la5nta/wl2k-go/transport/winmor" // Register other dialers _ "github.com/la5nta/wl2k-go/transport/ax25" _ "github.com/la5nta/wl2k-go/transport/telnet" ) var ( dialing *transport.URL // The connect URL currently being dialed (if any) wmTNC *winmor.TNC // Pointer to the WINMOR TNC used by Listen and Connect adTNC *ardop.TNC // Pointer to the ARDOP TNC used by Listen and Connect pModem *pactor.Modem ) func hasSSID(str string) bool { return strings.Contains(str, "-") } func connectAny(connectStr ...string) bool { for _, str := range connectStr { if Connect(str) { return true } } return false } func Connect(connectStr string) (success bool) { if connectStr == "" { return false } else if aliased, ok := config.ConnectAliases[connectStr]; ok { return Connect(aliased) } url, err := transport.ParseURL(connectStr) if err != nil { log.Println(err) return false } // Init TNCs switch url.Scheme { case MethodArdop: if err := initArdopTNC(); err != nil { log.Println(err) return } case MethodWinmor: if err := initWinmorTNC(); err != nil { log.Println(err) return } case MethodPactor: ptCmdInit := "" if val, ok := url.Params["init"]; ok { ptCmdInit = strings.Join(val, "\n") } if err := initPactorModem(ptCmdInit); err != nil { log.Println(err) return } } // Set default userinfo (mycall) if url.User == nil { url.SetUser(fOptions.MyCall) } // Set default host interface address if url.Host == "" { switch url.Scheme { case MethodAX25: url.Host = config.AX25.Port case MethodSerialTNC: url.Host = config.SerialTNC.Path if hbaud := config.SerialTNC.HBaud; hbaud > 0 { url.Params.Set("hbaud", fmt.Sprint(hbaud)) } if sbaud := config.SerialTNC.SerialBaud; sbaud > 0 { url.Params.Set("serial_baud", fmt.Sprint(sbaud)) } } } // Radio Only? radioOnly := fOptions.RadioOnly if v := url.Params.Get("radio_only"); v != "" { radioOnly, _ = strconv.ParseBool(v) } if radioOnly { if hasSSID(fOptions.MyCall) { log.Println("Radio Only does not support callsign with SSID") return } switch url.Scheme { case MethodAX25, MethodSerialTNC: log.Printf("Radio-Only is not available for %s", url.Scheme) return default: url.SetUser(url.User.Username() + "-T") } } // QSY var revertFreq func() if freq := url.Params.Get("freq"); freq != "" { revertFreq, err = qsy(url.Scheme, freq) if err != nil { log.Printf("Unable to QSY: %s", err) return } defer revertFreq() } var currFreq Frequency if vfo, _, ok, _ := VFOForTransport(url.Scheme); ok { f, _ := vfo.GetFreq() currFreq = Frequency(f) } // Wait for a clear channel switch url.Scheme { case MethodArdop: waitBusy(adTNC) case MethodWinmor: waitBusy(wmTNC) } // Catch interrupts (signals) while dialing, so users can abort ardop/winmor connects. doneHandleInterrupt := handleInterrupt() // Signal web gui that we are dialing a connection dialing = url websocketHub.UpdateStatus() log.Printf("Connecting to %s (%s)...", url.Target, url.Scheme) conn, err := transport.DialURL(url) // Signal web gui that we are no longer dialing dialing = nil websocketHub.UpdateStatus() close(doneHandleInterrupt) eventLog.LogConn("connect "+connectStr, currFreq, conn, err) if err != nil { log.Printf("Unable to establish connection to remote: %s", err) return } err = exchange(conn, url.Target, false) if err != nil { log.Printf("Exchange failed: %s", err) } else { log.Println("Disconnected.") success = true } return } func qsy(method, addr string) (revert func(), err error) { noop := func() {} rig, rigName, ok, err := VFOForTransport(method) if err != nil { return noop, err } else if !ok { return noop, fmt.Errorf("hamlib rig '%s' not loaded", rigName) } log.Printf("QSY %s: %s", method, addr) _, oldFreq, err := setFreq(rig, addr) if err != nil { return noop, err } time.Sleep(3 * time.Second) return func() { time.Sleep(time.Second) log.Printf("QSX %s: %.3f", method, float64(oldFreq)/1e3) rig.SetFreq(oldFreq) }, nil } func waitBusy(b transport.BusyChannelChecker) { printed := false for b.Busy() { if !printed && fOptions.IgnoreBusy { log.Println("Ignoring busy channel!") break } else if !printed { log.Println("Waiting for clear channel...") printed = true } time.Sleep(300 * time.Millisecond) } } func initWinmorTNC() error { if wmTNC != nil && wmTNC.Ping() == nil { return nil } if wmTNC != nil { wmTNC.Close() } var err error wmTNC, err = winmor.Open(config.Winmor.Addr, fOptions.MyCall, config.Locator) if err != nil { return fmt.Errorf("WINMOR TNC initialization failed: %w", err) } if config.Winmor.DriveLevel != 0 { if err := wmTNC.SetDriveLevel(config.Winmor.DriveLevel); err != nil { log.Println("Failed to set WINMOR drive level:", err) } } if v, err := wmTNC.Version(); err != nil { return fmt.Errorf("WINMOR TNC initialization failed: %s", err) } else { log.Printf("WINMOR TNC v%s initialized", v) } transport.RegisterDialer(MethodWinmor, wmTNC) if !config.Winmor.PTTControl { return nil } rig, ok := rigs[config.Winmor.Rig] if !ok { return fmt.Errorf("unable to set PTT rig '%s': not defined or not loaded", config.Winmor.Rig) } wmTNC.SetPTT(rig) return nil } func initArdopTNC() error { if adTNC != nil && adTNC.Ping() == nil { return nil } if adTNC != nil { adTNC.Close() } var err error adTNC, err = ardop.OpenTCP(config.Ardop.Addr, fOptions.MyCall, config.Locator) if err != nil { return fmt.Errorf("ARDOP TNC initialization failed: %w", err) } if !config.Ardop.ARQBandwidth.IsZero() { if err := adTNC.SetARQBandwidth(config.Ardop.ARQBandwidth); err != nil { return fmt.Errorf("unable to set ARQ bandwidth for ardop TNC: %w", err) } } if err := adTNC.SetCWID(config.Ardop.CWID); err != nil { return fmt.Errorf("unable to configure CWID for ardop TNC: %w", err) } if v, err := adTNC.Version(); err != nil { return fmt.Errorf("ARDOP TNC initialization failed: %s", err) } else { log.Printf("ARDOP TNC (%s) initialized", v) } transport.RegisterDialer(MethodArdop, adTNC) if !config.Ardop.PTTControl { return nil } rig, ok := rigs[config.Ardop.Rig] if !ok { return fmt.Errorf("unable to set PTT rig '%s': Not defined or not loaded", config.Ardop.Rig) } adTNC.SetPTT(rig) return nil } func initPactorModem(cmdlineinit string) error { if pModem != nil { pModem.Close() } var err error pModem, err = pactor.OpenModem(config.Pactor.Path, config.Pactor.Baudrate, fOptions.MyCall, config.Pactor.InitScript, cmdlineinit) if err != nil || pModem == nil { return fmt.Errorf("pactor initialization failed: %w", err) } transport.RegisterDialer(MethodPactor, pModem) return nil } pat-0.12.1/convert_image.go000066400000000000000000000020771415513746300155470ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "bytes" "image" _ "image/gif" "image/jpeg" _ "image/png" "mime" "path" "strings" "github.com/nfnt/resize" ) func isImageMediaType(filename, contentType string) bool { var mediaType string if contentType != "" { mediaType, _, _ = mime.ParseMediaType(contentType) } if mediaType == "" { mediaType = mime.TypeByExtension(path.Ext(filename)) } return strings.HasPrefix(mediaType, "image/") } func convertImage(orig []byte) ([]byte, error) { img, _, err := image.Decode(bytes.NewReader(orig)) if err != nil { return nil, err } // Scale down if img.Bounds().Dx() > 600 { img = resize.Resize(600, 0, img, resize.NearestNeighbor) } // Re-encode as low quality jpeg var buf bytes.Buffer if err := jpeg.Encode(&buf, img, &jpeg.Options{Quality: 40}); err != nil { return orig, err } if buf.Len() >= len(orig) { return orig, nil } return buf.Bytes(), nil } pat-0.12.1/debian/000077500000000000000000000000001415513746300136125ustar00rootroot00000000000000pat-0.12.1/debian/.gitignore000066400000000000000000000000531415513746300156000ustar00rootroot00000000000000pat/ files pat.debhelper.log pat.substvars pat-0.12.1/debian/changelog000066400000000000000000000314531415513746300154720ustar00rootroot00000000000000pat (0.12.1) stable; urgency=medium * Add support for configurable telnet dial timeout (for Iridium GO users) * Add support for scriptable message composition * Add CLI command `env` for retrieving related environment variables (for scripting) * More reliable Forms updates by using a new API for retrieving latest version and archive URL * Improve websocket handling * Fix bug in Forms update procedure that would delete the OS temp directory in rare cases * Fix bug with pactor serial communication on macOS (Darwin) * Fix bug with Web GUI and Message IDs containing the hash (`#`) symbol -- Martin Hebnes Pedersen Sat, 11 Dec 2021 15:14:22 +0100 pat (0.12.0) stable; urgency=medium * Follow the XDG Base Directory Specification * Add support for sending in precedence order * Add new serial-tnc baudrate configuration options * Fix bug in forms parsing leading to missing forms * Fix permissions issue when updating forms * Fix FBB protocol handshake issue * Improve fsnotify handling for mailbox events * More descriptive error on premature disconnect * Add basic debug logging capabilities * Various dependency updates and refactoring -- Martin Hebnes Pedersen Sun, 31 Oct 2021 17:28:02 +0100 pat (0.11.0) stable; urgency=medium * Add support for Winlink HTML Forms * Add support for individual passords for auxiliary addresses * Add ability to abort ongoing dialing/connection in Web GUI * Add systemd unit file for rigctld * Improve version reporing to Winlink API * Improve websocket handling * Improve visibility of QSY errors in Web GUI * Improve 'reply' and 'forward' functionality in Web GUI * Fix issue with azimuth calculation when distance is zero * Fix incorrect transport URI scheme for packet nodes * Fix build on FreeBSD and macOS. * Avoid truncating rmslist cache on refresh failure * Avoid recompressing images where the resulting file size increases * Require Go 1.16 or later -- Martin Hebnes Pedersen Wed, 30 Jun 2021 21:13:40 +0100 pat (0.10.0) stable; urgency=medium * Add support for P4 Dragon modems * Add RMS list viewer in Web GUI's connect modal * Add support for additional connect parameters for pactor * New max length of message attachment filenames (255 characters) -- Martin Hebnes Pedersen Thu, 08 Sep 2020 19:39:40 +0100 pat (0.9.0) stable; urgency=medium * Less aggressive websocket timeout * Add column sorting in Web GUI * Require Go 1.10 or later * Fix GPSd config bug introduced in v0.8.0 * Fix (mainly macOS) bug related to many open file descriptors -- Martin Hebnes Pedersen Wed, 19 Feb 2020 20:13:18 +0100 pat (0.8.0) stable; urgency=medium * GPSd support in Web GUI * User configurable Service Code * High Accuracy HTML5 Geolocation * Minor PACTOR enhancements and bug fixes * Fixed ARDOP listener issue -- Martin Hebnes Pedersen Thu, 03 Oct 2019 21:48:51 +0200 pat (0.7.0) stable; urgency=medium * Support PACTOR PTC-II and PTC-III (https://github.com/la5nta/pat/issues/40) * Fix QSY frequency rounding error (https://github.com/la5nta/pat/issues/147) * Fix panic on ARDOP TNC connection teardown (https://github.com/la5nta/pat/issues/137) * Fix ARDOP compatibility issue (https://github.com/la5nta/pat/issues/139) -- Martin Hebnes Pedersen Wed, 18 Sep 2019 21:56:17 +0200 pat (0.6.1) stable; urgency=medium * Add deb package `dist` as conflicting package (https://github.com/la5nta/pat/issues/131) * Include systemd unit file for ARDOPc (https://github.com/la5nta/pat/issues/130) * Set correct URL parameter for serial-tnc.Baudrate (https://github.com/la5nta/pat/issues/129) * Fix Go 1.10 compatibility issue (https://github.com/la5nta/pat/issues/121) -- Martin Hebnes Pedersen Sun, 21 Apr 2018 11:23:40 +0200 pat (0.6.0) stable; urgency=high * Support Winlink's new mixed-case password scheme (https://github.com/la5nta/pat/issues/113) * Support for distance and azumuth in rmslist (https://github.com/la5nta/pat/pull/112) * Improved ARDOP ID-frame parser -- Martin Hebnes Pedersen Mon, 22 Jan 2018 21:41:13 +0100 pat (0.5.1) stable; urgency=medium * Support ARDOP >= v1.0 (https://github.com/la5nta/pat/issues/108) * Add rmslist support for ARDOP nodes * Switch to the new Winlink rest API (https://github.com/la5nta/pat/issues/110) * Fix bug which caused WINMOR connection failure when dialing the (non-idle) TNC -- Martin Hebnes Pedersen Tue, 12 Dec 2017 19:03:04 +0100 pat (0.5.0) stable; urgency=high * Fix XSS vulnerability when serving attachments over HTTP (https://github.com/la5nta/pat/issues/105) * Gracefully recover/initialize failed external devices (https://github.com/la5nta/pat/issues/88) * Switch to the new Winlink CMS and API hostname (https://github.com/la5nta/pat/issues/104) * Add config option for WINMOR's Drive Level parameter (https://github.com/la5nta/pat/issues/99) * Add password prompt in web GUI (https://github.com/la5nta/pat/issues/90) * Include man pages in deb and pkg packages (https://github.com/la5nta/pat/pull/91) * Various minor web GUI improvements (https://github.com/la5nta/pat/issues/97) -- Martin Hebnes Pedersen Sat, 18 Nov 2017 11:40:28 +0100 pat (0.4.0) stable; urgency=medium * Desktop notifications for web GUI users (https://github.com/la5nta/pat/issues/85) * New status indicator in web GUI for display of various alerts and info (https://github.com/la5nta/pat/issues/86) * Add Cc field to the web GUI composer (https://github.com/la5nta/pat/issues/83) * Tokenize address input in the web GUI composer (https://github.com/la5nta/pat/issues/84) * Check for empty To/Cc on compose (https://github.com/la5nta/pat/issues/89) -- Martin Hebnes Pedersen Tue, 17 Sep 2017 11:14:59 +0200 pat (0.3.0) stable; urgency=high (Fixes compatibility with an upcoming Winlink CMS release) * Fix critical compatibility issues with WL2K-4.0 aka "AWS-CMS" (https://github.com/la5nta/pat/issues/81) * Fix close of AX.25 listener on Linux (https://github.com/la5nta/pat/issues/68) * Add "Delete" and "Move to archive" actions in web GUI (https://github.com/la5nta/pat/issues/63) -- Martin Hebnes Pedersen Tue, 18 Jul 2017 21:13:08 +0200 pat (0.2.4) stable; urgency=medium * Add progress bar for message transfer in web GUI (https://github.com/la5nta/pat/pull/78) * Properly parse offset in B2 compressed message header for BPQ compatibility (https://github.com/la5nta/pat/issues/74) * Fix libax25 segfault on invalid axport (https://github.com/la5nta/pat/issues/73) * Silence FREQUENCY parse errors for ardop (https://github.com/la5nta/pat/issues/75) -- Martin Hebnes Pedersen Tue, 28 Feb 2017 19:07:00 +0100 pat (0.2.3) stable; urgency=medium * Support ARDOP >= v0.9 (https://github.com/la5nta/pat/issues/69) * Improve list parsing in various UI fields * Handle non-ascii attachment names -- Martin Hebnes Pedersen Fri, 27 Jan 2016 18:17:30 +0100 pat (0.2.2) stable; urgency=medium * Ensure default config is written before opening the configuration editor (https://github.com/la5nta/pat/issues/70) * Add some missing config defaults -- Martin Hebnes Pedersen Thu, 01 Dec 2016 18:14:09 +0100 pat (0.2.1) stable; urgency=medium * Support ARDOP >= v0.6 (https://github.com/la5nta/pat/issues/60) * Fix bug that caused 'configure' to fail if config format was invalid (https://github.com/la5nta/pat/issues/62) * Add position format examples for --latlon (https://github.com/la5nta/pat/issues/65) * Statically link libax25 (linux) to avoid crash on incompatible shared library (https://github.com/la5nta/pat/issues/59) -- Martin Hebnes Pedersen Wed, 12 Oct 2016 20:24:18 +0200 pat (0.2.0) stable; urgency=medium * Support Radio only - Winlink Hybrid Network (https://github.com/la5nta/pat/issues/44) * Swich to Go port of lzhuf (https://github.com/la5nta/pat/issues/50) * Linux ax25 scripts: Add method for custom TNC initialization (https://github.com/la5nta/pat/issues/53) * Fix ardop PTT rigcontrol (https://github.com/la5nta/pat/issues/58) * Minor bug fixes and improvements in the web GUI -- Martin Hebnes Pedersen Fri, 05 Aug 2016 15:16:51 +0200 pat (0.1.5) stable; urgency=medium * Fix bug that caused command-line interface composer's prompt scan to see whitespace as end of line (https://github.com/la5nta/pat/issues/45) * Fix Mac OS default install path (https://github.com/la5nta/pat/issues/47) -- Martin Hebnes Pedersen Mon, 27 Jun 2016 22:43:36 +0200 pat (0.1.4) stable; urgency=medium * Fix case where secure_login_password was ignored if mycall was not all upper case (https://github.com/la5nta/pat/issues/42) * Support image resize in cli composer (https://github.com/la5nta/pat/issues/38) * Remove imagemagick dependency for image resize (https://github.com/la5nta/pat/issues/13) * Minor improvement of cli mailbox navigation (https://github.com/la5nta/pat/issues/39) -- Martin Hebnes Pedersen Thu, 09 Jun 2016 21:02:42 +0200 pat (0.1.3) stable; urgency=medium * Add filename extension for mailbox messages (https://github.com/la5nta/pat/issues/34) * Fix broken ax25:// digipeater syntax (https://github.com/la5nta/pat/issues/33) * Enable gzip experiment by default (https://github.com/la5nta/pat/issues/29) -- Martin Hebnes Pedersen Sat, 07 May 2016 22:18:12 +0200 pat (0.1.2) stable; urgency=medium * Fix callsign casing bug (https://github.com/la5nta/pat/issues/19) * Fix web composer Re: prefix issues in replies (https://github.com/la5nta/pat/issues/30) * Support running http server while in interactive mode (https://github.com/la5nta/pat/issues/26) * Send smallest messages first (suggested in the Winlink FAQ) (https://github.com/la5nta/pat/issues/25) * Fix handling of proposal code H (https://github.com/la5nta/pat/issues/25) * Fix handling of blocks with all messages deferred/rejected (https://github.com/la5nta/pat/issues/25) * Fix unstable serialization of messages that could result in corrupt partial message transfer (https://github.com/la5nta/pat/issues/25) * Support both utf8 and iso-8859-1 encoded subject header (https://github.com/la5nta/pat/issues/23) * Re-implement ctrl+c for aborting connect/session (https://github.com/la5nta/pat/issues/22) * Fix GUI post button issues on some browsers (https://github.com/la5nta/pat/issues/21) * Fix WINMOR unexpected EOF issue on session termination (https://github.com/la5nta/pat/issues/20) * Fix improper handling of callsign casing (https://github.com/la5nta/pat/issues/19) -- Martin Hebnes Pedersen Sat, 02 Apr 2016 10:41:16 +0200 pat (0.1.1) stable; urgency=medium * Fix various file locking errors on Windows (https://github.com/la5nta/pat/issues/9). * Automatic version reporting to Winlink CMS Web Services. -- Martin Hebnes Pedersen Fri, 11 Mar 2016 21:06:16 +0100 pat (0.1.0) stable; urgency=medium * Initial release under new name. * Fix leak that caused increasing CPU load. * Add band filtering for rmslist command. * Fix winmor robust issues. -- Martin Hebnes Pedersen Sun, 06 Mar 2016 14:09:11 +0100 wl2k-go (0.0.4) stable; urgency=medium * Fixed parse error of Date field from RMS Relay'ed messages (https://github.com/la5nta/wl2k-go/issues/29). * Fixed parse of ax25 URLs with digipeaters (https://github.com/la5nta/wl2k-go/issues/28). * Fixed panic on misconfigured (empty) axport (https://github.com/la5nta/wl2k-go/issues/27). * Prompt user for login password if mycall is overridden by --mycall even though a password is defined in config. * Run winmor in robust mode during handshake and proposal chatter. * GPSd support (for position reporting using a local serial/usb GPS). -- Martin Hebnes Pedersen Sun, 14 Feb 2016 18:19:02 +0100 wl2k-go (0.0.3) stable; urgency=medium * Fixed web ui assets bug (https://github.com/la5nta/wl2k-go/issues/26). * Fixed systemd user install script. -- Martin Hebnes Pedersen Thu, 14 Jan 2016 19:26:49 +0100 wl2k-go (0.0.2) stable; urgency=medium * Fixed ARDOPc issues. -- Martin Hebnes Pedersen Sun, 10 Jan 2016 15:56:00 +0100 wl2k-go (0.0.1) stable; urgency=medium * Initial release. -- Martin Hebnes Pedersen Sun, 04 Nov 2016 16:24:24 +0100 pat-0.12.1/debian/compat000066400000000000000000000000021415513746300150100ustar00rootroot000000000000007 pat-0.12.1/debian/control000066400000000000000000000007411415513746300152170ustar00rootroot00000000000000Source: pat Section: ham Priority: extra Maintainer: Martin Hebnes Pedersen Homepage: http://getpat.io Build-Depends: debhelper (>= 7.0.50~), golang (>= 2:1.16), libax25, libax25-dev Standards-Version: 3.9.1 Package: pat Architecture: amd64 i386 armhf Conflicts: wl2k-go, dist Replaces: wl2k-go Recommends: libhamlib-utils (>= 1.2), ax25-tools, gpsd (>= 2.90) Suggests: tmd710-tncsetup Description: A portable Winlink client for amateur radio email. pat-0.12.1/debian/pat.manpages000066400000000000000000000000101415513746300161020ustar00rootroot00000000000000man/*.1 pat-0.12.1/debian/pat@.service000066400000000000000000000003521415513746300160600ustar00rootroot00000000000000[Unit] Description=pat - Winlink client for %I Documentation=https://github.com/la5nta/pat/wiki After=ax25.service network.target [Service] User=%i ExecStart=/usr/bin/pat http Restart=on-failure [Install] WantedBy=multi-user.target pat-0.12.1/debian/rules000077500000000000000000000010231415513746300146660ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- PKGDIR=debian/pat %: dh $@ clean: dh_clean rm -rf $(PKGDIR) build: ./make.bash binary-arch: clean build dh_prep dh_installdirs mkdir -p $(PKGDIR)/usr/bin mkdir -p $(PKGDIR)/usr/share/pat mkdir -p $(PKGDIR)/lib/systemd/system mv ./pat $(PKGDIR)/usr/bin/ cp -r share/* $(PKGDIR)/usr/share/pat/ cp debian/pat@.service $(PKGDIR)/lib/systemd/system/ dh_installman dh_strip dh_compress dh_fixperms dh_installdeb dh_gencontrol dh_md5sums dh_builddeb binary: binary-arch pat-0.12.1/env.go000066400000000000000000000017051415513746300135120ustar00rootroot00000000000000package main import ( "fmt" "io" "os" "runtime" "github.com/la5nta/pat/internal/buildinfo" ) func envHandle(_ []string) { writeEnvAll(os.Stdout) } func writeEnvAll(w io.Writer) { writeEnv(w, "PAT_MYCALL", fOptions.MyCall) writeEnv(w, "PAT_LOCATOR", config.Locator) writeEnv(w, "PAT_VERSION", buildinfo.Version) writeEnv(w, "PAT_ARCH", runtime.GOARCH) writeEnv(w, "PAT_OS", runtime.GOOS) writeEnv(w, "PAT_MAILBOX_PATH", fOptions.MailboxPath) writeEnv(w, "PAT_CONFIG_PATH", fOptions.ConfigPath) writeEnv(w, "PAT_LOG_PATH", fOptions.LogPath) writeEnv(w, "PAT_EVENTLOG_PATH", fOptions.EventLogPath) writeEnv(w, "PAT_FORMS_PATH", fOptions.FormsPath) writeEnv(w, "PAT_DEBUG", os.Getenv("PAT_DEBUG")) writeEnv(w, "ARDOP_DEBUG", os.Getenv("ARDOP_DEBUG")) writeEnv(w, "WINMOR_DEBUG", os.Getenv("WINMOR_DEBUG")) writeEnv(w, "PACTOR_DEBUG", os.Getenv("PACTOR_DEBUG")) } func writeEnv(w io.Writer, k, v string) { fmt.Fprintf(w, "%s=\"%s\"\n", k, v) } pat-0.12.1/event_log.go000066400000000000000000000023371415513746300147060ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "encoding/json" "net" "os" "time" ) type EventLogger struct { file *os.File enc *json.Encoder } func NewEventLogger(path string) (*EventLogger, error) { file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) return &EventLogger{file, json.NewEncoder(file)}, err } func (l *EventLogger) Close() error { return l.file.Close() } func (l *EventLogger) Log(what string, event map[string]interface{}) { event["log_time"] = time.Now() event["what"] = what if err := l.enc.Encode(event); err != nil { panic(err) } } func (l *EventLogger) LogConn(op string, freq Frequency, conn net.Conn, err error) { e := map[string]interface{}{"success": err == nil} if err != nil { e["error"] = err.Error() } else { if remote := conn.RemoteAddr(); remote != nil { e["remote_addr"] = remote.String() e["network"] = conn.RemoteAddr().Network() } if local := conn.LocalAddr(); local != nil { e["local_addr"] = local.String() } } if freq > 0 { e["freq"] = freq } e["operation"] = op l.Log("connect", e) } pat-0.12.1/exchange.go000066400000000000000000000144561415513746300145130ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "fmt" "log" "net" "os" "os/signal" "strings" "time" "github.com/la5nta/pat/internal/buildinfo" "github.com/la5nta/wl2k-go/fbb" ) type ex struct { conn net.Conn target string master bool errors chan error } func exchangeLoop() (ce chan ex) { ce = make(chan ex) go func() { for ex := range ce { ex.errors <- sessionExchange(ex.conn, ex.target, ex.master) close(ex.errors) } }() return ce } func exchange(conn net.Conn, targetCall string, master bool) error { e := ex{ conn: conn, target: targetCall, master: master, errors: make(chan error), } exchangeChan <- e return <-e.errors } type NotifyMBox struct{ fbb.MBoxHandler } func (m NotifyMBox) ProcessInbound(msgs ...*fbb.Message) error { if err := m.MBoxHandler.ProcessInbound(msgs...); err != nil { return err } for _, msg := range msgs { websocketHub.WriteJSON(struct{ Notification Notification }{ Notification{ Title: fmt.Sprintf("New message from %s", msg.From().Addr), Body: msg.Subject(), }, }) } return nil } func sessionExchange(conn net.Conn, targetCall string, master bool) error { exchangeConn = conn websocketHub.UpdateStatus() defer func() { exchangeConn = nil; websocketHub.UpdateStatus() }() // New wl2k Session targetCall = strings.Split(targetCall, ` `)[0] session := fbb.NewSession( fOptions.MyCall, targetCall, config.Locator, NotifyMBox{mbox}, ) session.SetUserAgent(fbb.UserAgent{ Name: buildinfo.AppName, Version: buildinfo.Version, }) if len(config.MOTD) > 0 { session.SetMOTD(config.MOTD...) } // Handle secure login session.SetSecureLoginHandleFunc(func(addr fbb.Address) (string, error) { if addr.Addr == fOptions.MyCall && config.SecureLoginPassword != "" { return config.SecureLoginPassword, nil } for _, aux := range config.AuxAddrs { if addr.Addr != aux.Address { continue } switch { case aux.Password != nil: return *aux.Password, nil case config.SecureLoginPassword != "": return config.SecureLoginPassword, nil } } resp := <-promptHub.Prompt("password", "Enter secure login password for "+addr.String()) return resp.Value, resp.Err }) for _, addr := range config.AuxAddrs { session.AddAuxiliaryAddress(fbb.AddressFromString(addr.Address)) } session.IsMaster(master) session.SetLogger(log.New(logWriter, "", 0)) session.SetStatusUpdater(new(StatusUpdate)) if fOptions.Robust { session.SetRobustMode(fbb.RobustForced) } log.Printf("Connected to %s (%s)", conn.RemoteAddr(), conn.RemoteAddr().Network()) // Close connection on os.Interrupt stop := handleInterrupt() defer close(stop) start := time.Now() stats, err := session.Exchange(conn) if fbb.IsLoginFailure(err) { fmt.Println("NOTE: A new password scheme for Winlink is being implemented as of 2018-01-31.") fmt.Println(" Users with passwords created/changed prior to January 31, 2018 should be") fmt.Println(" aware that their password MUST be entered in ALL-UPPERCASE letters. Only") fmt.Println(" passwords created/changed/issued after January 31, 2018 should/may contain") fmt.Println(" lowercase letters. - https://github.com/la5nta/pat/issues/113") } event := map[string]interface{}{ "mycall": session.Mycall(), "targetcall": session.Targetcall(), "remote_fw": session.RemoteForwarders(), "remote_sid": session.RemoteSID(), "master": master, "local_locator": config.Locator, "auxiliary_addresses": config.AuxAddrs, "network": conn.RemoteAddr().Network(), "remote_addr": conn.RemoteAddr().String(), "local_addr": conn.LocalAddr().String(), "sent": stats.Sent, "received": stats.Received, "start": start.Unix(), "end": time.Now().Unix(), "success": err == nil, } if err != nil { event["error"] = err.Error() } eventLog.Log("exchange", event) return err } func handleInterrupt() (stop chan struct{}) { stop = make(chan struct{}) go func() { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) defer func() { signal.Stop(sig); close(sig) }() dirtyDisconnectNext := false // So we can do a dirty disconnect on the second interrupt for { select { case <-stop: return case <-sig: abortActiveConnection(dirtyDisconnectNext) dirtyDisconnectNext = !dirtyDisconnectNext } } }() return stop } func abortActiveConnection(dirty bool) (ok bool) { switch { case exchangeConn != nil: log.Println("Got abort signal, disconnecting...") exchangeConn.Close() return true case pModem != nil: log.Println("Disconnecting pactor...") err := pModem.Close() if err != nil { log.Println(err) } return err == nil case wmTNC != nil && !wmTNC.Idle(): if dirty { log.Println("Dirty disconnecting winmor...") wmTNC.DirtyDisconnect() return true } log.Println("Disconnecting winmor...") go func() { if err := wmTNC.Disconnect(); err != nil { log.Println(err) } }() return true case adTNC != nil && !adTNC.Idle(): if dirty { log.Println("Dirty disconnecting ardop...") adTNC.Abort() return true } log.Println("Disconnecting ardop...") go func() { if err := adTNC.Disconnect(); err != nil { log.Println(err) } }() return true case dialing != nil: log.Printf("Transport %s's dialer can not be aborted at this stage", dialing.Scheme) return false default: return false } } type StatusUpdate int func (s *StatusUpdate) UpdateStatus(stat fbb.Status) { var prop fbb.Proposal switch { case stat.Receiving != nil: prop = *stat.Receiving case stat.Sending != nil: prop = *stat.Sending } websocketHub.WriteProgress(Progress{ MID: prop.MID(), BytesTotal: stat.BytesTotal, BytesTransferred: stat.BytesTransferred, Subject: prop.Title(), Receiving: stat.Receiving != nil, Sending: stat.Sending != nil, Done: stat.Done, }) percent := float64(stat.BytesTransferred) / float64(stat.BytesTotal) * 100 fmt.Printf("\r%s: %3.0f%%", prop.Title(), percent) if stat.Done { fmt.Println("") } os.Stdout.Sync() } pat-0.12.1/flags.go000066400000000000000000000033621415513746300140170ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "fmt" "os" "strings" "github.com/spf13/pflag" ) var ErrNoCmd = fmt.Errorf("no cmd") type Command struct { Str string Aliases []string Desc string HandleFunc func(args []string) Usage string Options map[string]string Example string LongLived bool MayConnect bool } func (cmd Command) PrintUsage() { fmt.Fprintf(os.Stderr, "%s - %s\n", cmd.Str, cmd.Desc) fmt.Fprintf(os.Stderr, "\nUsage:\n %s %s\n", cmd.Str, strings.TrimSpace(cmd.Usage)) if len(cmd.Options) > 0 { fmt.Fprint(os.Stderr, "\nOptions:\n") for f, desc := range cmd.Options { fmt.Fprintf(os.Stderr, " %-17s %s\n", f, desc) } } if cmd.Example != "" { fmt.Fprintf(os.Stderr, "\nExample:\n %s\n", strings.TrimSpace(cmd.Example)) } fmt.Fprint(os.Stderr, "\n") } func parseFlags(args []string) (cmd Command, arguments []string) { var options []string var err error cmd, options, arguments, err = findCommand(args) if err != nil { pflag.Usage() os.Exit(1) } optionsSet().Parse(options) if len(arguments) == 0 { arguments = append(arguments, "") } switch arguments[0] { case "--help", "-help", "help", "-h": cmd.PrintUsage() os.Exit(1) } return } func findCommand(args []string) (cmd Command, pre, post []string, err error) { cmdMap := make(map[string]Command, len(commands)) for _, c := range commands { cmdMap[c.Str] = c for _, alias := range c.Aliases { cmdMap[alias] = c } } for i, arg := range args { if cmd, ok := cmdMap[arg]; ok { return cmd, args[1:i], args[i+1:], nil } } err = ErrNoCmd return } pat-0.12.1/freq.go000066400000000000000000000064421415513746300136620ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "encoding/json" "fmt" "log" "strconv" "strings" "github.com/la5nta/wl2k-go/rigcontrol/hamlib" ) var bands = map[string]Band{ "160m": {1.8e6, 2.0e6}, "80m": {3.5e6, 4.0e6}, "60m": {5.2e6, 5.5e6}, "40m": {7.0e6, 7.3e6}, "30m": {10.1e6, 10.2e6}, "20m": {14.0e6, 14.4e6}, "17m": {18.0e6, 18.2e6}, "15m": {21.0e6, 21.5e6}, "12m": {24.8e6, 25.0e6}, "10m": {28.0e6, 30.0e6}, "6m": {50.0e6, 54.0e6}, "4m": {70.0e6, 70.5e6}, "2m": {144.0e6, 148.0e6}, "1.25m": {219.0e6, 225.0e6}, // 220, 222 (MHz) "70cm": {420.0e6, 450.0e6}, } type Band struct{ lower, upper Frequency } func (b Band) Contains(f Frequency) bool { if b.lower == 0 && b.upper == 0 { return true } return f >= b.lower && f <= b.upper } type Frequency int // Hz func (f Frequency) String() string { m := f / 1e6 k := (float64(f) - float64(m)*1e6) / 1e3 return fmt.Sprintf("%d.%06.2f MHz", m, k) } func (f Frequency) MarshalJSON() ([]byte, error) { type obj struct { Hz json.Number `json:"hz"` KHz json.Number `json:"khz"` Desc string `json:"desc"` } return json.Marshal(obj{ Hz: json.Number(fmt.Sprint(int(f))), KHz: json.Number(fmt.Sprint(f.KHz())), Desc: f.String(), }) } func (f Frequency) KHz() float64 { return float64(f) / 1e3 } func (f Frequency) Dial(mode string) Frequency { mode = strings.ToLower(mode) // Try to detect FM modes // (ARDOP on FM is reported as `ARDOP 2000 FM`) if strings.HasSuffix(mode, "fm") { return f } offsets := map[string]Frequency{ MethodWinmor: 1500, MethodPactor: 1500, MethodArdop: 1500, } var shift Frequency for m, offset := range offsets { if strings.Contains(mode, m) { shift = -offset break } } return f + shift } func VFOForTransport(transport string) (vfo hamlib.VFO, rigName string, ok bool, err error) { var rig string switch transport { case MethodWinmor: rig = config.Winmor.Rig case MethodArdop: rig = config.Ardop.Rig case MethodAX25: rig = config.AX25.Rig case MethodPactor: rig = config.Pactor.Rig default: return vfo, "", false, fmt.Errorf("not supported with transport '%s'", transport) } if rig == "" { return vfo, "", false, fmt.Errorf("missing rig reference in config section for %s", transport) } vfo, ok = rigs[rig] return vfo, rig, ok, nil } func freq(param string) { parts := strings.SplitN(param, ":", 2) if parts[0] == "" { fmt.Println("Need freq method.") } rig, _, ok, _ := VFOForTransport(parts[0]) if !ok { log.Printf("Hamlib rig not loaded.") return } if len(parts) < 2 { freq, err := rig.GetFreq() if err != nil { log.Printf("Unable to get frequency: %s", err) } fmt.Printf("%.3f\n", float64(freq)/1e3) return } if _, _, err := setFreq(rig, parts[1]); err != nil { log.Printf("Unable to set frequency: %s", err) } } func setFreq(rig hamlib.VFO, freq string) (newFreq, oldFreq int, err error) { oldFreq, err = rig.GetFreq() if err != nil { return 0, 0, fmt.Errorf("unable to get rig frequency: %w", err) } f, err := strconv.ParseFloat(freq, 64) if err != nil { return 0, 0, err } newFreq = int(f * 1e3) err = rig.SetFreq(newFreq) return } pat-0.12.1/go.mod000066400000000000000000000020731415513746300135000ustar00rootroot00000000000000module github.com/la5nta/pat go 1.16 require ( github.com/adrg/xdg v0.3.3 github.com/bndr/gotabulate v1.1.3-0.20170315142410-bc555436bfd5 github.com/dimchansky/utfbom v1.1.1 github.com/fsnotify/fsnotify v1.4.9 github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 github.com/harenber/ptc-go/v2 v2.2.2 github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c github.com/la5nta/wl2k-go v0.9.0 github.com/mattn/go-runewidth v0.0.13 // indirect github.com/microcosm-cc/bluemonday v1.0.16 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/pd0mz/go-maidenhead v1.0.0 github.com/peterh/liner v1.2.1 github.com/spf13/pflag v1.0.5 go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e // indirect golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect ) pat-0.12.1/go.sum000066400000000000000000000214001415513746300135200ustar00rootroot00000000000000github.com/adrg/xdg v0.3.3 h1:s/tV7MdqQnzB1nKY8aqHvAMD+uCiuEDzVB5HLRY849U= github.com/adrg/xdg v0.3.3/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ= github.com/albenik/go-serial/v2 v2.3.0/go.mod h1:JUrQKdczCMB0FlXt2rlJJ8zbfFzmjTIAkLPyyVfr5ho= github.com/albenik/go-serial/v2 v2.4.0 h1:2yIM9C0l0YznmMbHF8Yw+j0XY1ZDy5YciqhGdOik6r8= github.com/albenik/go-serial/v2 v2.4.0/go.mod h1:JUrQKdczCMB0FlXt2rlJJ8zbfFzmjTIAkLPyyVfr5ho= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bndr/gotabulate v1.1.3-0.20170315142410-bc555436bfd5 h1:D48YSLPNJ8WpdwDqYF8bMMKUB2bgdWEiFx1MGwPIdbs= github.com/bndr/gotabulate v1.1.3-0.20170315142410-bc555436bfd5/go.mod h1:0+8yUgaPTtLRTjf49E8oju7ojpU11YmXyvq1LbPAb3U= github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/harenber/ptc-go/v2 v2.2.2 h1:b6+QwjtdV2bDvhFgM5tksNMx5zN/Ml9inldF7msS0Zw= github.com/harenber/ptc-go/v2 v2.2.2/go.mod h1:SDIy4XqnUq6YYPcLLjDTfbLPf1/Z82ho1VPkPGVXSTo= github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6 h1:IIVxLyDUYErC950b8kecjoqDet8P5S4lcVRUOM6rdkU= github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6/go.mod h1:JslaLRrzGsOKJgFEPBP65Whn+rdwDQSk0I0MCRFe2Zw= github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c h1:aY2hhxLhjEAbfXOx2nRJxCXezC6CO2V/yN+OCr1srtk= github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/la5nta/wl2k-go v0.7.3/go.mod h1:rTQaxPiAFD3pWGWN8Lh+BskN3Fpii84GoVwpTHNiCjE= github.com/la5nta/wl2k-go v0.9.0 h1:kdeUrGXLocisDpDvIkmtnXbHGopYiM6Pei/6rnYAGSs= github.com/la5nta/wl2k-go v0.9.0/go.mod h1:m/O3yYdsWhPdM1K/nAXqqioWRQGJwgKO7D8pEk6AQeY= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/microcosm-cc/bluemonday v1.0.16 h1:kHmAq2t7WPWLjiGvzKa5o3HzSfahUKiOq7fAPUiMNIc= github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83/go.mod h1:YnNlZP7l4MhyGQ4CBRwv6ohZTPrUJJZtEv4ZgADkbs4= github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c h1:P6XGcuPTigoHf4TSu+3D/7QOQ1MbL6alNwrGhcW7sKw= github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c/go.mod h1:YnNlZP7l4MhyGQ4CBRwv6ohZTPrUJJZtEv4ZgADkbs4= github.com/pd0mz/go-maidenhead v1.0.0 h1:zl2AXA36LnmP5TDEfshM0fWi1mc08fNc6qhj7YD5xjw= github.com/pd0mz/go-maidenhead v1.0.0/go.mod h1:4Q+QSDCqWqlabstLGUVm47rAcL06nEEty2d3KzsTNMk= github.com/peterh/liner v1.2.1 h1:O4BlKaq/LWu6VRWmol4ByWfzx6MfXc5Op5HETyIy5yg= github.com/peterh/liner v1.2.1/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tarm/goserial v0.0.0-20151007205400-b3440c3c6355/go.mod h1:jcMo2Odv5FpDA6rp8bnczbUolcICW6t54K3s9gOlgII= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e h1:VvfwVmMH40bpMeizC9/K7ipM5Qjucuu16RWfneFPyhQ= golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210223212115-eede4237b368/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= pat-0.12.1/http.go000066400000000000000000000507261415513746300137100ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "bufio" "bytes" "embed" "encoding/json" "errors" "fmt" "html/template" "io" "io/fs" "log" "mime/multipart" "net" "net/http" "net/url" "os" "path" "path/filepath" "sort" "strconv" "strings" "time" "github.com/la5nta/pat/internal/buildinfo" "github.com/la5nta/pat/internal/gpsd" "github.com/gorilla/mux" "github.com/gorilla/websocket" "github.com/la5nta/wl2k-go/catalog" "github.com/la5nta/wl2k-go/fbb" "github.com/la5nta/wl2k-go/mailbox" "github.com/microcosm-cc/bluemonday" ) //go:embed web/res/** var embeddedFS embed.FS var staticContent fs.FS // Status represents a status report as sent to the Web GUI type Status struct { ActiveListeners []string `json:"active_listeners"` Connected bool `json:"connected"` Dialing bool `json:"dialing"` RemoteAddr string `json:"remote_addr"` HTTPClients []string `json:"http_clients"` } // Progress represents a progress report as sent to the Web GUI type Progress struct { BytesTransferred int `json:"bytes_transferred"` BytesTotal int `json:"bytes_total"` MID string `json:"mid"` Subject string `json:"subject"` Receiving bool `json:"receiving"` Sending bool `json:"sending"` Done bool `json:"done"` } // Notification represents a desktop notification as sent to the Web GUI type Notification struct { Title string `json:"title"` Body string `json:"body"` } type HTTPError struct { error StatusCode int } var websocketHub *WSHub func init() { var err error staticContent, err = fs.Sub(embeddedFS, "web") if err != nil { panic(err) } } func ListenAndServe(addr string) error { log.Printf("Starting HTTP service (http://%s)...", addr) if host, _, _ := net.SplitHostPort(addr); host == "" && config.GPSd.EnableHTTP { // TODO: maybe make a popup showing the warning ont the web UI? _, _ = fmt.Fprintf(logWriter, "\nWARNING: You have enable GPSd HTTP endpoint (enable_http). You might expose"+ "\n your current position to anyone who has access to the Pat web interface!\n\n") } r := mux.NewRouter() r.HandleFunc("/api/connect_aliases", connectAliasesHandler).Methods("GET") r.HandleFunc("/api/connect", ConnectHandler) r.HandleFunc("/api/formcatalog", formsMgr.GetFormsCatalogHandler).Methods("GET") r.HandleFunc("/api/form", formsMgr.PostFormDataHandler).Methods("POST") r.HandleFunc("/api/form", formsMgr.GetFormDataHandler).Methods("GET") r.HandleFunc("/api/forms", formsMgr.GetFormTemplateHandler).Methods("GET") r.HandleFunc("/api/formsUpdate", formsMgr.UpdateFormTemplatesHandler).Methods("POST") r.HandleFunc("/api/disconnect", DisconnectHandler) r.HandleFunc("/api/mailbox/{box}", mailboxHandler).Methods("GET") r.HandleFunc("/api/mailbox/{box}/{mid}", messageHandler).Methods("GET") r.HandleFunc("/api/mailbox/{box}/{mid}", messageDeleteHandler).Methods("DELETE") r.HandleFunc("/api/mailbox/{box}/{mid}/{attachment}", attachmentHandler).Methods("GET") r.HandleFunc("/api/mailbox/{box}/{mid}/read", readHandler).Methods("POST") r.HandleFunc("/api/mailbox/{box}", postMessageHandler).Methods("POST") r.HandleFunc("/api/posreport", postPositionHandler).Methods("POST") r.HandleFunc("/api/status", statusHandler).Methods("GET") r.HandleFunc("/api/current_gps_position", positionHandler).Methods("GET") r.HandleFunc("/api/qsy", qsyHandler).Methods("POST") r.HandleFunc("/api/rmslist", rmslistHandler).Methods("GET") r.HandleFunc("/ws", wsHandler) r.HandleFunc("/ui", uiHandler).Methods("GET") r.HandleFunc("/", rootHandler).Methods("GET") http.Handle("/", r) http.Handle("/res/", http.FileServer(http.FS(staticContent))) websocketHub = NewWSHub() return http.ListenAndServe(addr, nil) } func rootHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/ui", http.StatusFound) } func connectAliasesHandler(w http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(w).Encode(config.ConnectAliases) } func readHandler(w http.ResponseWriter, r *http.Request) { var data struct{ Read bool } if err := json.NewDecoder(r.Body).Decode(&data); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) log.Printf("%s %s: %s", r.Method, r.URL.Path, err) return } box, mid := mux.Vars(r)["box"], mux.Vars(r)["mid"] msg, err := mailbox.OpenMessage(path.Join(mbox.MBoxPath, box, mid+mailbox.Ext)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if err := mailbox.SetUnread(msg, !data.Read); err != nil { log.Printf("%s %s: %s", r.Method, r.URL.Path, err) http.Error(w, err.Error(), http.StatusInternalServerError) } } func postPositionHandler(w http.ResponseWriter, r *http.Request) { var pos catalog.PosReport if err := json.NewDecoder(r.Body).Decode(&pos); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } _ = r.Body.Close() if pos.Date.IsZero() { pos.Date = time.Now() } // Post to outbox msg := pos.Message(fOptions.MyCall) if err := mbox.AddOut(msg); err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) } else { _, _ = fmt.Fprintln(w, "Position update posted") } } func isInPath(base string, path string) error { _, err := filepath.Rel(base, path) return err } func postMessageHandler(w http.ResponseWriter, r *http.Request) { box := mux.Vars(r)["box"] if box == "out" { postOutboundMessageHandler(w, r) return } srcPath := r.Header.Get("X-Pat-SourcePath") if srcPath == "" { http.Error(w, "Not implemented", http.StatusNotImplemented) return } srcPath, _ = url.PathUnescape(strings.TrimPrefix(srcPath, "/api/mailbox/")) srcPath = filepath.Join(mbox.MBoxPath, srcPath+mailbox.Ext) // Check that we don't escape our mailbox path srcPath = filepath.Clean(srcPath) if err := isInPath(mbox.MBoxPath, srcPath); err != nil { log.Println("Malicious source path in move:", err) http.Error(w, err.Error(), http.StatusBadRequest) return } targetPath := filepath.Join(mbox.MBoxPath, box, filepath.Base(srcPath)) if err := os.Rename(srcPath, targetPath); err != nil { log.Println("Could not move message:", err) http.Error(w, err.Error(), http.StatusBadRequest) } else { _ = json.NewEncoder(w).Encode("OK") } } func postOutboundMessageHandler(w http.ResponseWriter, r *http.Request) { err := r.ParseMultipartForm(10 * (1024 ^ 2)) // 10Mb if err != nil { if err := r.ParseForm(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } msg := fbb.NewMessage(fbb.Private, fOptions.MyCall) // files if r.MultipartForm != nil { files := r.MultipartForm.File["files"] for _, f := range files { err := attachFile(f, msg) switch err := err.(type) { case nil: // No problem case HTTPError: http.Error(w, err.Error(), err.StatusCode) default: http.Error(w, err.Error(), http.StatusInternalServerError) } } } cookie, err := r.Cookie("forminstance") if err == nil { formData := formsMgr.GetPostedFormData(cookie.Value) name := formsMgr.GetXMLAttachmentNameForForm(formData.TargetForm, formData.IsReply) msg.AddFile(fbb.NewFile(name, []byte(formData.MsgXML))) } // Other fields if v := r.Form["to"]; len(v) == 1 { addrs := strings.FieldsFunc(v[0], SplitFunc) msg.AddTo(addrs...) } if v := r.Form["cc"]; len(v) == 1 { addrs := strings.FieldsFunc(v[0], SplitFunc) msg.AddCc(addrs...) } if v := r.Form["subject"]; len(v) == 1 { msg.SetSubject(v[0]) } if v := r.Form["body"]; len(v) == 1 { _ = msg.SetBody(v[0]) } if v := r.Form["p2ponly"]; len(v) == 1 && v[0] != "" { msg.Header.Set("X-P2POnly", "true") } if v := r.Form["date"]; len(v) == 1 { t, err := time.Parse(time.RFC3339, v[0]) if err != nil { log.Printf("Unable to parse message date: %s", err) http.Error(w, err.Error(), http.StatusBadRequest) return } msg.SetDate(t) } else { log.Printf("Missing date value") http.Error(w, "Missing date value", http.StatusBadRequest) return } if err := msg.Validate(); err != nil { http.Error(w, "Validation error: "+err.Error(), http.StatusBadRequest) return } // Post to outbox if err := mbox.AddOut(msg); err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) var buf bytes.Buffer _ = msg.Write(&buf) _, _ = fmt.Fprintf(w, "Message posted (%.2f kB)", float64(buf.Len()/1024)) } func attachFile(f *multipart.FileHeader, msg *fbb.Message) error { // For some unknown reason, we receive this empty unnamed file when no // attachment is provided. Prior to Go 1.10, this was filtered by // multipart.Reader. if isEmptyFormFile(f) { return nil } if f.Filename == "" { err := errors.New("missing attachment name") return HTTPError{err, http.StatusBadRequest} } file, err := f.Open() if err != nil { return HTTPError{err, http.StatusInternalServerError} } p, err := io.ReadAll(file) _ = file.Close() if err != nil { return HTTPError{err, http.StatusInternalServerError} } if isImageMediaType(f.Filename, f.Header.Get("Content-Type")) { log.Printf("Auto converting '%s' [%s]...", f.Filename, f.Header.Get("Content-Type")) if converted, err := convertImage(p); err != nil { log.Printf("Error converting image: %s", err) } else { log.Printf("Done converting '%s'.", f.Filename) ext := path.Ext(f.Filename) f.Filename = f.Filename[:len(f.Filename)-len(ext)] + ".jpg" p = converted } } msg.AddFile(fbb.NewFile(f.Filename, p)) return nil } func wsHandler(w http.ResponseWriter, r *http.Request) { upgrader := websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) return } _ = conn.WriteJSON(struct{ MyCall string }{fOptions.MyCall}) websocketHub.Handle(conn) } func uiHandler(w http.ResponseWriter, _ *http.Request) { data, err := fs.ReadFile(staticContent, path.Join("res", "tmpl", "index.html")) if err != nil { log.Fatal(err) } t := template.New("index.html") // create a new template t, err = t.Parse(string(data)) if err != nil { log.Fatal(err) } tmplData := struct{ AppName, Version, Mycall string }{buildinfo.AppName, buildinfo.VersionString(), fOptions.MyCall} err = t.Execute(w, tmplData) if err != nil { log.Fatal(err) } } func getStatus() Status { status := Status{ ActiveListeners: []string{}, Dialing: dialing != nil, Connected: exchangeConn != nil, HTTPClients: websocketHub.ClientAddrs(), } for _, tl := range listenHub.Active() { status.ActiveListeners = append(status.ActiveListeners, tl.Name()) } sort.Strings(status.ActiveListeners) if exchangeConn != nil { addr := exchangeConn.RemoteAddr() status.RemoteAddr = fmt.Sprintf("%s:%s", addr.Network(), addr) } return status } func statusHandler(w http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(w).Encode(getStatus()) } func rmslistHandler(w http.ResponseWriter, req *http.Request) { forceDownload, _ := strconv.ParseBool(req.FormValue("force-download")) band := req.FormValue("band") mode := strings.ToLower(req.FormValue("mode")) prefix := strings.ToUpper(req.FormValue("prefix")) list, err := ReadRMSList(forceDownload, func(r RMS) bool { switch { case r.URL == nil: return false case mode != "" && !r.IsMode(mode): return false case band != "" && !r.IsBand(band): return false case prefix != "" && !strings.HasPrefix(r.Callsign, prefix): return false default: return true } }) if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } sort.Sort(byDist(list)) err = json.NewEncoder(w).Encode(list) if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) } } func qsyHandler(w http.ResponseWriter, req *http.Request) { type QSYPayload struct { Transport string `json:"transport"` Freq json.Number `json:"freq"` } var payload QSYPayload if err := json.NewDecoder(req.Body).Decode(&payload); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } rig, rigName, ok, err := VFOForTransport(payload.Transport) switch { case rigName == "": // Either unsupported mode or no rig configured for this transport w.WriteHeader(http.StatusServiceUnavailable) return case !ok: // A rig is configured, but not loaded properly w.WriteHeader(http.StatusInternalServerError) log.Printf("QSY failed: Hamlib rig '%s' not loaded.", rigName) case err != nil: w.WriteHeader(http.StatusInternalServerError) log.Printf("QSY failed: %v", err) default: if _, _, err := setFreq(rig, string(payload.Freq)); err != nil { w.WriteHeader(http.StatusInternalServerError) log.Printf("QSY failed: %v", err) return } _ = json.NewEncoder(w).Encode(payload) } } func positionHandler(w http.ResponseWriter, req *http.Request) { // Throw error if GPSd http endpoint is not enabled if !config.GPSd.EnableHTTP || config.GPSd.Addr == "" { http.Error(w, "GPSd not enabled or address not set in config file", http.StatusInternalServerError) return } host, _, _ := net.SplitHostPort(req.RemoteAddr) log.Printf("Location data from GPSd served to %s", host) conn, err := gpsd.Dial(config.GPSd.Addr) if err != nil { // do not pass error message to response as GPSd address might be leaked http.Error(w, "GPSd Dial failed", http.StatusInternalServerError) return } defer conn.Close() conn.Watch(true) pos, err := conn.NextPosTimeout(5 * time.Second) if err != nil { http.Error(w, "GPSd get next position failed: "+err.Error(), http.StatusInternalServerError) return } if config.GPSd.UseServerTime { pos.Time = time.Now() } _ = json.NewEncoder(w).Encode(pos) } func DisconnectHandler(w http.ResponseWriter, req *http.Request) { dirty, _ := strconv.ParseBool(req.FormValue("dirty")) if ok := abortActiveConnection(dirty); !ok { w.WriteHeader(http.StatusBadRequest) } _ = json.NewEncoder(w).Encode(struct{}{}) } func ConnectHandler(w http.ResponseWriter, req *http.Request) { connectStr := req.FormValue("url") nMsgs := mbox.InboxCount() if success := Connect(connectStr); !success { http.Error(w, "Session failure", http.StatusInternalServerError) } _ = json.NewEncoder(w).Encode(struct { NumReceived int }{ mbox.InboxCount() - nMsgs, }) } func mailboxHandler(w http.ResponseWriter, r *http.Request) { box := mux.Vars(r)["box"] var messages []*fbb.Message var err error switch box { case "in": messages, err = mbox.Inbox() case "out": messages, err = mbox.Outbox() case "sent": messages, err = mbox.Sent() case "archive": messages, err = mbox.Archive() default: http.NotFound(w, r) return } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) log.Println(err) } sort.Sort(sort.Reverse(fbb.ByDate(messages))) jsonSlice := make([]JSONMessage, len(messages)) for i, msg := range messages { jsonSlice[i] = JSONMessage{Message: msg} } _ = json.NewEncoder(w).Encode(jsonSlice) } type JSONMessage struct { *fbb.Message inclBody bool } func (m JSONMessage) MarshalJSON() ([]byte, error) { msg := struct { MID string Date time.Time From fbb.Address To []fbb.Address Cc []fbb.Address Subject string Body string BodyHTML string Files []*fbb.File P2POnly bool Unread bool }{ MID: m.MID(), Date: m.Date(), From: m.From(), To: m.To(), Cc: m.Cc(), Subject: m.Subject(), Files: m.Files(), P2POnly: m.Header.Get("X-P2POnly") == "true", Unread: mailbox.IsUnread(m.Message), } if m.inclBody { msg.Body, _ = m.Body() unsafe := toHTML([]byte(msg.Body)) msg.BodyHTML = string(bluemonday.UGCPolicy().SanitizeBytes(unsafe)) } return json.Marshal(msg) } func messageDeleteHandler(w http.ResponseWriter, r *http.Request) { box, mid := mux.Vars(r)["box"], mux.Vars(r)["mid"] file := filepath.Clean(filepath.Join(mbox.MBoxPath, box, mid+mailbox.Ext)) if err := isInPath(mbox.MBoxPath, file); err != nil { log.Println("Malicious source path in move:", err) http.Error(w, err.Error(), http.StatusBadRequest) return } err := os.Remove(file) if os.IsNotExist(err) { http.NotFound(w, r) return } else if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } _ = json.NewEncoder(w).Encode("OK") } func messageHandler(w http.ResponseWriter, r *http.Request) { box, mid := mux.Vars(r)["box"], mux.Vars(r)["mid"] msg, err := mailbox.OpenMessage(path.Join(mbox.MBoxPath, box, mid+mailbox.Ext)) if os.IsNotExist(err) { http.NotFound(w, r) return } else if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } _ = json.NewEncoder(w).Encode(JSONMessage{msg, true}) } func attachmentHandler(w http.ResponseWriter, r *http.Request) { // Attachments are potentially unsanitized HTML and/or javascript. // To avoid XSS, we enable the CSP sandbox directive so that these // attachments can't call other parts of the API (deny same origin). w.Header().Set("Content-Security-Policy", "sandbox allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-scripts") // Allow different sandboxed attachments to refer to each other. // This can be useful to provide rich HTML content as attachments, // without having to bundle it all up in one big file. w.Header().Set("Access-Control-Allow-Origin", "null") box, mid, attachment := mux.Vars(r)["box"], mux.Vars(r)["mid"], mux.Vars(r)["attachment"] composereply, _ := strconv.ParseBool(r.URL.Query().Get("composereply")) renderToHtml, _ := strconv.ParseBool(r.URL.Query().Get("rendertohtml")) if composereply || renderToHtml { // no-store is needed for displaying and replying to Winlink form-based messages w.Header().Set("Cache-Control", "no-store") } msg, err := mailbox.OpenMessage(path.Join(mbox.MBoxPath, box, mid+mailbox.Ext)) if os.IsNotExist(err) { http.NotFound(w, r) return } else if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // Find and write attachment var found bool for _, f := range msg.Files() { if f.Name() != attachment { continue } found = true if !renderToHtml { http.ServeContent(w, r, f.Name(), msg.Date(), bytes.NewReader(f.Data())) return } formRendered, err := formsMgr.RenderForm(f.Data(), composereply) if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } http.ServeContent(w, r, f.Name()+".html", msg.Date(), bytes.NewReader([]byte(formRendered))) } if !found { http.NotFound(w, r) } } // toHTML takes the given body and turns it into proper html with // paragraphs, blockquote, and
line breaks. func toHTML(body []byte) []byte { buf := bytes.NewBuffer(body) var out bytes.Buffer _, _ = fmt.Fprint(&out, "

") scanner := bufio.NewScanner(buf) var blockquote int for scanner.Scan() { line := scanner.Text() if len(line) == 0 { _, _ = fmt.Fprint(&out, "

") continue } depth := blockquoteDepth(line) for depth != blockquote { if depth > blockquote { _, _ = fmt.Fprintf(&out, "

") blockquote++ } else { _, _ = fmt.Fprintf(&out, "

") blockquote-- } } line = line[depth:] line = htmlEncode(line) line = linkify(line) _, _ = fmt.Fprint(&out, line+"\n") } for ; blockquote > 0; blockquote-- { _, _ = fmt.Fprintf(&out, "

") } _, _ = fmt.Fprint(&out, "

") return out.Bytes() } // blcokquoteDepth counts the number of '>' at the beginning of the string. func blockquoteDepth(str string) (n int) { for _, c := range str { if c != '>' { break } n++ } return } // htmlEncode encodes html characters func htmlEncode(str string) string { str = strings.ReplaceAll(str, ">", ">") str = strings.ReplaceAll(str, "<", "<") return str } // linkify detects url's in the given string and adds %s%s`, str[:start], link, str[start:end], linkify(str[end:])) } pat-0.12.1/http_multipart.go000066400000000000000000000002301415513746300157720ustar00rootroot00000000000000// +build go1.10 package main import "mime/multipart" func isEmptyFormFile(f *multipart.FileHeader) bool { return f.Size == 0 && f.Filename == "" } pat-0.12.1/interactive.go000066400000000000000000000067101415513746300152400ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "bytes" "fmt" "log" "os" "runtime" "strings" "time" "github.com/la5nta/wl2k-go/transport/ax25" "github.com/peterh/liner" ) func Interactive() { line := liner.NewLiner() defer line.Close() for { str, _ := line.Prompt(getPrompt()) if str == "" { continue } line.AppendHistory(str) if str[0] == '#' { continue } if quit := execCmd(str); quit { break } } } func execCmd(line string) (quit bool) { cmd, param := parseCommand(line) switch cmd { case "connect": if param == "" { printInteractiveUsage() return } Connect(param) case "listen": Listen(param) case "unlisten": Unlisten(param) case "heard": PrintHeard() case "freq": freq(param) case "qtc": PrintQTC() case "debug": os.Setenv("ardop_debug", "1") os.Setenv("winmor_debug", "1") fmt.Println("Number of goroutines:", runtime.NumGoroutine()) case "q", "quit": return true case "": return default: printInteractiveUsage() } return } func printInteractiveUsage() { fmt.Println("Uri examples: 'LA3F@5350', 'LA1B-10 v LA5NTA-1', 'LA5NTA:secret@192.168.1.1:54321'") methods := []string{ MethodWinmor, MethodArdop, MethodAX25, MethodTelnet, MethodSerialTNC, } fmt.Println("Methods:", strings.Join(methods, ", ")) cmds := []string{ "connect METHOD:[URI] or alias Connect to a remote station.", "listen METHOD Listen for incoming connections.", "unlisten METHOD Unregister listener for incoming connections.", "freq METHOD:FREQ Change rig frequency.", "heard Display all stations heard over the air.", "qtc Print pending outbound messages.", } fmt.Println("Commands: ") for _, cmd := range cmds { fmt.Printf(" %s\n", cmd) } } func getPrompt() string { var buf bytes.Buffer status := getStatus() if len(status.ActiveListeners) > 0 { fmt.Fprintf(&buf, "L%v", status.ActiveListeners) } fmt.Fprint(&buf, "> ") return buf.String() } func PrintHeard() { pf := func(call string, t time.Time) { fmt.Printf(" %-10s (%s)\n", call, t.Format(time.RFC1123)) } fmt.Println("winmor:") if wmTNC == nil { fmt.Println(" (not initialized)") } else if heard := wmTNC.Heard(); len(heard) == 0 { fmt.Println(" (none)") } else { for call, t := range heard { pf(call, t) } } fmt.Println("ardop:") if adTNC == nil { fmt.Println(" (not initialized)") } else if heard := adTNC.Heard(); len(heard) == 0 { fmt.Println(" (none)") } else { for call, t := range heard { pf(call, t) } } fmt.Println("ax25:") if heard, err := ax25.Heard(config.AX25.Port); err != nil { fmt.Printf(" (%s)\n", err) } else if len(heard) == 0 { fmt.Println(" (none)") } else { for call, t := range heard { pf(call, t) } } } func PrintQTC() { msgs, err := mbox.Outbox() if err != nil { log.Println(err) return } fmt.Printf("QTC: %d.\n", len(msgs)) for _, msg := range msgs { fmt.Printf(`%-12.12s (%s): %s`, msg.MID(), msg.Subject(), fmt.Sprint(msg.To())) if msg.Header.Get("X-P2POnly") == "true" { fmt.Printf(" (P2P only)") } fmt.Println("") } } func parseCommand(str string) (mode, param string) { parts := strings.SplitN(str, " ", 2) if len(parts) == 1 { return parts[0], "" } return parts[0], parts[1] } pat-0.12.1/internal/000077500000000000000000000000001415513746300142045ustar00rootroot00000000000000pat-0.12.1/internal/buildinfo/000077500000000000000000000000001415513746300161575ustar00rootroot00000000000000pat-0.12.1/internal/buildinfo/VERSION.go000066400000000000000000000006571415513746300176430ustar00rootroot00000000000000// Copyright 2017 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package buildinfo const ( // AppName is the friendly name of the app. AppName = "Pat" // Version is the app's SemVer. Version = "0.12.1" ) // GitRev is the git commit hash that the binary was built at. var GitRev = "unknown origin" // Set by make.bash pat-0.12.1/internal/buildinfo/strings.go000066400000000000000000000013751415513746300202050ustar00rootroot00000000000000package buildinfo import ( "fmt" "runtime" ) // VersionString returns a very descriptive version including the app SemVer, git rev plus the // Golang OS, architecture and version. func VersionString() string { return fmt.Sprintf("%s %s/%s - %s", VersionStringShort(), runtime.GOOS, runtime.GOARCH, runtime.Version()) } // VersionStringShort returns the app SemVer and git rev. func VersionStringShort() string { return fmt.Sprintf("v%s (%s)", Version, GitRev) } // UserAgent returns a suitable HTTP user agent string containing app name, SemVer, git rev, plus // the Golang OS, architecture and version. func UserAgent() string { return fmt.Sprintf("%v/%v (%v) %v (%v; %v)", AppName, Version, GitRev, runtime.Version(), runtime.GOOS, runtime.GOARCH) } pat-0.12.1/internal/cmsapi/000077500000000000000000000000001415513746300154605ustar00rootroot00000000000000pat-0.12.1/internal/cmsapi/api.go000066400000000000000000000104451415513746300165640ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package cmsapi import ( "bytes" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "os" "time" ) const ( RootURL = "https://api.winlink.org" PathVersionAdd = "/version/add" PathGatewayStatus = "/gateway/status.json" PathAccountExists = "/account/exists" // AccessKey issued December 2017 by the WDT for use with Pat AccessKey = "1880278F11684B358F36845615BD039A" ) type VersionAdd struct { Callsign string `json:"callsign"` Program string `json:"program"` Version string `json:"version"` Comments string `json:"comments,omitempty"` } func (v VersionAdd) Post() error { b, _ := json.Marshal(v) buf := bytes.NewBuffer(b) versionURL := RootURL + PathVersionAdd + "?key=" + AccessKey req, _ := http.NewRequest("POST", versionURL, buf) req.Header.Set("content-type", "application/json") req.Header.Set("accept", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() var response map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { return err } if errMsg, ok := response["ErrorMessage"]; ok { return fmt.Errorf("Winlink CMS Web Services: %s", errMsg) } return nil } func AccountExists(callsign string) (bool, error) { accountURL := RootURL + PathAccountExists + "?key=" + AccessKey + "&callsign=" + url.QueryEscape(callsign) req, _ := http.NewRequest("GET", accountURL, nil) req.Header.Set("Accept", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return false, err } defer resp.Body.Close() var obj struct{ CallsignExists bool } return obj.CallsignExists, json.NewDecoder(resp.Body).Decode(&obj) } type GatewayStatus struct { ServerName string `json:"ServerName"` ErrorCode int `json:"ErrorCode"` Gateways []Gateway `json:"Gateways"` } type Gateway struct { Callsign string BaseCallsign string RequestedMode string Comments string LastStatus RFC1123Time Latitude float64 Longitude float64 Channels []GatewayChannel `json:"GatewayChannels"` } type GatewayChannel struct { OperatingHours string SupportedModes string Frequency float64 ServiceCode string Baud string RadioRange string Mode int Gridsquare string Antenna string } type RFC1123Time struct{ time.Time } // GetGatewayStatus fetches the gateway status list returned by GatewayStatusUrl // // mode can be any of [packet, pactor, winmor, robustpacket, allhf or anyall]. Empty is AnyAll. // historyHours is the number of hours of history to include (maximum: 48). If < 1, then API default is used. // serviceCodes defaults to "PUBLIC". func GetGatewayStatus(mode string, historyHours int, serviceCodes ...string) (io.ReadCloser, error) { switch { case mode == "": mode = "AnyAll" case historyHours > 48: historyHours = 48 case len(serviceCodes) == 0: serviceCodes = []string{"PUBLIC"} } params := url.Values{"Mode": {mode}} params.Set("key", AccessKey) if historyHours >= 0 { params.Add("HistoryHours", fmt.Sprintf("%d", historyHours)) } for _, str := range serviceCodes { params.Add("ServiceCodes", str) } resp, err := http.PostForm(RootURL+PathGatewayStatus, params) switch { case err != nil: return nil, err case resp.StatusCode != http.StatusOK: return nil, fmt.Errorf("unexpected http status '%v'", resp.Status) } return resp.Body, err } func GetGatewayStatusCached(cacheFile string, forceDownload bool, serviceCodes ...string) (io.ReadCloser, error) { if !forceDownload { file, err := os.Open(cacheFile) if err == nil { return file, nil } } log.Println("Downloading latest gateway status information...") fresh, err := GetGatewayStatus("", 48, serviceCodes...) if err != nil { return nil, err } file, err := os.Create(cacheFile) if err != nil { return nil, err } _, err = io.Copy(file, fresh) file.Seek(0, 0) if err == nil { log.Println("download succeeded.") } return file, err } func (t *RFC1123Time) UnmarshalJSON(b []byte) (err error) { var str string if err = json.Unmarshal(b, &str); err != nil { return err } t.Time, err = time.Parse(time.RFC1123, str) return err } pat-0.12.1/internal/debug/000077500000000000000000000000001415513746300152725ustar00rootroot00000000000000pat-0.12.1/internal/debug/debug.go000066400000000000000000000004541415513746300167120ustar00rootroot00000000000000package debug import ( "log" "os" "strconv" ) const ( EnvVar = "PAT_DEBUG" Prefix = "[DEBUG] " ) var enabled bool func init() { enabled, _ = strconv.ParseBool(os.Getenv(EnvVar)) } func Printf(format string, v ...interface{}) { if !enabled { return } log.Printf(Prefix+format, v...) } pat-0.12.1/internal/directories/000077500000000000000000000000001415513746300165205ustar00rootroot00000000000000pat-0.12.1/internal/directories/directories.go000066400000000000000000000067251415513746300213750ustar00rootroot00000000000000package directories import ( "errors" "log" "os" "path/filepath" "strings" "sync" "github.com/la5nta/pat/internal/buildinfo" "github.com/la5nta/pat/internal/debug" "github.com/adrg/xdg" ) var ( lock = &sync.Mutex{} dataPath string configPath string statePath string ) func DataDir() string { return getDir(&dataPath, xdg.DataHome, "DataDir") } func ConfigDir() string { return getDir(&configPath, xdg.ConfigHome, "ConfigDir") } func StateDir() string { return getDir(&statePath, xdg.StateHome, "StateDir") } func getDir(dir *string, basePath string, methodName string) string { lock.Lock() defer lock.Unlock() if *dir == "" { initDir(dir, basePath, methodName) } return *dir } func initDir(dir *string, basePath string, methodName string) { *dir = filepath.Join(basePath, strings.ToLower(buildinfo.AppName)) if _, err := os.Stat(*dir); os.IsNotExist(err) { err := os.MkdirAll(*dir, os.ModeDir|0o755) if err != nil { log.Fatalf("unable to create or open %s %s: %v", methodName, *dir, err) } } } func MigrateLegacyDataDir() { if f, err := os.Stat(ConfigDir()); err != nil && f.IsDir() { debug.Printf("new config directory %s already exists, we have already migrated", ConfigDir()) return } homeDir, err := os.UserHomeDir() if err != nil { log.Fatal(err) } legacyDataDir := filepath.Join(homeDir, ".wl2k") switch f, err := os.Stat(legacyDataDir); { case os.IsNotExist(err): debug.Printf("tried to migrate from %s but it doesn't exist; nothing to do", legacyDataDir) return case err != nil: log.Fatal(err) case !f.IsDir(): log.Printf("tried to migrate from %s but it's not a directory, that's weird; ignoring", legacyDataDir) return } log.Printf("Migrating your Pat files from %s to new locations", legacyDataDir) if err = migrateFile("config.json", legacyDataDir, ConfigDir()); err != nil { log.Fatal(err) } if err = migrateFile("mailbox", legacyDataDir, DataDir()); err != nil { log.Fatal(err) } if err = migrateFile("Standard_Forms", legacyDataDir, DataDir()); err != nil { log.Fatal(err) } matches, err := filepath.Glob(filepath.Join(legacyDataDir, "rmslist*.json")) if err != nil { log.Fatal(err) } for _, match := range matches { _, f := filepath.Split(match) if err = migrateFile(f, legacyDataDir, DataDir()); err != nil { log.Fatal(err) } } debug.Printf("migration from %s finished, renaming it", legacyDataDir) err = os.Rename(legacyDataDir, legacyDataDir+"-old") if err != nil { log.Fatal(err) } } func migrateFile(fileName string, fromDir string, toDir string) error { // make sure the old file is there fromFile := filepath.Join(fromDir, fileName) if _, err := os.Stat(fromFile); errors.Is(err, os.ErrNotExist) { // no legacy file, nothing to do debug.Printf("File %s doesn't exist, not migrating it", fromFile) return nil } else if err != nil { return err } // touch the new file to make sure it's not there, and we can write to it toFile := filepath.Join(toDir, fileName) switch f, err := os.OpenFile(toFile, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666); { case errors.Is(err, os.ErrExist): // new file already exists, don't clobber it debug.Printf("new file %s already exists; ignoring %s", toFile, fromFile) return nil case err != nil: return err default: if err := f.Close(); err != nil { return err } if err := os.Remove(toFile); err != nil { return err } } debug.Printf("Migrating %s from %s to %s", fileName, fromDir, toDir) return os.Rename(fromFile, toFile) } pat-0.12.1/internal/forms/000077500000000000000000000000001415513746300153325ustar00rootroot00000000000000pat-0.12.1/internal/forms/forms.go000066400000000000000000000726071415513746300170230ustar00rootroot00000000000000// Copyright 2020 Rainer Grosskopf (KI7RMJ). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. // Processes Winlink-compatible message template (aka Winlink forms) package forms import ( "archive/zip" "bufio" "bytes" "encoding/json" "encoding/xml" "errors" "fmt" "io" "io/ioutil" "log" "net/http" "os" "path" "path/filepath" "regexp" "sort" "strconv" "strings" "sync" "time" "unicode/utf8" "github.com/dimchansky/utfbom" ) const ( fieldValueFalseInXML = "False" txtFileExt = ".txt" formsVersionInfoURL = "https://api.getpat.io/v1/forms/standard-templates/latest" ) // Manager manages the forms subsystem // When the web frontend POSTs the form template data, this map holds the POST'ed data. // Each form composer instance renders into another browser tab, and has a unique instance cookie. // This instance cookie is the key into the map, so that we can keep the values // from different form authoring sessions separate from each other. type Manager struct { config Config postedFormData struct { sync.RWMutex internalFormDataMap map[string]FormData } } // Config passes config options to the forms package type Config struct { FormsPath string MyCall string Locator string AppVersion string LineReader func() string UserAgent string } // Form holds information about a Winlink form template type Form struct { Name string `json:"name"` TxtFileURI string `json:"txt_file_uri"` InitialURI string `json:"initial_uri"` ViewerURI string `json:"viewer_uri"` ReplyTxtFileURI string `json:"reply_txt_file_uri"` ReplyInitialURI string `json:"reply_initial_uri"` ReplyViewerURI string `json:"reply_viewer_uri"` } // FormFolder is a folder with forms. A tree structure with Form leaves and sub-Folder branches type FormFolder struct { Name string `json:"name"` Path string `json:"path"` Version string `json:"version"` FormCount int `json:"form_count"` Forms []Form `json:"forms"` Folders []FormFolder `json:"folders"` } // FormData holds the instance data that define a filled-in form type FormData struct { TargetForm Form `json:"target_form"` Fields map[string]string `json:"fields"` MsgSubject string `json:"msg_subject"` MsgBody string `json:"msg_body"` MsgXML string `json:"msg_xml"` IsReply bool `json:"is_reply"` Submitted time.Time `json:"submitted"` } // MessageForm represents a concrete form-based message type MessageForm struct { Subject string Body string AttachmentXML string AttachmentName string } // UpdateResponse is the API response format for the upgrade forms endpoint type UpdateResponse struct { NewestVersion string `json:"newestVersion"` Action string `json:"action"` } var client = httpClient{http.Client{Timeout: 10 * time.Second}} // NewManager instantiates the forms manager func NewManager(conf Config) *Manager { _ = os.MkdirAll(conf.FormsPath, 0o755) retval := &Manager{ config: conf, } retval.postedFormData.internalFormDataMap = make(map[string]FormData) return retval } // GetFormsCatalogHandler reads all forms from config.FormsPath and writes them in the http response as a JSON object graph // This lets the frontend present a tree-like GUI for the user to select a form for composing a message func (m *Manager) GetFormsCatalogHandler(w http.ResponseWriter, r *http.Request) { formFolder, err := m.buildFormFolder() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) log.Printf("%s %s: %s", r.Method, r.URL.Path, err) return } _ = json.NewEncoder(w).Encode(formFolder) } // PostFormDataHandler - When the user is done filling a form, the frontend posts the input fields to this handler, // which stores them in a map, so that other browser tabs can read the values back with GetFormDataHandler func (m *Manager) PostFormDataHandler(w http.ResponseWriter, r *http.Request) { if err := r.ParseMultipartForm(10e6); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } formPath := r.URL.Query().Get("formPath") if formPath == "" { http.Error(w, "formPath query param missing", http.StatusBadRequest) log.Printf("formPath query param missing %s %s", r.Method, r.URL.Path) return } composeReply, _ := strconv.ParseBool(r.URL.Query().Get("composereply")) formFolder, err := m.buildFormFolder() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) log.Printf("%s %s: %s", r.Method, r.URL.Path, err) return } form, err := findFormFromURI(formPath, formFolder) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) log.Printf("can't find form to match posted form data %s %s", formPath, r.URL) return } formInstanceKey, err := r.Cookie("forminstance") if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) log.Printf("missing cookie %s %s", formPath, r.URL) return } formData := FormData{ IsReply: composeReply, TargetForm: form, Fields: make(map[string]string), } for key, values := range r.PostForm { formData.Fields[strings.TrimSpace(strings.ToLower(key))] = values[0] } formMsg, err := formMessageBuilder{ Template: form, FormValues: formData.Fields, Interactive: false, IsReply: composeReply, FormsMgr: m, }.build() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) log.Printf("%s %s: %s", r.Method, r.URL.Path, err) } formData.MsgSubject = formMsg.Subject formData.MsgBody = formMsg.Body formData.MsgXML = formMsg.AttachmentXML formData.Submitted = time.Now() m.postedFormData.Lock() m.postedFormData.internalFormDataMap[formInstanceKey.Value] = formData m.postedFormData.Unlock() m.cleanupOldFormData() _, _ = io.WriteString(w, "") } // GetFormDataHandler is the counterpart to PostFormDataHandler. Returns the form field values to the frontend func (m *Manager) GetFormDataHandler(w http.ResponseWriter, r *http.Request) { formInstanceKey, err := r.Cookie("forminstance") if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) log.Printf("missing cookie %s %s", formInstanceKey, r.URL) return } _ = json.NewEncoder(w).Encode(m.GetPostedFormData(formInstanceKey.Value)) } // GetPostedFormData is similar to GetFormDataHandler, but used when posting the form-based message to the outbox func (m *Manager) GetPostedFormData(key string) FormData { m.postedFormData.RLock() retVal := m.postedFormData.internalFormDataMap[key] m.postedFormData.RUnlock() return retVal } // GetFormTemplateHandler handles the request for viewing a form filled-in with instance values func (m *Manager) GetFormTemplateHandler(w http.ResponseWriter, r *http.Request) { formPath := r.URL.Query().Get("formPath") if formPath == "" { http.Error(w, "formPath query param missing", http.StatusBadRequest) log.Printf("formPath query param missing %s %s", r.Method, r.URL.Path) return } absPathTemplate, err := m.findAbsPathForTemplatePath(formPath) if err != nil { http.Error(w, "find the full path for requested template "+formPath, http.StatusBadRequest) log.Printf("find the full path for requested template %s %s: %s", r.Method, r.URL.Path, "can't open template "+formPath) return } responseText, err := m.fillFormTemplate(absPathTemplate, "/api/form?"+r.URL.Query().Encode(), nil, make(map[string]string)) if err != nil { http.Error(w, "can't open template "+formPath, http.StatusBadRequest) log.Printf("problem filling form template file %s %s: can't open template %s. Err: %s", r.Method, r.URL.Path, formPath, err) return } _, err = io.WriteString(w, responseText) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) log.Printf("can't write form into response %s %s: %s", r.Method, r.URL.Path, err) return } } // UpdateFormTemplatesHandler handles API calls to update form templates. func (m *Manager) UpdateFormTemplatesHandler(w http.ResponseWriter, _ *http.Request) { response, err := m.UpdateFormTemplates() if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } jsn, _ := json.Marshal(response) _, _ = w.Write(jsn) } // UpdateFormTemplates handles searching for and installing the latest version of the form templates. func (m *Manager) UpdateFormTemplates() (UpdateResponse, error) { if _, err := os.Stat(m.config.FormsPath); err != nil { if err := os.MkdirAll(m.config.FormsPath, 0o755); err != nil { return UpdateResponse{}, fmt.Errorf("can't write to forms dir [%w]", err) } } log.Printf("Updating form templates; current version is %v", m.getFormsVersion()) latest, err := m.getLatestFormsInfo() if err != nil { return UpdateResponse{}, err } if !m.isNewerVersion(latest.Version) { log.Printf("Latest forms version is %v; nothing to do", latest.Version) return UpdateResponse{ NewestVersion: latest.Version, Action: "none", }, nil } err = m.downloadAndUnzipForms(latest.ArchiveURL) if err != nil { return UpdateResponse{}, err } log.Printf("Finished forms update to %v", latest.Version) // TODO: re-init forms manager return UpdateResponse{ NewestVersion: latest.Version, Action: "update", }, nil } type formsInfo struct { Version string `json:"version"` ArchiveURL string `json:"archive_url"` } func (m *Manager) getLatestFormsInfo() (*formsInfo, error) { resp, err := client.Get(m, formsVersionInfoURL) if err != nil || resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("can't fetch winlink forms version page: %w", err) } defer resp.Body.Close() var v formsInfo if err := json.NewDecoder(resp.Body).Decode(&v); err != nil { return nil, err } return &v, nil } func (m *Manager) downloadAndUnzipForms(downloadLink string) error { log.Printf("Updating forms via %v", downloadLink) resp, err := client.Get(m, downloadLink) if err != nil { return fmt.Errorf("can't download update ZIP: %w", err) } defer resp.Body.Close() f, err := ioutil.TempFile(os.TempDir(), "pat") if err != nil { return fmt.Errorf("can't create temp file for download: %w", err) } defer f.Close() defer os.Remove(f.Name()) if _, err := io.Copy(f, resp.Body); err != nil { return fmt.Errorf("can't write update ZIP: %w", err) } unzipDir := m.config.FormsPath if err := unzip(f.Name(), unzipDir); err != nil { return fmt.Errorf("can't unzip forms update: %w", err) } return nil } func unzip(srcArchivePath, dstRoot string) error { // Closure to address file descriptors issue with all the deferred .Close() methods extractAndWriteFile := func(zf *zip.File) error { if zf.FileInfo().IsDir() { return nil } destPath := filepath.Join(dstRoot, zf.Name) // Check for ZipSlip (Directory traversal) if !strings.HasPrefix(destPath, filepath.Clean(dstRoot)+string(os.PathSeparator)) { return fmt.Errorf("illegal file path: %s", destPath) } // Ensure target directory exists if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil { return fmt.Errorf("can't create target directory: %w", err) } // Write file src, err := zf.Open() if err != nil { return err } defer src.Close() dst, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, zf.Mode()) if err != nil { return err } defer dst.Close() _, err = io.Copy(dst, src) return err } r, err := zip.OpenReader(srcArchivePath) if err != nil { return err } defer r.Close() for _, f := range r.File { err := extractAndWriteFile(f) if err != nil { return err } } return nil } // GetXMLAttachmentNameForForm returns the user-visible filename for the message attachment that holds the form instance values func (m *Manager) GetXMLAttachmentNameForForm(f Form, isReply bool) string { attachmentName := filepath.Base(f.ViewerURI) if isReply { attachmentName = filepath.Base(f.ReplyViewerURI) } attachmentName = strings.TrimSuffix(attachmentName, filepath.Ext(attachmentName)) attachmentName = "RMS_Express_Form_" + attachmentName + ".xml" if len(attachmentName) > 255 { attachmentName = strings.TrimPrefix(attachmentName, "RMS_Express_Form_") } return attachmentName } // RenderForm finds the associated form and returns the filled-in form in HTML given the contents of a form attachment func (m *Manager) RenderForm(contentUnsanitized []byte, composeReply bool) (string, error) { type Node struct { XMLName xml.Name Content []byte `xml:",innerxml"` Nodes []Node `xml:",any"` } sr := utfbom.SkipOnly(bytes.NewReader(contentUnsanitized)) contentData, err := io.ReadAll(sr) if err != nil { return "", fmt.Errorf("error reading sanitized form xml: %w", err) } if !utf8.Valid(contentData) { log.Println("Warning: unsupported string encoding in form XML, expected utf-8") } var n1 Node formParams := make(map[string]string) formVars := make(map[string]string) if err := xml.Unmarshal(contentData, &n1); err != nil { return "", err } if n1.XMLName.Local != "RMS_Express_Form" { return "", errors.New("missing RMS_Express_Form tag in form XML") } for _, n2 := range n1.Nodes { switch n2.XMLName.Local { case "form_parameters": for _, n3 := range n2.Nodes { formParams[n3.XMLName.Local] = string(n3.Content) } case "variables": for _, n3 := range n2.Nodes { formVars[n3.XMLName.Local] = string(n3.Content) } } } switch { case formParams["display_form"] == "": return "", errors.New("missing display_form tag in form XML") case composeReply && formParams["reply_template"] == "": return "", errors.New("missing reply_template tag in form XML for a reply message") } formFolder, err := m.buildFormFolder() if err != nil { return "", err } formToLoad := formParams["display_form"] if composeReply { // we're authoring a reply formToLoad = formParams["reply_template"] } form, err := findFormFromURI(formToLoad, formFolder) if err != nil { return "", err } var formRelPath string switch { case composeReply: // authoring a form reply formRelPath = form.ReplyInitialURI case strings.HasSuffix(form.ReplyViewerURI, formParams["display_form"]): // viewing a form reply formRelPath = form.ReplyViewerURI default: // viewing a form formRelPath = form.ViewerURI } absPathTemplate, err := m.findAbsPathForTemplatePath(formRelPath) if err != nil { return "", err } retVal, err := m.fillFormTemplate(absPathTemplate, "/api/form?composereply=true&formPath="+formRelPath, regexp.MustCompile(`{var\s+(\w+)\s*}`), formVars) return retVal, err } // ComposeForm combines all data needed for the whole form-based message: subject, body, and attachment func (m *Manager) ComposeForm(tmplPath string, subject string) (MessageForm, error) { formFolder, err := m.buildFormFolder() if err != nil { log.Printf("can't build form folder tree %s", err) return MessageForm{}, err } tmplPath = filepath.Clean(tmplPath) form, err := findFormFromURI(tmplPath, formFolder) if err != nil { log.Printf("can't find form to match form %s", tmplPath) return MessageForm{}, err } varMap := map[string]string{ "subjectline": subject, "templateversion": m.getFormsVersion(), "msgsender": m.config.MyCall, } fmt.Printf("Form '%s', version: %s", form.TxtFileURI, varMap["templateversion"]) formMsg, err := formMessageBuilder{ Template: form, FormValues: varMap, Interactive: true, IsReply: false, FormsMgr: m, }.build() if err != nil { err = fmt.Errorf("could not open form file: %w", err) log.Print(err) return MessageForm{}, err } return formMsg, nil } func (f Form) matchesName(nameToMatch string) bool { return f.InitialURI == nameToMatch || f.InitialURI == nameToMatch+".html" || f.ViewerURI == nameToMatch || f.ViewerURI == nameToMatch+".html" || f.ReplyInitialURI == nameToMatch || f.ReplyInitialURI == nameToMatch+".0" || f.ReplyViewerURI == nameToMatch || f.ReplyViewerURI == nameToMatch+".0" || f.TxtFileURI == nameToMatch || f.TxtFileURI == nameToMatch+".txt" } func (f Form) containsName(partialName string) bool { return strings.Contains(f.InitialURI, partialName) || strings.Contains(f.ViewerURI, partialName) || strings.Contains(f.ReplyInitialURI, partialName) || strings.Contains(f.ReplyViewerURI, partialName) || strings.Contains(f.ReplyTxtFileURI, partialName) || strings.Contains(f.TxtFileURI, partialName) } func (m *Manager) buildFormFolder() (FormFolder, error) { formFolder, err := m.innerRecursiveBuildFormFolder(m.config.FormsPath) formFolder.Version = m.getFormsVersion() return formFolder, err } func (m *Manager) innerRecursiveBuildFormFolder(rootPath string) (FormFolder, error) { rootFile, err := os.Open(rootPath) if err != nil { return FormFolder{}, err } defer rootFile.Close() rootFileInfo, _ := os.Stat(rootPath) if !rootFileInfo.IsDir() { return FormFolder{}, errors.New(rootPath + " is not a directory") } retVal := FormFolder{ Name: rootFileInfo.Name(), Path: rootFile.Name(), Forms: []Form{}, Folders: []FormFolder{}, } infos, err := rootFile.Readdir(0) if err != nil { return retVal, err } _ = rootFile.Close() formCnt := 0 for _, info := range infos { if info.IsDir() { subfolder, err := m.innerRecursiveBuildFormFolder(path.Join(rootPath, info.Name())) if err != nil { return retVal, err } retVal.Folders = append(retVal.Folders, subfolder) retVal.FormCount += subfolder.FormCount continue } if filepath.Ext(info.Name()) != txtFileExt { continue } frm, err := m.buildFormFromTxt(path.Join(rootPath, info.Name())) if err != nil { continue } if frm.InitialURI != "" || frm.ViewerURI != "" { formCnt++ retVal.Forms = append(retVal.Forms, frm) retVal.FormCount++ } } sort.Slice(retVal.Folders, func(i, j int) bool { return retVal.Folders[i].Name < retVal.Folders[j].Name }) sort.Slice(retVal.Forms, func(i, j int) bool { return retVal.Forms[i].Name < retVal.Forms[j].Name }) return retVal, nil } func (m *Manager) buildFormFromTxt(txtPath string) (Form, error) { f, err := os.Open(txtPath) if err != nil { return Form{}, err } defer f.Close() formsPathWithSlash := m.config.FormsPath + "/" retVal := Form{ Name: strings.TrimSuffix(path.Base(txtPath), ".txt"), TxtFileURI: strings.TrimPrefix(txtPath, formsPathWithSlash), } scanner := bufio.NewScanner(f) baseURI := path.Dir(retVal.TxtFileURI) for scanner.Scan() { l := scanner.Text() switch { case strings.HasPrefix(l, "Form:"): trimmed := strings.TrimSpace(strings.TrimPrefix(l, "Form:")) fileNames := strings.Split(trimmed, ",") if len(fileNames) >= 2 { initial := strings.TrimSpace(fileNames[0]) viewer := strings.TrimSpace(fileNames[1]) retVal.InitialURI = path.Join(baseURI, initial) retVal.ViewerURI = path.Join(baseURI, viewer) } else { view := strings.TrimSpace(fileNames[0]) retVal.InitialURI = path.Join(baseURI, view) retVal.ViewerURI = path.Join(baseURI, view) } case strings.HasPrefix(l, "ReplyTemplate:"): retVal.ReplyTxtFileURI = path.Join(baseURI, strings.TrimSpace(strings.TrimPrefix(l, "ReplyTemplate:"))) tmpForm, _ := m.buildFormFromTxt(path.Join(m.config.FormsPath, retVal.ReplyTxtFileURI)) retVal.ReplyInitialURI = tmpForm.InitialURI retVal.ReplyViewerURI = tmpForm.ViewerURI } } return retVal, err } func findFormFromURI(formName string, folder FormFolder) (Form, error) { retVal := Form{Name: "unknown"} for _, subFolder := range folder.Folders { form, err := findFormFromURI(formName, subFolder) if err == nil { return form, nil } } for _, form := range folder.Forms { if form.matchesName(formName) { return form, nil } } // couldn't find it by full path, so try to find match by guessing folder name formName = path.Join(folder.Name, formName) for _, form := range folder.Forms { if form.containsName(formName) { return form, nil } } return retVal, errors.New("form not found") } func (m *Manager) findAbsPathForTemplatePath(tmplPath string) (string, error) { absPathTemplate := filepath.Join(m.config.FormsPath, path.Clean(tmplPath)) // now deal with cases where the html file name specified in the .txt file, has different caseness than the actual .html file on disk. absPathTemplateFolder := filepath.Dir(absPathTemplate) templateDir, err := os.Open(absPathTemplateFolder) if err != nil { return "", errors.New("can't read template folder") } defer templateDir.Close() fileNames, err := templateDir.Readdirnames(0) if err != nil { return "", errors.New("can't read template folder") } var retVal string for _, name := range fileNames { if strings.EqualFold(filepath.Base(tmplPath), name) { retVal = filepath.Join(absPathTemplateFolder, name) break } } return retVal, nil } func (m *Manager) fillFormTemplate(absPathTemplate string, formDestURL string, placeholderRegEx *regexp.Regexp, formVars map[string]string) (string, error) { fUnsanitized, err := os.Open(absPathTemplate) if err != nil { return "", err } defer fUnsanitized.Close() // skipping over UTF-8 byte-ordering mark EFBBEF, some 3rd party templates use it // (e.g. Sonoma county's ICS213_v2.1_SonomaACS_TwoWay_Initial_Viewer.html) f := utfbom.SkipOnly(fUnsanitized) sanitizedFileContent, err := io.ReadAll(f) if err != nil { return "", fmt.Errorf("error reading file %s", absPathTemplate) } if !utf8.Valid(sanitizedFileContent) { log.Printf("Warning: unsupported string encoding in template %s, expected utf-8", absPathTemplate) } retVal := "" now := time.Now() nowDateTime := now.Format("2006-01-02 15:04:05") nowDateTimeUTC := now.UTC().Format("2006-01-02 15:04:05Z") nowDate := now.Format("2006-01-02") nowTime := now.Format("15:04:05") nowDateUTC := now.UTC().Format("2006-01-02Z") nowTimeUTC := now.UTC().Format("15:04:05Z") udtg := strings.ToUpper(now.UTC().Format("021504Z Jan 2006")) scanner := bufio.NewScanner(bytes.NewReader(sanitizedFileContent)) for scanner.Scan() { l := scanner.Text() l = strings.ReplaceAll(l, "http://{FormServer}:{FormPort}", formDestURL) // some Canada BC forms don't use the {FormServer} placeholder, it's OK, can deal with it here l = strings.ReplaceAll(l, "http://localhost:8001", formDestURL) l = strings.ReplaceAll(l, "{MsgSender}", m.config.MyCall) l = strings.ReplaceAll(l, "{Callsign}", m.config.MyCall) l = strings.ReplaceAll(l, "{ProgramVersion}", "Pat "+m.config.AppVersion) l = strings.ReplaceAll(l, "{DateTime}", nowDateTime) l = strings.ReplaceAll(l, "{UDateTime}", nowDateTimeUTC) l = strings.ReplaceAll(l, "{Date}", nowDate) l = strings.ReplaceAll(l, "{UDate}", nowDateUTC) l = strings.ReplaceAll(l, "{UDTG}", udtg) l = strings.ReplaceAll(l, "{Time}", nowTime) l = strings.ReplaceAll(l, "{UTime}", nowTimeUTC) if placeholderRegEx != nil { l = fillPlaceholders(l, placeholderRegEx, formVars) } retVal += l + "\n" } return retVal, nil } func (m *Manager) getFormsVersion() string { // walking up the path to find a version file. // Winlink's Standard_Forms.zip includes it in its root. dir := m.config.FormsPath if filepath.Ext(dir) == txtFileExt { dir = filepath.Dir(dir) } var verFile *os.File // loop to walk up the subfolders until we find the top, or Winlink's Standard_Forms_Version.dat file for { f, err := os.Open(filepath.Join(dir, "Standard_Forms_Version.dat")) if err != nil { dir = filepath.Dir(dir) // have not found the version file or couldn't open it, going up by one if dir == "." || dir == ".." || strings.HasSuffix(dir, string(os.PathSeparator)) { return "unknown" // reached top-level and couldn't find version .dat file } continue } // found and opened the version file verFile = f break } if verFile != nil { defer verFile.Close() return readFileFirstLine(verFile) } return "unknown" } func readFileFirstLine(f *os.File) string { scanner := bufio.NewScanner(f) if scanner.Scan() { return scanner.Text() } return "" } type formMessageBuilder struct { Interactive bool IsReply bool Template Form FormValues map[string]string FormsMgr *Manager } // build returns message subject, body, and XML attachment content for the given template and variable map func (b formMessageBuilder) build() (MessageForm, error) { tmplPath := filepath.Join(b.FormsMgr.config.FormsPath, b.Template.TxtFileURI) if filepath.Ext(tmplPath) == "" { tmplPath += txtFileExt } if b.IsReply && b.Template.ReplyTxtFileURI != "" { tmplPath = filepath.Join(b.FormsMgr.config.FormsPath, b.Template.ReplyTxtFileURI) } retVal, err := b.scanTmplBuildMessage(tmplPath) if err != nil { return MessageForm{}, err } b.initFormValues() formVarsAsXML := "" for varKey, varVal := range b.FormValues { formVarsAsXML += fmt.Sprintf(" <%s>%s\n", xmlEscape(varKey), xmlEscape(varVal), xmlEscape(varKey)) } viewer := "" if b.Template.ViewerURI != "" { viewer = filepath.Base(b.Template.ViewerURI) } if b.IsReply && b.Template.ReplyViewerURI != "" { viewer = filepath.Base(b.Template.ReplyViewerURI) } replier := "" if !b.IsReply && b.Template.ReplyTxtFileURI != "" { replier = filepath.Base(b.Template.ReplyTxtFileURI) } retVal.AttachmentXML = fmt.Sprintf(`%s %s %s %s %s %s %s %s %s `, xml.Header, "1.0", b.FormsMgr.config.AppVersion, time.Now().UTC().Format("20060102150405"), b.FormsMgr.config.MyCall, b.FormsMgr.config.Locator, viewer, replier, formVarsAsXML) retVal.AttachmentName = b.FormsMgr.GetXMLAttachmentNameForForm(b.Template, false) retVal.Subject = strings.TrimSpace(retVal.Subject) retVal.Body = strings.TrimSpace(retVal.Body) return retVal, nil } func (b formMessageBuilder) initFormValues() { if b.IsReply { b.FormValues["msgisreply"] = "True" } else { b.FormValues["msgisreply"] = "False" } b.FormValues["msgsender"] = b.FormsMgr.config.MyCall // some defaults that we can't set yet. Winlink doesn't seem to care about these b.FormValues["msgto"] = "" b.FormValues["msgcc"] = "" b.FormValues["msgsubject"] = "" b.FormValues["msgbody"] = "" b.FormValues["msgp2p"] = "" b.FormValues["msgisforward"] = fieldValueFalseInXML b.FormValues["msgisacknowledgement"] = fieldValueFalseInXML b.FormValues["msgseqnum"] = "0" } func (b formMessageBuilder) scanTmplBuildMessage(tmplPath string) (MessageForm, error) { infile, err := os.Open(tmplPath) if err != nil { return MessageForm{}, err } defer infile.Close() placeholderRegEx := regexp.MustCompile(`<[vV][aA][rR]\s+(\w+)\s*>`) scanner := bufio.NewScanner(infile) var retVal MessageForm for scanner.Scan() { lineTmpl := scanner.Text() lineTmpl = fillPlaceholders(lineTmpl, placeholderRegEx, b.FormValues) lineTmpl = strings.ReplaceAll(lineTmpl, "", b.FormsMgr.config.MyCall) lineTmpl = strings.ReplaceAll(lineTmpl, "", "Pat "+b.FormsMgr.config.AppVersion) if strings.HasPrefix(lineTmpl, "Form:") || strings.HasPrefix(lineTmpl, "ReplyTemplate:") || strings.HasPrefix(lineTmpl, "To:") || strings.HasPrefix(lineTmpl, "Msg:") { continue } if b.Interactive { matches := placeholderRegEx.FindAllStringSubmatch(lineTmpl, -1) fmt.Println(lineTmpl) for i := range matches { varName := matches[i][1] varNameLower := strings.ToLower(varName) if b.FormValues[varNameLower] != "" { continue } fmt.Print(varName + ": ") b.FormValues[varNameLower] = "blank" val := b.FormsMgr.config.LineReader() if val != "" { b.FormValues[varNameLower] = val } } } lineTmpl = fillPlaceholders(lineTmpl, placeholderRegEx, b.FormValues) if strings.HasPrefix(lineTmpl, "Subject:") { retVal.Subject = strings.TrimPrefix(lineTmpl, "Subject:") } else { retVal.Body += lineTmpl + "\n" } } return retVal, nil } func xmlEscape(s string) string { var buf bytes.Buffer if err := xml.EscapeText(&buf, []byte(s)); err != nil { log.Printf("Error trying to escape XML string %s", err) } return buf.String() } func fillPlaceholders(s string, re *regexp.Regexp, values map[string]string) string { if _, ok := values["txtstr"]; !ok { values["txtstr"] = "" } result := s matches := re.FindAllStringSubmatch(s, -1) for _, match := range matches { value, ok := values[strings.ToLower(match[1])] if ok { result = strings.ReplaceAll(result, match[0], value) } } return result } func (m *Manager) cleanupOldFormData() { m.postedFormData.Lock() for key, form := range m.postedFormData.internalFormDataMap { elapsed := time.Since(form.Submitted).Hours() if elapsed > 24 { log.Println("deleting old FormData after", elapsed, "hrs") delete(m.postedFormData.internalFormDataMap, key) } } m.postedFormData.Unlock() } func (m *Manager) isNewerVersion(newestVersion string) bool { currentVersion := m.getFormsVersion() cv := strings.Split(currentVersion, ".") nv := strings.Split(newestVersion, ".") for i := 0; i < 4; i++ { var cp int64 if len(cv) > i { cp, _ = strconv.ParseInt(cv[i], 10, 16) } var np int64 if len(nv) > i { np, _ = strconv.ParseInt(nv[i], 10, 16) } if cp < np { return true } } return false } type httpClient struct{ http.Client } func (c httpClient) Get(m *Manager, url string) (*http.Response, error) { req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } req.Header.Set("User-Agent", m.config.UserAgent) req.Header.Set("Cache-Control", "no-cache") return c.Do(req) } pat-0.12.1/internal/gpsd/000077500000000000000000000000001415513746300151415ustar00rootroot00000000000000pat-0.12.1/internal/gpsd/gpsd.go000066400000000000000000000116661415513746300164370ustar00rootroot00000000000000package gpsd import ( "bufio" "encoding/json" "errors" "fmt" "io" "net" "sync" "time" ) type NMEAMode int const ( ModeUnknown NMEAMode = iota ModeNoFix Mode2D Mode3D ) var ErrUnsupportedProtocolVersion = errors.New("unsupported protocol version") // Positioner implementations provide geographic positioning data. // // This is particularly useful for testing if an object returned by Next can be used to determine the device position. type Positioner interface { Position() Position HasFix() bool } // Position holds geographic positioning data. type Position struct { Lat, Lon float64 // Latitude/longitude in degrees. +/- signifies north/south. Alt float64 // Altitude in meters. Track float64 // Course over ground, degrees from true north. Speed float64 // Speed over ground, meters per second. Time time.Time // Time as reported by the device. } // Conn represents a socket connection to an GPSd daemon. type Conn struct { Version Version mu sync.Mutex tcpConn net.Conn rd *bufio.Reader watchEnabled bool closed bool } // Dial establishes a socket connection to the GPSd daemon. func Dial(addr string) (*Conn, error) { tcpConn, err := net.DialTimeout("tcp", addr, 30*time.Second) if err != nil { return nil, err } c := &Conn{ tcpConn: tcpConn, rd: bufio.NewReader(tcpConn), } err = json.NewDecoder(c.rd).Decode(&c.Version) if err != nil || c.Version.Release == "" { tcpConn.Close() return nil, errors.New("unexpected server response") } if c.Version.ProtoMajor < 3 { tcpConn.Close() return nil, ErrUnsupportedProtocolVersion } return c, nil } // Watch enables or disables the watcher mode. // // In watcher mode, GPS reports are dumped as TPV and SKY objects. These objects are available through the Next method. func (c *Conn) Watch(enable bool) bool { c.mu.Lock() defer c.mu.Unlock() if c.closed { return false } if enable == c.watchEnabled { return enable } c.tcpConn.SetDeadline(time.Now().Add(30 * time.Second)) defer c.tcpConn.SetDeadline(time.Time{}) param, _ := json.Marshal( map[string]interface{}{ "class": "WATCH", "enable": enable, "json": true, }) c.send("?WATCH=%s", param) for { obj, err := c.next() if err != nil { return false } if watch, ok := obj.(watch); ok { c.watchEnabled = watch.Enable break } } return c.watchEnabled } // Close closes the GPSd daemon connection. func (c *Conn) Close() error { c.Watch(false) c.closed = true return c.tcpConn.Close() } // Next returns the next object sent from the daemon, or an error. // // The empty interface returned can be any of the following types: // * Sky: A Sky object reports a sky view of the GPS satellite positions. // * TPV: A TPV object is a time-position-velocity report. func (c *Conn) Next() (interface{}, error) { c.mu.Lock() defer c.mu.Unlock() for { obj, err := c.next() if err != nil { return nil, err } switch obj.(type) { case TPV, Sky: return obj, nil default: // Ignore other objects for now. } } } func (c *Conn) next() (interface{}, error) { line, err := c.rd.ReadBytes('\n') if err != nil { return nil, err } return parseJSONObject(line) } var ( ErrTimeout = errors.New("timeout") ErrWatchModeEnabled = errors.New("operation not available while in watch mode") ) // NextPos returns the next reported position. func (c *Conn) NextPos() (Position, error) { return c.NextPosTimeout(0) } // NextPosTimeout returns the next reported position, or an empty position on timeout. func (c *Conn) NextPosTimeout(timeout time.Duration) (Position, error) { var deadline time.Time if timeout > 0 { deadline = time.Now().Add(timeout) c.tcpConn.SetDeadline(deadline) defer c.tcpConn.SetDeadline(time.Time{}) } for { obj, err := c.Next() var netErr net.Error if ok := errors.As(err, &netErr); ok && netErr.Timeout() { return Position{}, ErrTimeout } else if err != nil { return Position{}, err } if pos, ok := obj.(Positioner); ok && pos.HasFix() { return pos.Position(), nil } if !deadline.IsZero() && time.Now().After(deadline) { return Position{}, ErrTimeout } } } // Devices returns a list of all devices GPSd is aware of. // // ErrWatchModeEnabled will be returned if the connection is in watch mode. // A nil-slice will be returned if the connection has been closed. func (c *Conn) Devices() ([]Device, error) { if c.closed { return nil, nil } else if c.watchEnabled { return nil, ErrWatchModeEnabled } c.mu.Lock() defer c.mu.Unlock() c.send("?DEVICES;") for { obj, err := c.next() if err != nil { return nil, errUnexpected(err) } if devs, ok := obj.([]Device); ok { return devs, nil } } } func (c *Conn) send(s string, params ...interface{}) error { _, err := fmt.Fprintf(c.tcpConn, s, params...) return errUnexpected(err) } func errUnexpected(err error) error { if errors.Is(err, io.EOF) { err = io.ErrUnexpectedEOF } return err } pat-0.12.1/internal/gpsd/objects.go000066400000000000000000000076621415513746300171340ustar00rootroot00000000000000package gpsd import ( "encoding/json" "errors" "time" ) // A Sky object reports a sky view of the GPS satellite positions. type Sky struct { Device string `json:"device,omitempty"` Time time.Time `json:"time,omitempty"` XDOP, YDOP, VDOP, TDOP, HDOP, PDOP, GDOP json.Number Satellites []Satellite `json:"satellites"` } // A TPV object is a time-position-velocity report. type TPV struct { Device string // Name of originating device. Mode NMEAMode // NMEA mode: %d, 0=no mode value yet seen, 1=no fix, 2=2D, 3=3D. Time time.Time // Time/date stamp. May have a fractional part of up to .001sec precision. May be absent if mode is not 2D or 3D. EPT json.Number // Estimated timestamp error (%f, seconds, 95% confidence). Present if time is present. Lat, Lon, Alt json.Number EPX, EPY, EPV json.Number // Lat, Lon, Alt error estimate in meters, 95% confidence. Present if mode is 2 or 3 and DOPs can be calculated from the satellite view. Track, Speed, Climb json.Number EPD, EPS, EPC json.Number } func (t TPV) Position() Position { lat, _ := t.Lat.Float64() lon, _ := t.Lon.Float64() alt, _ := t.Alt.Float64() track, _ := t.Track.Float64() speed, _ := t.Speed.Float64() return Position{Lat: lat, Lon: lon, Alt: alt, Track: track, Speed: speed, Time: t.Time} } func (t TPV) HasFix() bool { return t.Mode > ModeNoFix } // Satellite represents a GPS satellite. type Satellite struct { // PRN ID of the satellite. 1-63 are GNSS satellites, 64-96 are GLONASS satellites, 100-164 are SBAS satellites. PRN int `json:"PRN"` // Azimuth, degrees from true north. Azimuth json.Number `json:"az"` // Elevation in degrees. Elevation json.Number `json:"el"` // Signal strength in dB. SignalStrength json.Number `json:"ss"` // Used in current solution? // // (SBAS/WAAS/EGNOS satellites may be flagged used if the solution has corrections from them, but not all drivers make this information available). Used bool `json:"used"` } // Version holds GPSd version data. type Version struct { Release string `json:"release"` Rev string `json:"rev"` ProtoMajor int `json:"proto_major"` ProtoMinor int `json:"proto_minor"` } // Device represents a connected sensor/GPS. type Device struct { Path string `json:"path,omitempty"` Flags *int `json:"flags,omitempty"` Driver string `json:"driver,omitempty"` Subtype string `json:"subtype,omitempty"` Bps *int `json:"bps,omitempty"` Parity string `json:"parity"` StopBits int `json:"stopbits"` // Activated time.Time `json:"activated,omitempty"` (Must parse as fractional epoch time) } type watch struct { Class string `json:"class"` Enable bool `json:"enable,omitempty"` JSON *bool `json:"json,omitempty"` NMEA *bool `json:"nmea,omitempty"` Raw *int `json:"raw,omitempty"` Scaled *bool `json:"scaled,omitempty"` Split24 *bool `json:"split24,omitempty"` PPS *bool `json:"pps,omitempty"` Device string `json:"device,omitempty"` Devices []Device `json:"devices,omitempty"` // Only in response } func parseJSONObject(raw []byte) (interface{}, error) { var class struct{ Class string } err := json.Unmarshal(raw, &class) if err != nil { return nil, err } switch class.Class { case "WATCH": var w watch err = json.Unmarshal(raw, &w) return w, err case "DEVICES": var devs struct{ Devices []Device } err = json.Unmarshal(raw, &devs) return devs.Devices, err case "DEVICE": var dev Device err = json.Unmarshal(raw, &dev) return dev, err case "VERSION": var ver Version err = json.Unmarshal(raw, &ver) return ver, err case "ERROR": var err struct{ Message string } json.Unmarshal(raw, &err) return nil, errors.New(err.Message) case "SKY": var sky Sky err = json.Unmarshal(raw, &sky) return sky, err case "TPV": var tpv TPV err = json.Unmarshal(raw, &tpv) return tpv, err default: var m map[string]interface{} err = json.Unmarshal(raw, &m) return m, err } } pat-0.12.1/internal/osutil/000077500000000000000000000000001415513746300155235ustar00rootroot00000000000000pat-0.12.1/internal/osutil/rlimit_freebsd.go000066400000000000000000000011251415513746300210430ustar00rootroot00000000000000// +build freebsd package osutil import ( "fmt" "syscall" ) // RaiseOpenFileLimit tries to maximize the limit of open file descriptors, limited by max or the OS's hard limit func RaiseOpenFileLimit(max uint64) error { var limit syscall.Rlimit if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { return fmt.Errorf("Could not get current limit: %v", err) } if limit.Cur >= limit.Max || limit.Cur >= int64(max) { return nil } limit.Cur = limit.Max if limit.Cur > int64(max) { limit.Cur = int64(max) } return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit) } pat-0.12.1/internal/osutil/rlimit_unix.go000066400000000000000000000011121415513746300204100ustar00rootroot00000000000000// +build !windows,!freebsd package osutil import ( "fmt" "syscall" ) // RaiseOpenFileLimit tries to maximize the limit of open file descriptors, limited by max or the OS's hard limit func RaiseOpenFileLimit(max uint64) error { var limit syscall.Rlimit if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { return fmt.Errorf("could not get current limit: %w", err) } if limit.Cur >= limit.Max || limit.Cur >= max { return nil } limit.Cur = limit.Max if limit.Cur > max { limit.Cur = max } return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit) } pat-0.12.1/internal/osutil/rlimit_windows.go000066400000000000000000000002171415513746300211240ustar00rootroot00000000000000// +build windows package osutil import "fmt" func RaiseOpenFileLimit(max uint64) error { return fmt.Errorf("Not available for Windows") } pat-0.12.1/listen.go000066400000000000000000000073461415513746300142270ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "log" "net" "strings" "time" "github.com/la5nta/wl2k-go/transport/ax25" "github.com/la5nta/wl2k-go/transport/telnet" ) func Unlisten(param string) { methods := strings.FieldsFunc(param, SplitFunc) for _, method := range methods { ok, err := listenHub.Disable(method) if err != nil { log.Printf("Unable to close %s listener: %s", method, err) } else if !ok { log.Printf("No active %s listener, ignoring.\n", method) } } } func Listen(listenStr string) { methods := strings.FieldsFunc(listenStr, SplitFunc) for _, method := range methods { switch strings.ToLower(method) { case MethodWinmor: listenHub.Enable(WINMORListener{}) case MethodArdop: listenHub.Enable(ARDOPListener{}) case MethodTelnet: listenHub.Enable(TelnetListener{}) case MethodAX25: listenHub.Enable(&AX25Listener{}) case MethodSerialTNC: log.Printf("%s listen not implemented, ignoring.", method) default: log.Printf("'%s' is not a valid listen method", method) return } } log.Printf("Listening for incoming traffic on %s...", listenStr) } type AX25Listener struct{ stopBeacon chan<- struct{} } func (l *AX25Listener) Init() (net.Listener, error) { return ax25.ListenAX25(config.AX25.Port, fOptions.MyCall) } func (l *AX25Listener) BeaconStart() error { if config.AX25.Beacon.Every > 0 { l.stopBeacon = l.beaconLoop(time.Duration(config.AX25.Beacon.Every) * time.Second) } return nil } func (l *AX25Listener) BeaconStop() { select { case l.stopBeacon <- struct{}{}: default: } } func (l *AX25Listener) beaconLoop(dur time.Duration) chan<- struct{} { stop := make(chan struct{}, 1) go func() { b, err := ax25.NewAX25Beacon(config.AX25.Port, fOptions.MyCall, config.AX25.Beacon.Destination, config.AX25.Beacon.Message) if err != nil { log.Printf("Unable to activate beacon: %s", err) return } t := time.Tick(dur) for { select { case <-t: if err := b.Now(); err != nil { log.Printf("%s beacon failed: %s", l.Name(), err) return } case <-stop: return } } }() return stop } func (l *AX25Listener) CurrentFreq() (Frequency, bool) { return 0, false } func (l *AX25Listener) Name() string { return MethodAX25 } type ARDOPListener struct{} func (l ARDOPListener) Name() string { return MethodArdop } func (l ARDOPListener) Init() (net.Listener, error) { if err := initArdopTNC(); err != nil { return nil, err } ln, err := adTNC.Listen() if err != nil { return nil, err } return ln, err } func (l ARDOPListener) CurrentFreq() (Frequency, bool) { if rig, ok := rigs[config.Ardop.Rig]; ok { f, _ := rig.GetFreq() return Frequency(f), ok } return 0, false } func (l ARDOPListener) BeaconStart() error { return adTNC.BeaconEvery(time.Duration(config.Ardop.BeaconInterval) * time.Second) } func (l ARDOPListener) BeaconStop() { adTNC.BeaconEvery(0) } type WINMORListener struct{} func (l WINMORListener) Name() string { return MethodWinmor } func (l WINMORListener) Init() (net.Listener, error) { if err := initWinmorTNC(); err != nil { return nil, err } return wmTNC.Listen(config.Winmor.InboundBandwidth) } func (l WINMORListener) CurrentFreq() (Frequency, bool) { if rig, ok := rigs[config.Winmor.Rig]; ok { f, _ := rig.GetFreq() return Frequency(f), ok } return 0, false } type TelnetListener struct{} func (l TelnetListener) Name() string { return MethodTelnet } func (l TelnetListener) Init() (net.Listener, error) { return telnet.Listen(config.Telnet.ListenAddr) } func (l TelnetListener) CurrentFreq() (Frequency, bool) { return 0, false } pat-0.12.1/listener_hub.go000066400000000000000000000070441415513746300154070ustar00rootroot00000000000000// Copyright 2017 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "log" "net" "sync" "time" ) type TransportListener interface { Init() (net.Listener, error) Name() string CurrentFreq() (Frequency, bool) } type Beaconer interface { BeaconStop() BeaconStart() error } type Listener struct { t TransportListener hub *ListenerHub mu sync.Mutex isClosed bool err error ln net.Listener } func NewListener(t TransportListener) *Listener { return &Listener{t: t} } func (l *Listener) Err() error { l.mu.Lock() defer l.mu.Unlock() return l.err } func (l *Listener) Close() error { l.mu.Lock() defer l.mu.Unlock() if l.isClosed { return l.err } l.isClosed = true // If l.err is not nil, then the last attempt to open the listener failed and we don't have anything to close if l.err != nil { return l.err } return l.ln.Close() } func (l *Listener) listenLoop() { var silenceErr bool for { l.mu.Lock() if l.isClosed { l.mu.Unlock() break } // Try to init the TNC l.ln, l.err = l.t.Init() if l.err != nil { l.mu.Unlock() if !silenceErr { log.Printf("Listener %s failed: %s", l.t.Name(), l.err) log.Printf("Will try to re-establish listener in the background...") silenceErr = true websocketHub.UpdateStatus() } time.Sleep(time.Second) continue } l.mu.Unlock() if silenceErr { log.Printf("Listener %s re-established", l.t.Name()) silenceErr = false websocketHub.UpdateStatus() } if b, ok := l.t.(Beaconer); ok { b.BeaconStart() } // Run the accept loop until an error occurs if err := l.acceptLoop(); err != nil { log.Printf("Accept %s failed: %s", l.t.Name(), err) } if b, ok := l.t.(Beaconer); ok { b.BeaconStop() } } } type RemoteCaller interface { RemoteCall() string } func (l *Listener) acceptLoop() error { for { conn, err := l.ln.Accept() if err != nil { return err } remoteCall := conn.RemoteAddr().String() if c, ok := conn.(RemoteCaller); ok { remoteCall = c.RemoteCall() } freq, _ := l.t.CurrentFreq() eventLog.LogConn("accept", freq, conn, nil) log.Printf("Got connect (%s:%s)", l.t.Name(), remoteCall) err = exchange(conn, remoteCall, true) if err != nil { log.Printf("Exchange failed: %s", err) } else { log.Println("Disconnected.") } } } type ListenerHub struct { mu sync.Mutex listeners map[string]*Listener } func NewListenerHub() *ListenerHub { return &ListenerHub{ listeners: map[string]*Listener{}, } } func (h *ListenerHub) Active() []TransportListener { h.mu.Lock() defer h.mu.Unlock() slice := make([]TransportListener, 0, len(h.listeners)) for _, l := range h.listeners { if l.Err() != nil { continue } slice = append(slice, l.t) } return slice } func (h *ListenerHub) Enable(t TransportListener) { h.mu.Lock() defer func() { h.mu.Unlock() websocketHub.UpdateStatus() }() l := NewListener(t) if _, ok := h.listeners[t.Name()]; ok { return } h.listeners[t.Name()] = l go l.listenLoop() } func (h *ListenerHub) Disable(name string) (bool, error) { h.mu.Lock() defer func() { h.mu.Unlock() websocketHub.UpdateStatus() }() l, ok := h.listeners[name] if !ok { return false, nil } delete(h.listeners, name) return true, l.Close() } func (h *ListenerHub) Close() { h.mu.Lock() defer func() { h.mu.Unlock() websocketHub.UpdateStatus() }() for k, l := range h.listeners { l.Close() delete(h.listeners, k) } } pat-0.12.1/main.go000066400000000000000000000414231415513746300136470ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. // A portable Winlink client for amateur radio email. package main import ( "fmt" "io" "log" "net" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "time" "github.com/la5nta/pat/cfg" "github.com/la5nta/pat/internal/buildinfo" "github.com/la5nta/pat/internal/debug" "github.com/la5nta/pat/internal/directories" "github.com/la5nta/pat/internal/forms" "github.com/la5nta/pat/internal/gpsd" "github.com/la5nta/wl2k-go/catalog" "github.com/la5nta/wl2k-go/fbb" "github.com/la5nta/wl2k-go/mailbox" "github.com/la5nta/wl2k-go/rigcontrol/hamlib" "github.com/spf13/pflag" ) const ( MethodWinmor = "winmor" MethodArdop = "ardop" MethodTelnet = "telnet" MethodAX25 = "ax25" MethodSerialTNC = "serial-tnc" MethodPactor = "pactor" ) var commands = []Command{ { Str: "connect", Desc: "Connect to a remote station.", HandleFunc: connectHandle, Usage: UsageConnect, Example: ExampleConnect, MayConnect: true, }, { Str: "interactive", Desc: "Run interactive mode.", Usage: "[options]", Options: map[string]string{ "--http, -h": "Start http server for web UI in the background.", }, HandleFunc: InteractiveHandle, MayConnect: true, LongLived: true, }, { Str: "http", Desc: "Run http server for web UI.", Usage: "[options]", Options: map[string]string{ "--addr, -a": "Listen address. Default is :8080.", }, HandleFunc: httpHandle, MayConnect: true, LongLived: true, }, { Str: "compose", Desc: "Compose a new message.\n" + "\tIf no options are passed, composes interactively.\n" + "\tIf options are passed, reads message from stdin similar to mail(1).", Usage: "[options]", Options: map[string]string{ "--callsign, -r": "Callsign to send from. Default reads from config", "--subject, -s": "Subject", "--attachment , -a": "Attachment path (may be repeated)", "--cc, -c": "CC Address(es) (may be repeated)", "": "Recipient address (may be repeated)", }, HandleFunc: composeMessage, }, { Str: "read", Desc: "Read messages.", HandleFunc: func(args []string) { readMail() }, }, { Str: "composeform", Aliases: []string{"formPath"}, Desc: "Post form-based report.", Usage: "[options]", Options: map[string]string{ "--template": "path to the form template file. Uses the --forms directory as root. Defaults to 'ICS USA Forms/ICS213.txt'", }, HandleFunc: composeFormReport, }, { Str: "position", Aliases: []string{"pos"}, Desc: "Post a position report (GPSd or manual entry).", Usage: "[options]", Options: map[string]string{ "--latlon": "latitude,longitude in decimal degrees for manual entry. Will use GPSd if this is empty.", "--comment, -c": "Comment to be included in the position report.", }, Example: ExamplePosition, HandleFunc: posReportHandle, }, { Str: "extract", Desc: "Extract attachments from a message file.", Usage: "file", HandleFunc: extractMessageHandle, }, { Str: "rmslist", Desc: "Print/search in list of RMS nodes.", Usage: "[options] [search term]", Options: map[string]string{ "--mode, -m": "Mode filter.", "--band, -b": "Band filter (e.g. '80m').", "--force-download, -d": "Force download of latest list from winlink.org.", "--sort-distance, -s": "Sort by distance", }, HandleFunc: rmsListHandle, }, { Str: "updateforms", Desc: "Download the latest form templates from winlink.org.", HandleFunc: func(args []string) { if _, err := formsMgr.UpdateFormTemplates(); err != nil { log.Printf("%v", err) } }, }, { Str: "configure", Desc: "Open configuration file for editing.", HandleFunc: configureHandle, }, { Str: "version", Desc: "Print the application version.", HandleFunc: func(args []string) { fmt.Printf("%s %s\n", buildinfo.AppName, buildinfo.VersionString()) }, }, { Str: "env", Desc: "List environment variables.", HandleFunc: envHandle, }, { Str: "help", Desc: "Print detailed help for a given command.", // Avoid initialization loop by invoking helpHandler in main }, } var ( config cfg.Config rigs map[string]hamlib.VFO logWriter io.Writer eventLog *EventLogger exchangeChan chan ex // The channel that the exchange loop is listening on exchangeConn net.Conn // Pointer to the active session connection (exchange) mbox *mailbox.DirHandler // The mailbox listenHub *ListenerHub promptHub *PromptHub formsMgr *forms.Manager ) var fOptions struct { IgnoreBusy bool // Move to connect? SendOnly bool // Move to connect? RadioOnly bool Robust bool MyCall string Listen string MailboxPath string ConfigPath string LogPath string EventLogPath string FormsPath string } func optionsSet() *pflag.FlagSet { set := pflag.NewFlagSet("options", pflag.ExitOnError) set.StringVar(&fOptions.MyCall, "mycall", "", "Your callsign (winlink user).") set.StringVarP(&fOptions.Listen, "listen", "l", "", "Comma-separated list of methods to listen on (e.g. winmor,ardop,telnet,ax25).") set.BoolVarP(&fOptions.SendOnly, "send-only", "s", false, "Download inbound messages later, send only.") set.BoolVarP(&fOptions.RadioOnly, "radio-only", "", false, "Radio Only mode (Winlink Hybrid RMS only).") set.BoolVarP(&fOptions.Robust, "robust", "r", false, "Use robust modes only (useful to improve s/n-ratio at remote winmor station).") set.BoolVar(&fOptions.IgnoreBusy, "ignore-busy", false, "Don't wait for clear channel before connecting to a node.") defaultMBox := filepath.Join(directories.DataDir(), "mailbox") defaultFormsPath := filepath.Join(directories.DataDir(), "Standard_Forms") defaultConfigPath := filepath.Join(directories.ConfigDir(), "config.json") defaultLogPath := filepath.Join(directories.StateDir(), strings.ToLower(buildinfo.AppName+".log")) defaultEventLogPath := filepath.Join(directories.StateDir(), "eventlog.json") set.StringVar(&fOptions.MailboxPath, "mbox", defaultMBox, "Path to mailbox directory.") set.StringVar(&fOptions.FormsPath, "forms", defaultFormsPath, "Path to forms directory.") set.StringVar(&fOptions.ConfigPath, "config", defaultConfigPath, "Path to config file.") set.StringVar(&fOptions.LogPath, "log", defaultLogPath, "Path to log file. The file is truncated on each startup.") set.StringVar(&fOptions.EventLogPath, "event-log", defaultEventLogPath, "Path to event log file.") return set } func init() { listenHub = NewListenerHub() promptHub = NewPromptHub() pflag.Usage = func() { fmt.Fprintf(os.Stderr, "%s is a client for the Winlink 2000 Network.\n\n", buildinfo.AppName) fmt.Fprintf(os.Stderr, "Usage:\n %s [options] command [arguments]\n", os.Args[0]) fmt.Fprintln(os.Stderr, "\nCommands:") for _, cmd := range commands { fmt.Fprintf(os.Stderr, " %-15s %s\n", cmd.Str, cmd.Desc) } fmt.Fprintln(os.Stderr, "\nOptions:") optionsSet().PrintDefaults() fmt.Fprint(os.Stderr, "\n") } } func main() { cmd, args := parseFlags(os.Args) debug.Printf("Version: %s", buildinfo.VersionString()) debug.Printf("Command: %s %v", cmd.Str, args) fOptions.MailboxPath = filepath.Clean(fOptions.MailboxPath) fOptions.FormsPath = filepath.Clean(fOptions.FormsPath) fOptions.ConfigPath = filepath.Clean(fOptions.ConfigPath) fOptions.LogPath = filepath.Clean(fOptions.LogPath) fOptions.EventLogPath = filepath.Clean(fOptions.EventLogPath) debug.Printf("Mailbox dir is\t'%s'", fOptions.MailboxPath) debug.Printf("Forms dir is\t'%s'", fOptions.FormsPath) debug.Printf("Config file is\t'%s'", fOptions.ConfigPath) debug.Printf("Log file is \t'%s'", fOptions.LogPath) debug.Printf("Event log file is\t'%s'", fOptions.EventLogPath) directories.MigrateLegacyDataDir() // Skip initialization for some commands switch cmd.Str { case "help": helpHandle(args) return case "configure", "version": cmd.HandleFunc(args) return } // Enable the GZIP extension experiment by default if _, ok := os.LookupEnv("GZIP_EXPERIMENT"); !ok { os.Setenv("GZIP_EXPERIMENT", "1") } // Parse configuration file var err error config, err = LoadConfig(fOptions.ConfigPath, cfg.DefaultConfig) if err != nil { log.Fatalf("Unable to load/write config: %s", err) } maybeUpdateFormsDir(os.Args) // Initialize logger f, err := os.Create(fOptions.LogPath) if err != nil { log.Fatal(err) } logWriter = io.MultiWriter(f, os.Stdout) log.SetOutput(logWriter) eventLog, err = NewEventLogger(fOptions.EventLogPath) if err != nil { log.Fatal("Unable to open event log file:", err) } // Read command line options from config if unset if fOptions.MyCall == "" && config.MyCall == "" { fmt.Fprint(os.Stderr, "Missing mycall\n") os.Exit(1) } else if fOptions.MyCall == "" { fOptions.MyCall = config.MyCall } // Ensure mycall is all upper case. fOptions.MyCall = strings.ToUpper(fOptions.MyCall) // Don't use config password if we don't use config mycall if !strings.EqualFold(fOptions.MyCall, config.MyCall) { config.SecureLoginPassword = "" } // Replace placeholders in connect aliases for k, v := range config.ConnectAliases { config.ConnectAliases[k] = strings.ReplaceAll(v, cfg.PlaceholderMycall, fOptions.MyCall) } if fOptions.Listen == "" && len(config.Listen) > 0 { fOptions.Listen = strings.Join(config.Listen, ",") } // init forms subsystem formsMgr = forms.NewManager(forms.Config{ FormsPath: fOptions.FormsPath, MyCall: fOptions.MyCall, Locator: config.Locator, AppVersion: buildinfo.VersionStringShort(), UserAgent: buildinfo.UserAgent(), LineReader: readLine, }) // Make sure we clean up on exit, closing any open resources etc. defer cleanup() // Load the mailbox handler loadMBox() if cmd.MayConnect { rigs = loadHamlibRigs() exchangeChan = exchangeLoop() go func() { if config.VersionReportingDisabled { return } for { // Check every 6 hours, but it won't post more frequent than 24h. postVersionUpdate() // Ignore errors time.Sleep(6 * time.Hour) } }() } if cmd.LongLived { if fOptions.Listen != "" { Listen(fOptions.Listen) } scheduleLoop() } // Start command execution cmd.HandleFunc(args) } func maybeUpdateFormsDir(args []string) { // Backward compatability for config file forms_path formsFlagExplicitlyUsed := false for i := 0; i < len(args); i++ { if strings.HasPrefix(args[i], "--forms") { formsFlagExplicitlyUsed = true } } if !formsFlagExplicitlyUsed && config.FormsPath != "" { debug.Printf("Updating forms dir based on config file: %v", config.FormsPath) fOptions.FormsPath = config.FormsPath } } func configureHandle(args []string) { // Ensure config file has been written _, err := ReadConfig(fOptions.ConfigPath) if os.IsNotExist(err) { err = WriteConfig(cfg.DefaultConfig, fOptions.ConfigPath) if err != nil { log.Fatalf("Unable to write default config: %s", err) } } cmd := exec.Command(EditorName(), fOptions.ConfigPath) cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr if err := cmd.Run(); err != nil { log.Fatalf("Unable to start editor: %s", err) } } func InteractiveHandle(args []string) { var http string set := pflag.NewFlagSet("interactive", pflag.ExitOnError) set.StringVar(&http, "http", "", "HTTP listen address") set.Lookup("http").NoOptDefVal = config.HTTPAddr set.Parse(args) if http == "" { Interactive() return } go func() { if err := ListenAndServe(http); err != nil { log.Println(err) } }() time.Sleep(time.Second) Interactive() } func httpHandle(args []string) { addr := config.HTTPAddr if addr == "" { addr = ":8080" // For backwards compatibility (remove in future) } set := pflag.NewFlagSet("http", pflag.ExitOnError) set.StringVarP(&addr, "addr", "a", addr, "Listen address.") set.Parse(args) if addr == "" { set.Usage() os.Exit(1) } promptHub.OmitTerminal(true) if err := ListenAndServe(addr); err != nil { log.Fatal(err) } } func connectHandle(args []string) { if args[0] == "" { fmt.Println("Missing argument, try 'connect help'.") } if success := Connect(args[0]); !success { os.Exit(1) } } func helpHandle(args []string) { arg := args[0] var cmd *Command for _, c := range commands { if c.Str == arg { cmd = &c break } } if arg == "" || cmd == nil { pflag.Usage() return } cmd.PrintUsage() } func cleanup() { listenHub.Close() if wmTNC != nil { if err := wmTNC.Close(); err != nil { log.Fatalf("Failure to close winmor TNC: %s", err) } } if adTNC != nil { if err := adTNC.Close(); err != nil { log.Fatalf("Failure to close ardop TNC: %s", err) } } if pModem != nil { if err := pModem.Close(); err != nil { log.Fatalf("Failure to close pactor modem: %s", err) } } eventLog.Close() } func loadMBox() { mbox = mailbox.NewDirHandler( filepath.Join(fOptions.MailboxPath, fOptions.MyCall), fOptions.SendOnly, ) // Ensure the mailbox handler is ready if err := mbox.Prepare(); err != nil { log.Fatal(err) } } func loadHamlibRigs() map[string]hamlib.VFO { rigs := make(map[string]hamlib.VFO, len(config.HamlibRigs)) for name, conf := range config.HamlibRigs { if conf.Address == "" { log.Printf("Missing address-field for rig '%s', skipping.", name) continue } rig, err := hamlib.Open(conf.Network, conf.Address) if err != nil { log.Printf("Initialization hamlib rig %s failed: %s.", name, err) continue } var vfo hamlib.VFO switch strings.ToUpper(conf.VFO) { case "A", "VFOA": vfo, err = rig.VFOA() case "B", "VFOB": vfo, err = rig.VFOB() case "": vfo = rig.CurrentVFO() default: log.Printf("Cannot load rig '%s': Unrecognized VFO identifier '%s'", name, conf.VFO) continue } if err != nil { log.Printf("Cannot load rig '%s': Unable to select VFO: %s", name, err) continue } f, err := vfo.GetFreq() if err != nil { log.Printf("Unable to get frequency from rig %s: %s.", name, err) } else { log.Printf("%s ready. Dial frequency is %s.", name, Frequency(f)) } rigs[name] = vfo } return rigs } func extractMessageHandle(args []string) { if len(args) == 0 || args[0] == "" { panic("TODO: usage") } file, _ := os.Open(args[0]) defer file.Close() msg := new(fbb.Message) if err := msg.ReadFrom(file); err != nil { log.Fatal(err) } else { fmt.Println(msg) for _, f := range msg.Files() { if err := os.WriteFile(f.Name(), f.Data(), 0o664); err != nil { log.Fatal(err) } } } } func EditorName() string { if e := os.Getenv("EDITOR"); e != "" { return e } else if e := os.Getenv("VISUAL"); e != "" { return e } switch runtime.GOOS { case "windows": return "notepad" case "linux": if path, err := exec.LookPath("editor"); err == nil { return path } } return "vi" } func posReportHandle(args []string) { var latlon, comment string set := pflag.NewFlagSet("position", pflag.ExitOnError) set.StringVar(&latlon, "latlon", "", "") set.StringVarP(&comment, "comment", "c", "", "") set.Parse(args) report := catalog.PosReport{Comment: comment} if latlon != "" { parts := strings.Split(latlon, ",") if len(parts) != 2 { log.Fatal(`Invalid position format. Expected "latitude,longitude".`) } lat, err := strconv.ParseFloat(parts[0], 64) if err != nil { log.Fatal(err) } report.Lat = &lat lon, err := strconv.ParseFloat(parts[1], 64) if err != nil { log.Fatal(err) } report.Lon = &lon } else if config.GPSd.Addr != "" { conn, err := gpsd.Dial(config.GPSd.Addr) if err != nil { log.Fatalf("GPSd daemon: %s", err) } defer conn.Close() conn.Watch(true) log.Println("Waiting for position from GPSd...") // TODO: Spinning bar? pos, err := conn.NextPos() if err != nil { log.Fatalf("GPSd: %s", err) } report.Lat = &pos.Lat report.Lon = &pos.Lon if config.GPSd.UseServerTime { report.Date = time.Now() } else { report.Date = pos.Time } // Course and speed is part of the spec, but does not seem to be // supported by winlink.org anymore. Ignore it for now. if false && pos.Track != 0 { course := CourseFromFloat64(pos.Track, false) report.Course = &course } } else { fmt.Println("No position available. See --help") os.Exit(1) } if report.Date.IsZero() { report.Date = time.Now() } postMessage(report.Message(fOptions.MyCall)) } func CourseFromFloat64(f float64, magnetic bool) catalog.Course { c := catalog.Course{Magnetic: magnetic} str := fmt.Sprintf("%03.0f", f) for i := 0; i < 3; i++ { c.Digits[i] = str[i] } return c } func postMessage(msg *fbb.Message) { if err := msg.Validate(); err != nil { fmt.Printf("WARNING - Message does not validate: %s\n", err) } if err := mbox.AddOut(msg); err != nil { log.Fatal(err) } fmt.Println("Message posted") } pat-0.12.1/make.bash000077500000000000000000000040601415513746300141470ustar00rootroot00000000000000#!/usr/bin/env bash set -e export GO111MODULE=on if [ -d $GOOS ]; then OS=$(go env GOOS); else OS=$GOOS; fi GITREV=$(git rev-parse --short HEAD) VERSION=$(grep "Version =" internal/buildinfo/VERSION.go|cut -d '"' -f2) # Go 1.16 or later is required GO_POINT_VERSION=$(go version| perl -ne 'm/go1\.(\d+)/; print $1;') [ "$GO_POINT_VERSION" -lt "16" ] && echo "Go 1.16 or later required" && exit 1; AX25VERSION="0.0.12-rc4" AX25DIST="libax25-${AX25VERSION}" AX25DIST_URL="http://http.debian.net/debian/pool/main/liba/libax25/libax25_${AX25VERSION}.orig.tar.gz" function install_libax25 { mkdir -p .build && cd .build [[ -f "${AX25DIST}" ]] || curl -LSsf "${AX25DIST_URL}" | tar zx cd "${AX25DIST}/" && ./configure --prefix=/ && make && cd ../../ } [[ "$1" == "libax25" ]] && install_libax25 && exit 0; # Link against libax25 (statically) on Linux if [[ "$OS" == "linux"* ]]; then TAGS="libax25 $TAGS" LIB=".build/${AX25DIST}/.libs/libax25.a" if [[ -z "$CGO_LDFLAGS" ]] && [[ -f "$LIB" ]]; then export CGO_CFLAGS="-I$(pwd)/.build/${AX25DIST}" export CGO_LDFLAGS="$(pwd)/${LIB}" fi if [[ -z "$CGO_LDFLAGS" ]]; then echo "WARNING: No static libax25 library available." echo " Linking against shared library instead. To fix" echo " this issue, set CGO_LDFLAGS to the full path of" echo " libax25.a, or run 'make.bash libax25' to download" echo " and compile ${AX25DIST} in .build/" sleep 3; else TAGS="static $TAGS" fi fi echo -e "Downloading Go dependencies..." go mod download echo "Running tests..." if [[ "$SKIP_TESTS" == "1" ]]; then echo "Skipping." else go test -tags "$TAGS" ./... github.com/la5nta/wl2k-go/... fi echo echo "Building Pat v$VERSION..." go build -tags "$TAGS" -ldflags "-X \"github.com/la5nta/pat/internal/buildinfo.GitRev=$GITREV\"" $(go list .) # Build macOS pkg (amd64) if [[ "$OS" == "darwin"* ]] && command -v packagesbuild >/dev/null 2>&1; then echo "Generating macOS installer package..." packagesbuild osx/pat.pkgproj mv 'Pat :: A Modern Winlink Client.pkg' "pat_${VERSION}_darwin_amd64_unsigned.pkg" fi echo -e "Enjoy!" pat-0.12.1/man/000077500000000000000000000000001415513746300131435ustar00rootroot00000000000000pat-0.12.1/man/pat-configure.1000066400000000000000000000011301415513746300157630ustar00rootroot00000000000000.TH PAT 1 "2017-09-04" "" "Pat Configure" .SH NAME pat configure \- opens Pat's configuration file using the system default editor .SH Configuration .SS Main Configuration The default configuration file for pat is \fI~/.wl2k/config.json\fP .sp 1 To get "on the air" you'll first have to set up your callsign, maidenhead locator, and secure login credentials. Look for the attributes \fImycall\fP, \fIlocator\fP and \fIsecure_login_password\fP and set them appropriately. .sp 1 .in 20 { "mycall": "LA5NTA", "locator": "JP20qe", "secure_login_password": "MYPASSWORD", } .in .SH "See Also" pat(1) pat-0.12.1/man/pat.1000066400000000000000000000033301415513746300140100ustar00rootroot00000000000000.TH PAT 1 "2017-09-04" "" "Pat Overview" .SH NAME pat \- a cross platform Winlink client with basic messaging capabilities .SH SYNOPSIS \fBpat\fP [options] \fIcommand\fP [arguments] .SS Commands .TP \fIconnect\fP Connect to a remote station. .TP \fIinteractive\fP Run interactive mode. .TP \fIhttp\fP Run http server for web gui. .TP \fIcompose\fP Compose a new message. .TP \fIcomposeform\fP Compose a new message based on a Winlink-style form, e.g. the ICS213 form. .TP \fIread\fP Read Messages. .TP \fIposition\fP Post a position report (GPSd or manual entry). .TP \fIextract\fP Extract attachments from a message file. .TP \fIrmslist\fP Print/search in list of RMS nodes. .TP \fIconfigure\fP Open configuration file for editing. .TP \fIversion\fP Print the application version. .TP \fIhelp\fP Print detailed help for a given command. .SS Options .TP \fR--config string\fP Path to config file (default "/home/USER/.wl2k/config.json"). .TP \fR--event-log string\fP Path to event log file (default "/home/USER/.wl2k/config.json"). .TP \fR--ignore-busy\fP Don't wait for clear channel before connevting to a node. .TP \fR-l, --listen string\fP Comma-separated list of methods to listen on (e.g. winmor,ardop,telnet,ax25). .TP \fR--log string\fP Path to log file. The file is truncated on each startup (default "/home/USER/.wl2k/pat.log"). .TP \fR--mbox string\fP Path to mailbox directory (default "/home/USER/.wl2k/mailbox"). .TP \fR--mycall string\fP Your callsing (winlink user). .TP \fR--radio-only\fP Radio Only mode (Winlink Hybrid RMS only). .TP \fR-r, --robust\fP Use robust modes only (Useful to improve s/n ratio at remote winmor station). .TP \fR-s, --send-only\fP Download inbound messages later, send only. .SH "See Also" pat-configure(1) pat-0.12.1/osx/000077500000000000000000000000001415513746300132015ustar00rootroot00000000000000pat-0.12.1/osx/Pat-Info.rtfd/000077500000000000000000000000001415513746300155545ustar00rootroot00000000000000pat-0.12.1/osx/Pat-Info.rtfd/TXT.rtf000066400000000000000000000010671415513746300167540ustar00rootroot00000000000000{\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Menlo-Regular;} {\colortbl;\red255\green255\blue255;} \margl1440\margr1440\vieww12540\viewh16140\viewkind1 \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc\partightenfactor0 \f0\fs24 \cf0 This will install \f1 pat \f0 into \f1 /usr/local/bin \f0 . \ To run \f1 pat \f0 , use Terminal.app in the \f1 /Applications/Utilities \f0 folder.\ \ \fs26 For more help and information, visit getpat.io}pat-0.12.1/osx/Pat-License.txt000066400000000000000000000021171415513746300160470ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014-2017 Martin Hebnes Pedersen (LA5NTA) 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. pat-0.12.1/osx/Pat-Welcome.rtfd/000077500000000000000000000000001415513746300162545ustar00rootroot00000000000000pat-0.12.1/osx/Pat-Welcome.rtfd/Pasted Graphic.tiff000066400000000000000000000441321415513746300217100ustar00rootroot00000000000000MM*;8 P8$ BaPd6DbQ8V-FcQv=HdR9$M'JeRd]/LfS9m7NgS}?PhT:%GRiTe6OTjU:VWVkUv_XlV;%gZmVeo\nW;w^oW`pX<& bqXf7drY'ry\g7tz]>Wv{]wx|^?'z}^g|~_?~_ , Lj P*3 CNDBDq4JDs B4:`BBT; @"!H(dCN~g'{'B} r&DQ\\4fJB: ;6 @l:Gq"R8?I/S s' R*CByg(Irx|UE104r8B, : B}PR Ȁ924O jAp 'B?* T>v'U0u gMLRP"s 7PbLg3X0 bLP_%gCo0v g!4,q .S\pETF;hpWrE  AT. CNŀ3wŜDݑI#gE&i5M ` 9COD0 !PiUUnK`osMjO"vAgvu] 'o`5K T9O)Dh $&)}VYT``^*1 #2ŊT0 e>oAP'Y$ML qW Pěq Q*2i`{H8㢂2}$GkHϚiF(3PB>4ۅb*! Fqj {T)|foH'ˣk>p dzKc`E`h(079Nsuq2 .xgVUx$!zN"1 PjZDlGε]%,n$U [WN#=y1c-A'XVtu (y$eE,5}=('an/^s\cYֵs]׵Eؔ{(. ;L)Tс"j͸e4M ?8#?TvWxXcFh&J ƕ`8#mk4j>ns8Xi[W _c0 tSюKhWv  jgŠ'ևyXz4ZWa:((ۆ@|y8' ݍmXPS>c_F"Drڟ -drX k J95u|8YbN3^q-kr3-usn"M>&Ec{^=:yȪ`3o3"*`~!}civؒ0{Z]A#~(ܡnCMˍ@X!H3l 5,zg+EUr Ql4K aM=f?I3 } @˜3&$ۂ_A`,5~aڱ/h DR!J@К=2F\V~a꾆d^Ƒ@)QAlP5G'g!ëE}BZ0HoY259 S@6E>GJw5$5зZmA"h$08UCwPnK]g @))4X&,p_`%/ U3e*e ٪0GbD0b"]I {Pw7}kdp~2 <Q'2_P-]} 7-+jj Sde& 6x/DSόcό, # gIՀb | 0S{VX?"q)*^m4Y!h H7UBU𸠔7!l:u{O2Gd>Pc  ,I׏ʦC*Z&`#w!Wl-8a=Ҕd#/ E1bxeFPouj` pirGg,!o(g$C.rhʦ] @~< "@`*G^ ty?C 'oW Uh <~<@] p Cl ,pgS k\P?d7@֋ZVJH E4Z"sF.Ay@{"CPCpTe`%ri$Vv&4$ MP)@8~dDp=N(3h A>pqP[6:0:pY0"zSEk*!2@SN24cnBxO />/OF((D8wLP,yC/дalCo<\t#>GbKq*@菶k)]+{@P\o@(G#S2@ٕIիM#@EEѐ08D:| <`y.z ZaEBQ 1J,I¹MM[`1Q .l0eNy`>!0޽ݹbX h`L6<i MI!+RUȬ8* ^*|'{ 7T?M!¿-`] v5x،+m" hgjX3辘 Zwz302+x`/î/0R(S c8dhxpy3B/a}[4j2Ո( ¨@Hyw. X}8P hj cHNOkl;Xz2z)`+@4**+D9~|P=<&À0~@s@I\]h#4VDq2j#7@):Di| * xp87gBkq r`B(C`|Gtb*/sO{Ā(xThƜq((8w~t,~?`2 M\08RSemn@|ZXqVh~4Pebʸ  zisA l}<6lQܕIsE bH{ClA 1_r2&+el̷ijsKݷ \Zܾ6#VS!((nN( \fp| ݀_}0yϨkps@s5 &# 5,8{eaPmF& U>p~8p`louK9j_X,V-FX_fˆ ^zi=u_PÆdr0 ah2聇̀|Hh!X~=KSk}Vfj@jqLjmnwvbAlV5Z!3 ` Y)E|] |V>Evi3kQrM%h^NoG(w2 ;@&`s8ņx O,̸iwVfG4v@nGNi]hlLT֩h Wp(鈭"Pp n8n|H vm76^G|Xpp") q/hFfhZyH;FL{qRz^ǭ٣YNi͇l/q8sܝx]"CHpȀPF"`?B Ff cZWe2$ZX$Kc@y2k_0{VG%\\@u~Vh pMԀ!P;Q33[mpn(Ok8'3RЀ9 SXk\$͓ݏ/sB4 Ly*+KjЕRAJHН F(l')ılK2hɵHfHpH6Hi|NioR R*4 )K*Bˮu'(O 2d򰉄+j=(L wlgЋή 1LdbFt-H4\XS~{3D)R{D0;, /A$rtXs#QH+J+aٮc=FQn &Ei[O }z@"@PK}q\^$31^RȲX2uwEY1L igx] qenS>İ^ ")ѣ[%V-E`J6H2CC]f$9#aNn1S :K1s2q*IR aqJ`Pw$:+c2F/H%bw2SE3%XlT9֦&{l*jLFQT0^& Hbk6ĞEnF6ÙHy<הӁP%a&H7Bt.،'6tQm g$WaEQQRRs @8il:Pt2|NùQMMbvbx=]. h@PYNIT,239=m͝>UUG/ղpDiE`%WTt^W_VE͝O*TY@ ˳XHQSUՈr֔ _p%xRzJl}wvk1q^4`?a2 BC|!n5h^0uM#?\inlBʑ1A6y H- jع(Z#~;|FGX}_H]v<.*@T pv7ae#]D^8h@, 8I`"qF3؀y.̾Ü A7MwxM;~8ouv6(p¸eJ]k&|kU=Ǹt.+EhVb 0^ `9 ĽƆÔ<` ̩ _B[n_)櫹S[,!9B!z(Rzb1i[?uo}ge!v{`lÎ=pf4'Z=^r` #đw,#3A!n `,!Z`2m%'蝡D@.$>*MN4.B >@#+> V,{-Vo/P o6/* a8ւ@2#) !`a@hp<@ a0(&cmdB@`ZT<<, CNvA!#A,`D` "zFKNTQ#jOqa! *#p Nw/ʱ qP N"a N ; [ PB``'AD LŪ!aXމ,:+D.@ r t/q$N@=P/an (4<4B @ATVG@enHVe$Jq "{1qϣ4.N 8 XQ163j 6! .";+`.srK S~6V2N! @aVvf֬\! 0q/0f"x O=00q1s~85q !E !"2'" `!!9aaPmLf`hG `$Al`^ .2dR)zv/ L=D 'C"R:t#!-Pk.t ]- 3w /5@\ڳ5Q+ o ? 9@p@&#R/5p!106a$RQRTQOH!!D#!`;_a!pr$zETAmE-[ЕTF KX#&ĒK2b` t\.M Τ`JFNa1bɕ >j5 bnQ-Q%3?-.8$@Z  n@A /6>C}D# qjތҶ#dѐaN@9{4N!AT`55C@ ׯinloz>ߦ<KHS EQӀB]t(9c͖ag'(H@%`^` 粃9D,5}e\[ gln_h$S$<̶ mmލ7x"r#P cN ]0X?PS5cdTy͚Ҡ"6 m@$fi= 6@U+3Nb2V /P# awYت8غx8njxոx*.r]lN %&.3jpEPtL0+FLFUIc2Km?#X͂Gnq;&HC%8a6@Ay:7X~Π9ߍEaaC#b_X%X#ϥ⽛7c9h v1AlJ`$Ck|dǨC6Z `a`g`(;m!v4JfV!?n`"02#v:;CפMˤR.QnچJOAR1 6/+`x#U#д1R @ !YTo`NBg9 29l$!`!mv#AN DdAiهJal|.#vˣAAm~pk᳹Z[;j P1#Z@M_ `#i<$#s= B/wvOB#S E\V='@=,! A(U c,2kJa<q+V[{%< ك9ôP6>6@F  :ܼOB5}C@3튆jnMݝ V >#T`Rnڻ/%ÏN{[#y|x]1ߵ;2 AN~dOh!r` 3V=f n8B&K70ġy2i[`彝2!tA`c!᱕`Tb 0U7ijd:ǻX/%`NFg+=1 <(!63H# aa9 0!هa$A q (-NFC! @7DX^(  V*ť2P=fYjʉ`&QAPZ5^z='>ؠ dK`*q\@`sM`aF@P51Md > GZܫV0x<}A4]28SS`DP"LH2,h4B}^/T* IمC! J.7x PqZ?@+uo 1Y%a@ 'ӿ B.I_X* īzK vbyFn.h0E0!  .{x6@v{p< qh k\Ğ{i|  .$P(Nٴ22|0{D~F fF (/Qՠld@G,þg`X*Big~\"mՅh"@m7Ơ@Xg}c Dx` @f$6t2,ȣ׀w}g"V& ݌e9NS`6NF0(eDnk!<XE(@sGnA@A2^l9{`ل#U&,DUqGXDZ%DZ"`kvj@Su"`T(l,Sq\V|$(GjYupcX* v@!=Gv1%`WEn]Kh;IO#>O2 |DϕxV`8`X'ZP}!Y8KK".jq,M@J_ H p5ﳦ \QlD:`"B0"h@C;gtD@XVVҿp,A>jî",B*qT{܍94  Gr.D@CW0"@d>šlӾ^M mUGWQ8gM|x&ȇ>i]I6E34<Lj28ƤrKH|7#=H"8},̹hPOd.eɮTj,:!rǀNxReJ} 9D蠺X@&,&P^D +e, Գ"FOiGg?d2mH(y)紨vȣ)FHy`M-"l} TD`Ǯ70e&T%>[rUc glFuSI%QںRE*H׆ ` D l\Dz9Ao@iv4BerBJ0b٢QeűKdFɌ>BinVԣY76C2 rR8 J)uڽo\nW;w) 7}k>O5x+1 @O݌?`q#juZf]5À'|nXN}'[l 0 l 'v{]/f{͌ j@ ̀0 ' WAqgЁ;LAl6'q&Jw0țŽzd>h"`@Qm'[dvޥ0ITH кG$̵-O8y0&`/f|ê8% Nh~:H4t? J(S"|k0Mr@ٌh4~Cc+nhO#Yaq6Ǯ >W-G\cݟ76j:,CɏAk?tO/n8y/f ve5"~59x]<4ZY-7ݢ! !ơbdl1 !pI̋. (4N5E ၮW#ؽC3`ÕW b]".OP`!؁;yH7a{u1b1+vp6Xc9ec qDثTOB-^*r #6@yNS^a{gؓOjgRRRt:2yJRTػV'd59&'[LRVJ.FB@! WBƛEdԽ:&{ b) S.3:g#*%eπKvd /%ZOa>%[(|-`#j*D %RT$s@HH:FP4 S*q/p~Nk=8 Y9$Y$,@d hF-5l.FK5A4*rO(%y:S(v-V4df\,cz}VՏBͱV$aA)2Tq.¦hэwiQ ԫyEW^{W_[}W_X`\ F X/`XO a\-Xoa=!Xb\MF)Xb]1Xc\m9Xc}AY"d\FIY/&dܝQYO*e\YYo.eܽaY2f\͙FiY6fݛqY:g\yY>;<;=RS< s H<4!4) HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmpat-0.12.1/osx/Pat-Welcome.rtfd/TXT.rtf000066400000000000000000000012531415513746300174510ustar00rootroot00000000000000{\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \margl1440\margr1440\vieww13140\viewh16500\viewkind1 \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc\partightenfactor0 \f0\fs24 \cf0 {{\NeXTGraphic Pasted Graphic.tiff \width3620 \height5120 }}\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc\partightenfactor0 \cf0 \ \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc\partightenfactor0 \fs48 \cf0 Pat :: A Modern Winlink Client}pat-0.12.1/osx/pat.pkgproj000066400000000000000000000637061415513746300153770ustar00rootroot00000000000000 PACKAGES PACKAGE_FILES DEFAULT_INSTALL_LOCATION / HIERARCHY CHILDREN CHILDREN GID 80 PATH Applications PATH_TYPE 0 PERMISSIONS 509 TYPE 1 UID 0 CHILDREN CHILDREN GID 80 PATH Application Support PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Automator PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Documentation PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Extensions PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Filesystems PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Frameworks PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Input Methods PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Internet Plug-Ins PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH LaunchAgents PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH LaunchDaemons PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH PreferencePanes PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Preferences PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 80 PATH Printers PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH PrivilegedHelperTools PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH QuickLook PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH QuickTime PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Screen Savers PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Scripts PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Services PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN GID 0 PATH Widgets PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 GID 0 PATH Library PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN CHILDREN GID 0 PATH Shared PATH_TYPE 0 PERMISSIONS 1023 TYPE 1 UID 0 GID 80 PATH Users PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 CHILDREN CHILDREN CHILDREN CHILDREN GID 0 PATH ../pat PATH_TYPE 1 PERMISSIONS 493 TYPE 3 UID 0 GID 0 PATH bin PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 CHILDREN CHILDREN CHILDREN CHILDREN GID 0 PATH ../man/pat.1 PATH_TYPE 1 PERMISSIONS 420 TYPE 3 UID 0 CHILDREN GID 0 PATH ../man/pat-configure.1 PATH_TYPE 1 PERMISSIONS 420 TYPE 3 UID 0 GID 0 PATH man1 PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 0 PATH man PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 0 PATH share PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 0 PATH local PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 0 PATH usr PATH_TYPE 0 PERMISSIONS 493 TYPE 2 UID 0 GID 0 PATH / PATH_TYPE 0 PERMISSIONS 493 TYPE 1 UID 0 PAYLOAD_TYPE 0 VERSION 4 PACKAGE_SCRIPTS RESOURCES PACKAGE_SETTINGS AUTHENTICATION 1 CONCLUSION_ACTION 0 IDENTIFIER io.getpat.pat NAME pat OVERWRITE_PERMISSIONS USE_HFS+_COMPRESSION VERSION 0.12.1 UUID 5562F199-B4C6-48BC-98FA-5BBA494CB61F PROJECT PROJECT_COMMENTS NOTES PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBIVE1M IDQuMDEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDQv c3RyaWN0LmR0ZCI+CjxodG1sPgo8aGVhZD4KPG1ldGEgaHR0cC1l cXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7 IGNoYXJzZXQ9VVRGLTgiPgo8bWV0YSBodHRwLWVxdWl2PSJDb250 ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjE0MDQuNDciPgo8c3R5bGUg dHlwZT0idGV4dC9jc3MiPgo8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5 Pgo8L2JvZHk+CjwvaHRtbD4K PROJECT_PRESENTATION BACKGROUND ALIGNMENT 4 BACKGROUND_PATH CUSTOM SCALING 0 INSTALLATION TYPE HIERARCHIES INSTALLER LIST DESCRIPTION OPTIONS HIDDEN STATE 0 PACKAGE_UUID 5562F199-B4C6-48BC-98FA-5BBA494CB61F REQUIREMENTS TITLE TOOLTIP TYPE 0 UUID D361C8B7-AA56-4C1A-B30D-0D443ABB3DAD REMOVED INSTALLATION TYPE 0 MODE 1 INSTALLATION_STEPS ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS ICPresentationViewIntroductionController INSTALLER_PLUGIN Introduction LIST_TITLE_KEY InstallerSectionTitle ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS ICPresentationViewReadMeController INSTALLER_PLUGIN ReadMe LIST_TITLE_KEY InstallerSectionTitle ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS ICPresentationViewLicenseController INSTALLER_PLUGIN License LIST_TITLE_KEY InstallerSectionTitle ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS ICPresentationViewDestinationSelectController INSTALLER_PLUGIN TargetSelect LIST_TITLE_KEY InstallerSectionTitle ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS ICPresentationViewInstallationTypeController INSTALLER_PLUGIN PackageSelection LIST_TITLE_KEY InstallerSectionTitle ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS ICPresentationViewInstallationController INSTALLER_PLUGIN Install LIST_TITLE_KEY InstallerSectionTitle ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS ICPresentationViewSummaryController INSTALLER_PLUGIN Summary LIST_TITLE_KEY InstallerSectionTitle INTRODUCTION LOCALIZATIONS LANGUAGE English VALUE PATH Pat-Welcome.rtfd PATH_TYPE 1 LICENSE KEYWORDS LOCALIZATIONS LANGUAGE English VALUE PATH Pat-License.txt PATH_TYPE 1 MODE 0 README LOCALIZATIONS LANGUAGE English VALUE PATH Pat-Info.rtfd PATH_TYPE 1 SUMMARY LOCALIZATIONS TITLE LOCALIZATIONS LANGUAGE English VALUE Pat PROJECT_REQUIREMENTS LIST POSTINSTALL_PATH PREINSTALL_PATH RESOURCES ROOT_VOLUME_ONLY PROJECT_SETTINGS ADVANCED_OPTIONS installer-script.options.platforms.client:arch installer-script.options:hostArchitectures BUILD_FORMAT 0 BUILD_PATH PATH .. PATH_TYPE 1 EXCLUDED_FILES PATTERNS_ARRAY REGULAR_EXPRESSION STRING .DS_Store TYPE 0 PROTECTED PROXY_NAME Remove .DS_Store files PROXY_TOOLTIP Remove ".DS_Store" files created by the Finder. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING .pbdevelopment TYPE 0 PROTECTED PROXY_NAME Remove .pbdevelopment files PROXY_TOOLTIP Remove ".pbdevelopment" files created by ProjectBuilder or Xcode. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING CVS TYPE 1 REGULAR_EXPRESSION STRING .cvsignore TYPE 0 REGULAR_EXPRESSION STRING .cvspass TYPE 0 REGULAR_EXPRESSION STRING .svn TYPE 1 REGULAR_EXPRESSION STRING .git TYPE 1 REGULAR_EXPRESSION STRING .gitignore TYPE 0 PROTECTED PROXY_NAME Remove SCM metadata PROXY_TOOLTIP Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING classes.nib TYPE 0 REGULAR_EXPRESSION STRING designable.db TYPE 0 REGULAR_EXPRESSION STRING info.nib TYPE 0 PROTECTED PROXY_NAME Optimize nib files PROXY_TOOLTIP Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles. STATE PATTERNS_ARRAY REGULAR_EXPRESSION STRING Resources Disabled TYPE 1 PROTECTED PROXY_NAME Remove Resources Disabled folders PROXY_TOOLTIP Remove "Resources Disabled" folders. STATE SEPARATOR NAME Pat :: A Modern Winlink Client TYPE 0 VERSION 2 pat-0.12.1/prompt_hub.go000066400000000000000000000050251415513746300151000ustar00rootroot00000000000000package main import ( "fmt" "io" "os" "time" "github.com/howeyc/gopass" ) type Prompt struct { resp chan PromptResponse cancel chan struct{} ID string `json:"id"` Kind string `json:"kind"` Deadline time.Time `json:"deadline"` Message string `json:"message"` } type PromptResponse struct { ID string `json:"id"` Value string `json:"value"` Err error `json:"error"` } type PromptHub struct { c chan *Prompt rc chan PromptResponse omitTerminal bool } func NewPromptHub() *PromptHub { p := new(PromptHub); go p.loop(); return p } func (p *PromptHub) OmitTerminal(t bool) { p.omitTerminal = t } func (p *PromptHub) loop() { p.c = make(chan *Prompt) p.rc = make(chan PromptResponse) for prompt := range p.c { timeout := time.After(prompt.Deadline.Sub(time.Now())) select { case <-timeout: prompt.resp <- PromptResponse{ID: prompt.ID, Err: fmt.Errorf("deadline reached")} close(prompt.cancel) case resp := <-p.rc: if resp.ID != prompt.ID { continue } select { case prompt.resp <- resp: default: } close(prompt.cancel) } } } func (p *PromptHub) Respond(id, value string, err error) { select { case p.rc <- PromptResponse{ID: id, Value: value, Err: err}: default: } } func (p *PromptHub) Prompt(kind, message string) <-chan PromptResponse { prompt := &Prompt{ resp: make(chan PromptResponse), cancel: make(chan struct{}), // Closed on cancel (e.g. prompt response received) ID: fmt.Sprint(time.Now().UnixNano()), Kind: kind, Message: message, Deadline: time.Now().Add(time.Minute), } p.c <- prompt websocketHub.Prompt(*prompt) if !p.omitTerminal { go p.promptTerminal(*prompt) } return prompt.resp } type ReadAborter struct { *os.File abort chan struct{} } func (r ReadAborter) Read(p []byte) (int, error) { tick := time.Tick(100 * time.Millisecond) for { select { case <-r.abort: return 0, io.EOF case <-tick: stat, err := r.Stat() if err != nil { panic(err) } if stat.Size() > 0 { return r.File.Read(p) } } } } func (p *PromptHub) promptTerminal(prompt Prompt) { switch prompt.Kind { case "password": q := make(chan struct{}, 1) go func() { select { case <-prompt.cancel: fmt.Printf(" Prompt Aborted - Press ENTER to continue...") case <-q: return } }() passwd, err := gopass.GetPasswdPrompt(prompt.Message+": ", true, os.Stdin, os.Stdout) q <- struct{}{} p.Respond(prompt.ID, string(passwd), err) default: panic(prompt.Kind + " prompt not implemented") } } pat-0.12.1/read.go000066400000000000000000000060531415513746300136360ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "bytes" "fmt" "io" "log" "os" "path" "sort" "strconv" "strings" "github.com/bndr/gotabulate" "github.com/la5nta/wl2k-go/fbb" "github.com/la5nta/wl2k-go/mailbox" ) var mailboxes = []string{"in", "out", "sent", "archive"} func readMail() { w := os.Stdout for { // Query user for mailbox to list printMailboxes(w) fmt.Fprintf(w, "\nChoose mailbox [n]: ") mailboxIdx, ok := readInt() if !ok { break } else if mailboxIdx+1 > len(mailboxes) { fmt.Fprintln(w, "Invalid mailbox number") continue } for { // Fetch messages msgs, err := mailbox.LoadMessageDir(path.Join(mailbox.UserPath(fOptions.MailboxPath, fOptions.MyCall), mailboxes[mailboxIdx])) if err != nil { log.Fatal(err) } else if len(msgs) == 0 { fmt.Fprintf(w, "(empty)\n") break } // Print messages (sorted by date) sort.Sort(fbb.ByDate(msgs)) printMessages(w, msgs) // Query user for message to print fmt.Fprintf(w, "Choose message [n]: ") msgIdx, ok := readInt() if !ok { break } else if msgIdx+1 > len(msgs) { fmt.Fprintf(w, "invalid message number\n") continue } printMsg(w, msgs[msgIdx]) // Mark as read? if mailbox.IsUnread(msgs[msgIdx]) { fmt.Fprintf(w, "Mark as read? [Y/n]: ") ans := readLine() if ans == "" || strings.EqualFold(ans, "y") { mailbox.SetUnread(msgs[msgIdx], false) } } // Reply? fmt.Fprintf(w, "Reply (ctrl+c to quit) [y/N]: ") ans := readLine() if strings.EqualFold(ans, "y") { composeReplyMessage(msgs[msgIdx]) } } } } func readInt() (int, bool) { str := readLine() if str == "" { return 0, false } i, _ := strconv.Atoi(str) return i, true } type PrettyAddrSlice []fbb.Address func (addrs PrettyAddrSlice) String() string { var buf bytes.Buffer for i, addr := range addrs { fmt.Fprintf(&buf, "%s", addr.Addr) if i < len(addrs)-1 { fmt.Fprintf(&buf, ", ") } } return buf.String() } func printMsg(w io.Writer, msg *fbb.Message) { fmt.Fprintf(w, "========================================\n") fmt.Fprintln(w, msg) fmt.Fprintf(w, "========================================\n\n") } func printMailboxes(w io.Writer) { for i, mbox := range mailboxes { fmt.Fprintf(w, "%d:%s\t", i, mbox) } } func printMessages(w io.Writer, msgs []*fbb.Message) { rows := make([][]string, len(msgs)) for i, msg := range msgs { var to string if len(msg.To()) > 0 { to = msg.To()[0].Addr } if len(msg.To()) > 1 { to += ", ..." } var flags string if mailbox.IsUnread(msg) { flags += "N" // New } rows[i] = []string{ fmt.Sprintf("%2d", i), flags, msg.Subject(), msg.From().Addr, msg.Date().String(), to, } } t := gotabulate.Create(rows) t.SetHeaders([]string{"i", "Flags", "Subject", "From", "Date", "To"}) t.SetAlign("left") t.SetWrapStrings(true) t.SetMaxCellSize(60) fmt.Fprintln(w, t.Render("simple")) } pat-0.12.1/riglist.go000066400000000000000000000014501415513746300143740ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. // +build libhamlib package main import ( "fmt" "strings" "github.com/la5nta/wl2k-go/rigcontrol/hamlib" ) func init() { cmd := Command{ Str: "riglist", Usage: "[search term]", Desc: "Print/search a list of rigcontrol supported transceivers.", HandleFunc: riglistHandle, } commands = append(commands[:8], append([]Command{cmd}, commands[8:]...)...) } func riglistHandle(args []string) { term := strings.ToLower(args[0]) fmt.Print("id\ttransceiver\n") for m, str := range hamlib.Rigs() { if !strings.Contains(strings.ToLower(str), term) { continue } fmt.Printf("%d\t%s\n", m, str) } } pat-0.12.1/rmslist.go000066400000000000000000000124331415513746300144170ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "encoding/json" "fmt" "log" "math" "net/url" "path/filepath" "sort" "strconv" "strings" "github.com/la5nta/pat/internal/cmsapi" "github.com/la5nta/pat/internal/debug" "github.com/la5nta/pat/internal/directories" "github.com/pd0mz/go-maidenhead" "github.com/spf13/pflag" ) type JSONURL struct{ url.URL } func (url JSONURL) MarshalJSON() ([]byte, error) { return json.Marshal(url.String()) } // JSONFloat64 is a float64 which serializes NaN and Inf(+-) as JSON value null type JSONFloat64 float64 func (f JSONFloat64) MarshalJSON() ([]byte, error) { if math.IsNaN(float64(f)) || math.IsInf(float64(f), 0) { return json.Marshal(nil) } return json.Marshal(float64(f)) } type RMS struct { Callsign string `json:"callsign"` Gridsquare string `json:"gridsquare"` Distance JSONFloat64 `json:"distance"` Azimuth JSONFloat64 `json:"azimuth"` Modes string `json:"modes"` Freq Frequency `json:"freq"` Dial Frequency `json:"dial"` URL *JSONURL `json:"url"` } func (r RMS) IsMode(mode string) bool { return strings.Contains(strings.ToLower(r.Modes), mode) } func (r RMS) IsBand(band string) bool { return bands[band].Contains(r.Freq) } type byDist []RMS func (r byDist) Len() int { return len(r) } func (r byDist) Swap(i, j int) { r[i], r[j] = r[j], r[i] } func (r byDist) Less(i, j int) bool { return r[i].Distance < r[j].Distance } func rmsListHandle(args []string) { set := pflag.NewFlagSet("rmslist", pflag.ExitOnError) mode := set.StringP("mode", "m", "", "") band := set.StringP("band", "b", "", "") forceDownload := set.BoolP("force-download", "d", false, "") byDistance := set.BoolP("sort-distance", "s", false, "") set.Parse(args) var query string if len(set.Args()) > 0 { query = strings.ToUpper(set.Args()[0]) } *mode = strings.ToLower(*mode) rList, err := ReadRMSList(*forceDownload, func(rms RMS) bool { switch { case query != "" && !strings.HasPrefix(rms.Callsign, query): return false case mode != nil && !rms.IsMode(*mode): return false case band != nil && !rms.IsBand(*band): return false default: return true } }) if err != nil { log.Fatal(err) } if *byDistance { sort.Sort(byDist(rList)) } fmtStr := "%-9.9s [%-6.6s] %-6.6s %3.3s %-15.15s %14.14s %14.14s %s\n" // Print header fmt.Printf(fmtStr, "callsign", "gridsq", "dist", "Az", "mode(s)", "dial freq", "center freq", "url") // Print gateways (separated by blank line) for i := 0; i < len(rList); i++ { r := rList[i] distance := strconv.FormatFloat(float64(r.Distance), 'f', 0, 64) azimuth := strconv.FormatFloat(float64(r.Azimuth), 'f', 0, 64) fmt.Printf(fmtStr, r.Callsign, r.Gridsquare, distance, azimuth, r.Modes, r.Dial, r.Freq, r.URL) if i+1 < len(rList) && rList[i].Callsign != rList[i+1].Callsign { fmt.Println("") } } } func ReadRMSList(forceDownload bool, filterFn func(rms RMS) (keep bool)) ([]RMS, error) { me, err := maidenhead.ParseLocator(config.Locator) if err != nil { log.Print("Missing or Invalid Locator, will not compute distance and Azimuth") } fileName := "rmslist" isDefaultServiceCode := len(config.ServiceCodes) == 1 && config.ServiceCodes[0] == "PUBLIC" if !isDefaultServiceCode { fileName += "-" + strings.Join(config.ServiceCodes, "-") } filePath := filepath.Join(directories.DataDir(), fileName+".json") debug.Printf("RMS list file is %s", filePath) f, err := cmsapi.GetGatewayStatusCached(filePath, forceDownload, config.ServiceCodes...) if err != nil { return nil, err } defer f.Close() var status cmsapi.GatewayStatus if err = json.NewDecoder(f).Decode(&status); err != nil { return nil, err } var slice []RMS for _, gw := range status.Gateways { for _, channel := range gw.Channels { r := RMS{ Callsign: gw.Callsign, Gridsquare: channel.Gridsquare, Modes: channel.SupportedModes, Freq: Frequency(channel.Frequency), Dial: Frequency(channel.Frequency).Dial(channel.SupportedModes), } if chURL := toURL(channel, gw.Callsign); chURL != nil { r.URL = &JSONURL{*chURL} } hasLocator := me != maidenhead.Point{} if them, err := maidenhead.ParseLocator(channel.Gridsquare); err == nil && hasLocator { r.Distance = JSONFloat64(me.Distance(them)) r.Azimuth = JSONFloat64(me.Bearing(them)) } if keep := filterFn(r); !keep { continue } slice = append(slice, r) } } return slice, nil } func toURL(gc cmsapi.GatewayChannel, targetcall string) *url.URL { freq := Frequency(gc.Frequency).Dial(gc.SupportedModes) chURL, _ := url.Parse(fmt.Sprintf("%s:///%s?freq=%v", toTransport(gc), targetcall, freq.KHz())) return chURL } var transports = []string{MethodWinmor, MethodAX25, MethodPactor, MethodArdop} func toTransport(gc cmsapi.GatewayChannel) string { modes := strings.ToLower(gc.SupportedModes) for _, transport := range transports { if strings.Contains(modes, "packet") { // bug(maritnhpedersen): We really don't know which transport to use here. It could be serial-tnc or ax25, but ax25 is most likely. return MethodAX25 } if strings.Contains(modes, transport) { return transport } } return "" } pat-0.12.1/schedule.go000066400000000000000000000014651415513746300145210ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "log" "time" "github.com/gorhill/cronexpr" ) type Job struct { expr *cronexpr.Expression cmd string next time.Time } func scheduleLoop() { jobs := make([]*Job, 0, len(config.Schedule)) for exprStr, cmd := range config.Schedule { expr := cronexpr.MustParse(exprStr) jobs = append(jobs, &Job{ expr, cmd, expr.Next(time.Now()), }) } go func() { for range time.Tick(time.Second) { for _, j := range jobs { if time.Now().Before(j.next) { continue } log.Printf("Executing scheduled command '%s'...", j.cmd) execCmd(j.cmd) j.next = j.expr.Next(time.Now()) } } }() } pat-0.12.1/share/000077500000000000000000000000001415513746300134725ustar00rootroot00000000000000pat-0.12.1/share/ardopc/000077500000000000000000000000001415513746300147425ustar00rootroot00000000000000pat-0.12.1/share/ardopc/ardop@.service000066400000000000000000000003311415513746300175260ustar00rootroot00000000000000[Unit] Description=ardopc - ARDOP softmodem for %i After=network.target sound.target [Service] User=%i ExecStart=/bin/sh -c "cd /tmp && /usr/local/bin/ardopc" Restart=on-failure [Install] WantedBy=multi-user.target pat-0.12.1/share/ardopc/install-systemd-ardop-unit.bash000077500000000000000000000005621415513746300230230ustar00rootroot00000000000000#!/usr/bin/env bash set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" [ -e "/etc/systemd/system/ardop@.service" ] && rm /etc/systemd/system/ardop@.service cp "$DIR/ardop@.service" "/lib/systemd/system/ardop@.service" systemctl daemon-reload echo "Installed. Install (pi)ardopc as /usr/local/bin/ardopc and start it with 'systemctl start ardop@username'" pat-0.12.1/share/ax25/000077500000000000000000000000001415513746300142515ustar00rootroot00000000000000pat-0.12.1/share/ax25/README.systemd000066400000000000000000000003041415513746300166150ustar00rootroot00000000000000For AX.25 as a systemd service: * Edit and copy ax25.service into /etc/systemd/system/ax25.service. * sudo systemctl enable ax25 * sudo systemctl start ax25 * systemctl status ax25 (optional) pat-0.12.1/share/ax25/ax25.default000066400000000000000000000013631415513746300164010ustar00rootroot00000000000000# Configuration for AX.25 systemd unit file from Pat. # The axport from /etc/ax25/axports to bring up. AXPORT=wl2k # The AX.25 baudrate the TNC is configured for. # Make sure this matches the HBAUD setting of your TNC. HBAUD=1200 # The TNC serial path. DEV=/dev/ttyUSB0 # Script for initializing the TNC. # # This optional parameter is convenient when dealing with TNCs that require # additional initialization, e.g. entering KISS mode. Modify to fit your own needs. # #TNC_INIT_CMD="/usr/local/bin/my_tnc_init_script --serial-tty $DEV --hbaud $HBAUD" # # Example (for Kenwood TH-D7x and TM-D7x0): # Download, modify, compile and install https://github.com/fmarier/tmd710_tncsetup #TNC_INIT_CMD="/usr/local/bin/tmd710_tncsetup -B 1 -S $DEV -b $HBAUD" pat-0.12.1/share/ax25/ax25.service000066400000000000000000000003261415513746300164130ustar00rootroot00000000000000[Unit] Description=AX.25 KISS interface After=network.target [Service] EnvironmentFile=/etc/default/ax25 Type=forking ExecStart=/usr/share/pat/bin/axup ${DEV} ${AXPORT} ${HBAUD} [Install] WantedBy=default.target pat-0.12.1/share/ax25/install-systemd-ax25-unit.bash000077500000000000000000000006211415513746300220000ustar00rootroot00000000000000#!/usr/bin/env bash set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" [ -e "/etc/systemd/system/ax25.service" ] && rm /etc/systemd/system/ax25.service cp "$DIR/ax25.service" "/lib/systemd/system/ax25.service" [ -e "/etc/default/ax25" ] || cp "$DIR/ax25.default" "/etc/default/ax25" systemctl daemon-reload echo "Installed. Edit /etc/default/ax25 and start with 'systemctl start ax25'" pat-0.12.1/share/ax25/mheardd.service000066400000000000000000000002431415513746300172360ustar00rootroot00000000000000[Unit] Description=AX.25 mheard daemon After=ax25.service [Service] Type=forking ExecStart=/bin/sh -c "/usr/sbin/mheardd -l &" [Install] WantedBy=default.target pat-0.12.1/share/ax25/soundmodem-example.conf000066400000000000000000000011551415513746300207250ustar00rootroot00000000000000 pat-0.12.1/share/bin/000077500000000000000000000000001415513746300142425ustar00rootroot00000000000000pat-0.12.1/share/bin/axup000077500000000000000000000033321415513746300151460ustar00rootroot00000000000000#!/bin/sh # # This script is written to help configure an axport to use as a winlink node. # It sets AX.25 and KISS params appropriate to the given HBAUD (link baudrate) set -e if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] then echo "Usage: $0 [tty] [axport] [1200/9600]" exit 1; fi # Init TNC (optional) [ -z "$TNC_INIT_CMD" ] || eval "$TNC_INIT_CMD"; if [ "$3" -eq "9600" ]; then /usr/sbin/kissattach $1 $2 -m 256 /usr/sbin/kissparms -p $2 -t 100 -l 10 -s 12 -r 80 -f n echo 4 > /proc/sys/net/ax25/ax0/standard_window_size # 2-7 (max frames) echo 256 > /proc/sys/net/ax25/ax0/maximum_packet_length # 1-512 (paclen) echo 3100 > /proc/sys/net/ax25/ax0/t1_timeout # (Frack /1000 = seconds) echo 800 > /proc/sys/net/ax25/ax0/t2_timeout # (RESPtime /1000 = seconds) echo 300000 > /proc/sys/net/ax25/ax0/t3_timeout # (Check /1000 = seconds) echo 100000 > /proc/sys/net/ax25/ax0/idle_timeout # (/10000(?) = seconds) echo 5 > /proc/sys/net/ax25/ax0/maximum_retry_count # n echo 2 > /proc/sys/net/ax25/ax0/connect_mode # 0 = None, 1 = Network, 2 = All elif [ "$3" -eq "1200" ]; then /usr/sbin/kissattach $1 $2 -m 128 /usr/sbin/kissparms -p $2 -t 300 -l 10 -s 12 -r 80 -f n echo 4 > /proc/sys/net/ax25/ax0/standard_window_size echo 128 > /proc/sys/net/ax25/ax0/maximum_packet_length echo 2000 > /proc/sys/net/ax25/ax0/t1_timeout echo 1000 > /proc/sys/net/ax25/ax0/t2_timeout echo 300000 > /proc/sys/net/ax25/ax0/t3_timeout echo 100000 > /proc/sys/net/ax25/ax0/idle_timeout echo 5 > /proc/sys/net/ax25/ax0/maximum_retry_count echo 2 > /proc/sys/net/ax25/ax0/connect_mode else echo "Invalid HBAUD $3" return 1; fi pat-0.12.1/share/rigctld/000077500000000000000000000000001415513746300151225ustar00rootroot00000000000000pat-0.12.1/share/rigctld/install-systemd-rigctld-unit.bash000077500000000000000000000006541415513746300235300ustar00rootroot00000000000000#!/usr/bin/env bash set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" [ -e "/etc/systemd/system/rigctld.service" ] && rm /etc/systemd/system/rigctld.service cp "$DIR/rigctld.service" "/lib/systemd/system/rigctld.service" [ -e "/etc/default/rigctld" ] || cp "$DIR/rigctld.default" "/etc/default/rigctld" systemctl daemon-reload echo "Installed. Edit /etc/default/rigctld and start with 'systemctl start rigctld'" pat-0.12.1/share/rigctld/rigctld.default000066400000000000000000000004321415513746300201170ustar00rootroot00000000000000# Configuration for rigctld systemd unit file from Pat. # Radio model number (-m) # (run rigctl -l to find yours) MODEL=1 # Path to your rig's tty device (-r) DEV=/dev/ttyUSB0 # Additional parameters passed to rigctld # (this example sets serial baud 9600) #EXTRA_OPTS="-s 9600" pat-0.12.1/share/rigctld/rigctld.service000066400000000000000000000003451415513746300201360ustar00rootroot00000000000000[Unit] Description=Rigcontrol with rigctld After=network.target [Service] EnvironmentFile=/etc/default/rigctld ExecStart=/usr/bin/rigctld -m ${MODEL} -r ${DEV} ${EXTRA_OPTS} Restart=on-failure [Install] WantedBy=default.target pat-0.12.1/usage.go000066400000000000000000000047131415513746300140300ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main var ( UsageConnect = `'alias' or 'transport://[host][/digi]/targetcall[?params...]' transport: winmor: WINMOR TNC ardop: ARDOP TNC ax25: AX.25 (Linux only) telnet: TCP/IP serial-tnc: Serial AX.25 TNC pactor: SCS PTC modems host: Used to address the host interface (TNC/modem), _not_ to be confused with the connection PATH. Format: [user[:pass]@]host[:port] telnet: [user:pass]@host:port ax25: (optional) host=axport pactor: (optional) serial device (e.g. COM1 or /dev/ttyUSB0) path: The last element of the path is the target station's callsign. If the path has multiple hops (e.g. AX.25), they are separated by '/'. params: ?freq= Sets QSY frequency (winmor, ardop and ax25 only) ?host= Overrides the host part of the path. Useful for serial-tnc to specify e.g. /dev/ttyS0. ` ExampleConnect = ` connect telnet (alias) Connect to one of the Winlink Common Message Servers via tcp. connect ax25:///LA1B-10 Connect to the RMS Gateway LA1B-10 using Linux AX.25 on the default axport. connect ax25://tmd710/LA1B-10 Connect to the RMS Gateway LA1B-10 using Linux AX.25 on axport 'tmd710'. connect ax25:///LA1B/LA5NTA Peer-to-peer connection with LA5NTA via LA1B digipeater. connect winmor:///LA3F Connect to the RMS HF Gateway LA3F using WINMOR TNC on default tcp address and port. connect winmor:///LA3F?freq=5350 Same as above, but set dial frequency of the radio using rigcontrol. connect ardop:///LA3F Connect to the RMS HF Gateway LA3F using ARDOP on the default tcp address and port. connect ardop:///LA3F?freq=5350 Same as above, but set dial frequency of the radio using rigcontrol. connect serial-tnc:///LA1B-10 Connect to the RMS Gateway LA1B-10 over a AX.25 serial TNC on the default serial port. connect pactor:///LA3F Connect to RMS HF Gateway LA3F using PACTOR. ` ) var ExamplePosition = ` position -c "QRV 145.500MHz" Send position and comment with coordinates retrieved from GPSd. position --latlon 59.123,005.123 Send position 59.123N 005.123E. position --latlon 40.704,-73.945 Send position 40.704N 073.945W. position --latlon -10.123,-60.123 Send position 10.123S 060.123W. ` pat-0.12.1/utils.go000066400000000000000000000004451415513746300140620ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "unicode" ) func SplitFunc(c rune) bool { return unicode.IsSpace(c) || c == ',' || c == ';' } pat-0.12.1/version_report.go000066400000000000000000000042611415513746300160020ustar00rootroot00000000000000// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. // Use of this source code is governed by the MIT-license that can be // found in the LICENSE file. package main import ( "encoding/json" "fmt" "os" "path/filepath" "runtime" "time" "github.com/la5nta/pat/internal/buildinfo" "github.com/la5nta/pat/internal/cmsapi" "github.com/la5nta/pat/internal/directories" ) func accountExistsCached(callsign string) (bool, error) { var cache struct { Expires time.Time AccountExists bool } fileName := fmt.Sprintf(".cached_account_check_%s.json", callsign) filePath := filepath.Join(directories.StateDir(), fileName) f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0o600) if err != nil { return false, err } json.NewDecoder(f).Decode(&cache) if time.Since(cache.Expires) < 0 { return cache.AccountExists, nil } defer func() { f.Truncate(0) f.Seek(0, 0) json.NewEncoder(f).Encode(cache) }() exists, err := cmsapi.AccountExists(callsign) if !exists || err != nil { // Let's try again in 48 hours cache.Expires = time.Now().Add(48 * time.Hour) return false, err } // Keep this response for a month. It will probably not change. cache.Expires = time.Now().Add(30 * 24 * time.Hour) cache.AccountExists = exists return exists, err } func postVersionUpdate() error { var lastUpdated time.Time filePath := filepath.Join(directories.StateDir(), "last_version_report.json") file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0o600) if err != nil { return err } defer file.Close() json.NewDecoder(file).Decode(&lastUpdated) if time.Since(lastUpdated) < 24*time.Hour { return nil } // WDT do not want us to post version reports for callsigns without a registered account if exists, err := accountExistsCached(fOptions.MyCall); err != nil { return err } else if !exists { return nil } v := cmsapi.VersionAdd{ Callsign: fOptions.MyCall, Program: buildinfo.AppName, Version: buildinfo.Version, Comments: fmt.Sprintf("%s - %s/%s", buildinfo.GitRev, runtime.GOOS, runtime.GOARCH), } if err := v.Post(); err != nil { return err } file.Truncate(0) file.Seek(0, 0) return json.NewEncoder(file).Encode(time.Now()) } pat-0.12.1/web/000077500000000000000000000000001415513746300131455ustar00rootroot00000000000000pat-0.12.1/web/.gitignore000066400000000000000000000000171415513746300151330ustar00rootroot00000000000000/node_modules/ pat-0.12.1/web/.prettierrc000066400000000000000000000001521415513746300153270ustar00rootroot00000000000000{ "printWidth": 100, "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5" } pat-0.12.1/web/package-lock.json000066400000000000000000000015221415513746300163610ustar00rootroot00000000000000{ "name": "web", "lockfileVersion": 2, "requires": true, "packages": { "": { "devDependencies": { "prettier": "^2.3.2" } }, "node_modules/prettier": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", "dev": true, "bin": { "prettier": "bin-prettier.js" }, "engines": { "node": ">=10.13.0" } } }, "dependencies": { "prettier": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", "dev": true } } } pat-0.12.1/web/package.json000066400000000000000000000002351415513746300154330ustar00rootroot00000000000000{ "devDependencies": { "prettier": "^2.3.2" }, "scripts": { "format": "npx prettier -w res/tmpl/index.html res/js/index.js res/site.css" } } pat-0.12.1/web/res/000077500000000000000000000000001415513746300137365ustar00rootroot00000000000000pat-0.12.1/web/res/LICENSES.md000066400000000000000000000146041415513746300154720ustar00rootroot00000000000000The following licenses covers all third party software used by the web GUI ============================================================================== Name: Bootstrap Version: v3.2.0 Repository: github.com/twbs/bootstrap The MIT License (MIT) Copyright (c) 2011-2016 Twitter, Inc. 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. ============================================================================== Name: JQuery Version: v1.11.1 Repository: github.com/jquery/jquery Copyright 2014 jQuery Foundation and other contributors http://jquery.com/ 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. ============================================================================== Name: URI.js Version: v1.16.1 (?) Repository: github.com/medialize/URI.js The MIT License (MIT) Copyright (c) 2011 Rodney Rehm 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. ============================================================================== Name: Bootstrap-select Version: v1.7.5 Repository: github.com/silviomoreto/bootstrap-select The MIT License (MIT) Copyright (c) 2013-2015 bootstrap-select 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. ============================================================================== Name: Boostrap-tokenfield (Open-Xchange-Frontend fork) Version: v0.12.1 (db79219699545ddea9b82ce31716045d064a5d5c) Repository: github.com/Open-Xchange-Frontend/bootstrap-tokenfield #### Sliptree - by Illimar Tambek for [Sliptree](http://sliptree.com) - Copyright (c) 2013 by Sliptree Available for use under the [MIT License](http://en.wikipedia.org/wiki/MIT_License) 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. pat-0.12.1/web/res/bootstrap-3.2.0-dist/000077500000000000000000000000001415513746300173525ustar00rootroot00000000000000pat-0.12.1/web/res/bootstrap-3.2.0-dist/css/000077500000000000000000000000001415513746300201425ustar00rootroot00000000000000pat-0.12.1/web/res/bootstrap-3.2.0-dist/css/bootstrap-theme.css000066400000000000000000000515701415513746300240010ustar00rootroot00000000000000/*! * Bootstrap v3.2.0 (http://getbootstrap.com) * Copyright 2011-2014 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, .2); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(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, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } .btn:active, .btn.active { background-image: none; } .btn-default { text-shadow: 0 1px 0 #fff; background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); background-image: linear-gradient(to bottom, #fff 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; 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] { background-color: #e0e0e0; background-image: none; } .btn-primary { background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2)); background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #2b669a; } .btn-primary:hover, .btn-primary:focus { background-color: #2d6ca2; background-position: 0 -15px; } .btn-primary:active, .btn-primary.active { background-color: #2d6ca2; border-color: #2b669a; } .btn-primary:disabled, .btn-primary[disabled] { background-color: #2d6ca2; 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] { 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] { 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] { 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] { background-color: #c12e2a; background-image: none; } .thumbnail, .img-thumbnail { -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); box-shadow: 0 1px 2px rgba(0, 0, 0, .075); } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { background-color: #e8e8e8; 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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); background-repeat: repeat-x; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { background-color: #357ebd; background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); background-repeat: repeat-x; } .navbar-default { background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); } .navbar-default .navbar-nav > .active > a { background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3)); background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); background-repeat: repeat-x; -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); } .navbar-brand, .navbar-nav > li > a { text-shadow: 0 1px 0 rgba(255, 255, 255, .25); } .navbar-inverse { background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; } .navbar-inverse .navbar-nav > .active > a { background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%); background-image: -o-linear-gradient(top, #222 0%, #282828 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828)); background-image: linear-gradient(to bottom, #222 0%, #282828 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); background-repeat: repeat-x; -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); } .navbar-inverse .navbar-brand, .navbar-inverse .navbar-nav > li > a { text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); } .navbar-static-top, .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } .alert { text-shadow: 0 1px 0 rgba(255, 255, 255, .2); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); background-repeat: repeat-x; 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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); background-repeat: repeat-x; 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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); background-repeat: repeat-x; 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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); background-repeat: repeat-x; 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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); background-repeat: repeat-x; } .progress-bar { background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9)); background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); background-repeat: repeat-x; } .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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); background-repeat: repeat-x; } .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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); background-repeat: repeat-x; } .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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); background-repeat: repeat-x; } .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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); background-repeat: repeat-x; } .progress-bar-striped { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .list-group { border-radius: 4px; -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); box-shadow: 0 1px 2px rgba(0, 0, 0, .075); } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { text-shadow: 0 -1px 0 #3071a9; background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3)); background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); background-repeat: repeat-x; border-color: #3278b3; } .panel { -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); box-shadow: 0 1px 2px rgba(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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); background-repeat: repeat-x; } .panel-primary > .panel-heading { background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); background-repeat: repeat-x; } .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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); background-repeat: repeat-x; } .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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); background-repeat: repeat-x; } .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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); background-repeat: repeat-x; } .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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); background-repeat: repeat-x; } .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%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); background-repeat: repeat-x; border-color: #dcdcdc; -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); } /*# sourceMappingURL=bootstrap-theme.css.map */ pat-0.12.1/web/res/bootstrap-3.2.0-dist/css/bootstrap-theme.css.map000066400000000000000000000550371415513746300245570ustar00rootroot00000000000000{"version":3,"file":"bootstrap-theme.css","sources":["less/theme.less","less/mixins/vendor-prefixes.less","bootstrap-theme.css","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"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","sourcesContent":["\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &:disabled,\n &[disabled] {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-bg, 5%); @end-color: darken(@navbar-default-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-bg; @end-color: lighten(@navbar-inverse-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n}\n\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n &::-moz-placeholder { color: @color; // Firefox\n opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n",null,"// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]}pat-0.12.1/web/res/bootstrap-3.2.0-dist/css/bootstrap-theme.min.css000066400000000000000000000446541415513746300245700ustar00rootroot00000000000000/*! * Bootstrap v3.2.0 (http://getbootstrap.com) * Copyright 2011-2014 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,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(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,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 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;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]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-o-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#2d6ca2));background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-primary:disabled,.btn-primary[disabled]{background-color:#2d6ca2;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]{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]{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]{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]{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f3f3f3));background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:-o-linear-gradient(top,#222 0,#282828 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#222),to(#282828));background-image:linear-gradient(to bottom,#222 0,#282828 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-o-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3071a9));background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);background-repeat:repeat-x}.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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-o-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3278b3));background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);background-repeat:repeat-x;border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.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%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}pat-0.12.1/web/res/bootstrap-3.2.0-dist/css/bootstrap.css000066400000000000000000004027021415513746300226760ustar00rootroot00000000000000/*! * Bootstrap v3.2.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! normalize.css v3.0.1 | MIT License | git.io/normalize */ html { font-family: sans-serif; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } body { margin: 0; } article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } audio, canvas, progress, video { display: inline-block; vertical-align: baseline; } audio:not([controls]) { display: none; height: 0; } [hidden], template { display: none; } a { background: transparent; } a:active, a:hover { outline: 0; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } dfn { font-style: italic; } h1 { margin: .67em 0; font-size: 2em; } mark { color: #000; background: #ff0; } small { font-size: 80%; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -.5em; } sub { bottom: -.25em; } img { border: 0; } svg:not(:root) { overflow: hidden; } figure { margin: 1em 40px; } hr { height: 0; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } pre { overflow: auto; } code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } button, input, optgroup, select, textarea { margin: 0; font: inherit; color: inherit; } button { overflow: visible; } button, select { text-transform: none; } button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; } button[disabled], html input[disabled] { cursor: default; } button::-moz-focus-inner, input::-moz-focus-inner { padding: 0; border: 0; } input { line-height: normal; } input[type="checkbox"], input[type="radio"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; padding: 0; } input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } input[type="search"] { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; -webkit-appearance: textfield; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } fieldset { padding: .35em .625em .75em; margin: 0 2px; border: 1px solid #c0c0c0; } legend { padding: 0; border: 0; } textarea { overflow: auto; } optgroup { font-weight: bold; } table { border-spacing: 0; border-collapse: collapse; } td, th { padding: 0; } @media print { * { color: #000 !important; text-shadow: none !important; background: transparent !important; -webkit-box-shadow: none !important; box-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } select { background: #fff !important; } .navbar { display: none; } .table td, .table th { background-color: #fff !important; } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px solid #000; } .table { border-collapse: collapse !important; } .table-bordered th, .table-bordered td { border: 1px solid #ddd !important; } } @font-face { font-family: 'Glyphicons Halflings'; src: url('../fonts/glyphicons-halflings-regular.eot'); src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); } .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .glyphicon-asterisk:before { content: "\2a"; } .glyphicon-plus:before { content: "\2b"; } .glyphicon-euro:before { content: "\20ac"; } .glyphicon-minus:before { content: "\2212"; } .glyphicon-cloud:before { content: "\2601"; } .glyphicon-envelope:before { content: "\2709"; } .glyphicon-pencil:before { content: "\270f"; } .glyphicon-glass:before { content: "\e001"; } .glyphicon-music:before { content: "\e002"; } .glyphicon-search:before { content: "\e003"; } .glyphicon-heart:before { content: "\e005"; } .glyphicon-star:before { content: "\e006"; } .glyphicon-star-empty:before { content: "\e007"; } .glyphicon-user:before { content: "\e008"; } .glyphicon-film:before { content: "\e009"; } .glyphicon-th-large:before { content: "\e010"; } .glyphicon-th:before { content: "\e011"; } .glyphicon-th-list:before { content: "\e012"; } .glyphicon-ok:before { content: "\e013"; } .glyphicon-remove:before { content: "\e014"; } .glyphicon-zoom-in:before { content: "\e015"; } .glyphicon-zoom-out:before { content: "\e016"; } .glyphicon-off:before { content: "\e017"; } .glyphicon-signal:before { content: "\e018"; } .glyphicon-cog:before { content: "\e019"; } .glyphicon-trash:before { content: "\e020"; } .glyphicon-home:before { content: "\e021"; } .glyphicon-file:before { content: "\e022"; } .glyphicon-time:before { content: "\e023"; } .glyphicon-road:before { content: "\e024"; } .glyphicon-download-alt:before { content: "\e025"; } .glyphicon-download:before { content: "\e026"; } .glyphicon-upload:before { content: "\e027"; } .glyphicon-inbox:before { content: "\e028"; } .glyphicon-play-circle:before { content: "\e029"; } .glyphicon-repeat:before { content: "\e030"; } .glyphicon-refresh:before { content: "\e031"; } .glyphicon-list-alt:before { content: "\e032"; } .glyphicon-lock:before { content: "\e033"; } .glyphicon-flag:before { content: "\e034"; } .glyphicon-headphones:before { content: "\e035"; } .glyphicon-volume-off:before { content: "\e036"; } .glyphicon-volume-down:before { content: "\e037"; } .glyphicon-volume-up:before { content: "\e038"; } .glyphicon-qrcode:before { content: "\e039"; } .glyphicon-barcode:before { content: "\e040"; } .glyphicon-tag:before { content: "\e041"; } .glyphicon-tags:before { content: "\e042"; } .glyphicon-book:before { content: "\e043"; } .glyphicon-bookmark:before { content: "\e044"; } .glyphicon-print:before { content: "\e045"; } .glyphicon-camera:before { content: "\e046"; } .glyphicon-font:before { content: "\e047"; } .glyphicon-bold:before { content: "\e048"; } .glyphicon-italic:before { content: "\e049"; } .glyphicon-text-height:before { content: "\e050"; } .glyphicon-text-width:before { content: "\e051"; } .glyphicon-align-left:before { content: "\e052"; } .glyphicon-align-center:before { content: "\e053"; } .glyphicon-align-right:before { content: "\e054"; } .glyphicon-align-justify:before { content: "\e055"; } .glyphicon-list:before { content: "\e056"; } .glyphicon-indent-left:before { content: "\e057"; } .glyphicon-indent-right:before { content: "\e058"; } .glyphicon-facetime-video:before { content: "\e059"; } .glyphicon-picture:before { content: "\e060"; } .glyphicon-map-marker:before { content: "\e062"; } .glyphicon-adjust:before { content: "\e063"; } .glyphicon-tint:before { content: "\e064"; } .glyphicon-edit:before { content: "\e065"; } .glyphicon-share:before { content: "\e066"; } .glyphicon-check:before { content: "\e067"; } .glyphicon-move:before { content: "\e068"; } .glyphicon-step-backward:before { content: "\e069"; } .glyphicon-fast-backward:before { content: "\e070"; } .glyphicon-backward:before { content: "\e071"; } .glyphicon-play:before { content: "\e072"; } .glyphicon-pause:before { content: "\e073"; } .glyphicon-stop:before { content: "\e074"; } .glyphicon-forward:before { content: "\e075"; } .glyphicon-fast-forward:before { content: "\e076"; } .glyphicon-step-forward:before { content: "\e077"; } .glyphicon-eject:before { content: "\e078"; } .glyphicon-chevron-left:before { content: "\e079"; } .glyphicon-chevron-right:before { content: "\e080"; } .glyphicon-plus-sign:before { content: "\e081"; } .glyphicon-minus-sign:before { content: "\e082"; } .glyphicon-remove-sign:before { content: "\e083"; } .glyphicon-ok-sign:before { content: "\e084"; } .glyphicon-question-sign:before { content: "\e085"; } .glyphicon-info-sign:before { content: "\e086"; } .glyphicon-screenshot:before { content: "\e087"; } .glyphicon-remove-circle:before { content: "\e088"; } .glyphicon-ok-circle:before { content: "\e089"; } .glyphicon-ban-circle:before { content: "\e090"; } .glyphicon-arrow-left:before { content: "\e091"; } .glyphicon-arrow-right:before { content: "\e092"; } .glyphicon-arrow-up:before { content: "\e093"; } .glyphicon-arrow-down:before { content: "\e094"; } .glyphicon-share-alt:before { content: "\e095"; } .glyphicon-resize-full:before { content: "\e096"; } .glyphicon-resize-small:before { content: "\e097"; } .glyphicon-exclamation-sign:before { content: "\e101"; } .glyphicon-gift:before { content: "\e102"; } .glyphicon-leaf:before { content: "\e103"; } .glyphicon-fire:before { content: "\e104"; } .glyphicon-eye-open:before { content: "\e105"; } .glyphicon-eye-close:before { content: "\e106"; } .glyphicon-warning-sign:before { content: "\e107"; } .glyphicon-plane:before { content: "\e108"; } .glyphicon-calendar:before { content: "\e109"; } .glyphicon-random:before { content: "\e110"; } .glyphicon-comment:before { content: "\e111"; } .glyphicon-magnet:before { content: "\e112"; } .glyphicon-chevron-up:before { content: "\e113"; } .glyphicon-chevron-down:before { content: "\e114"; } .glyphicon-retweet:before { content: "\e115"; } .glyphicon-shopping-cart:before { content: "\e116"; } .glyphicon-folder-close:before { content: "\e117"; } .glyphicon-folder-open:before { content: "\e118"; } .glyphicon-resize-vertical:before { content: "\e119"; } .glyphicon-resize-horizontal:before { content: "\e120"; } .glyphicon-hdd:before { content: "\e121"; } .glyphicon-bullhorn:before { content: "\e122"; } .glyphicon-bell:before { content: "\e123"; } .glyphicon-certificate:before { content: "\e124"; } .glyphicon-thumbs-up:before { content: "\e125"; } .glyphicon-thumbs-down:before { content: "\e126"; } .glyphicon-hand-right:before { content: "\e127"; } .glyphicon-hand-left:before { content: "\e128"; } .glyphicon-hand-up:before { content: "\e129"; } .glyphicon-hand-down:before { content: "\e130"; } .glyphicon-circle-arrow-right:before { content: "\e131"; } .glyphicon-circle-arrow-left:before { content: "\e132"; } .glyphicon-circle-arrow-up:before { content: "\e133"; } .glyphicon-circle-arrow-down:before { content: "\e134"; } .glyphicon-globe:before { content: "\e135"; } .glyphicon-wrench:before { content: "\e136"; } .glyphicon-tasks:before { content: "\e137"; } .glyphicon-filter:before { content: "\e138"; } .glyphicon-briefcase:before { content: "\e139"; } .glyphicon-fullscreen:before { content: "\e140"; } .glyphicon-dashboard:before { content: "\e141"; } .glyphicon-paperclip:before { content: "\e142"; } .glyphicon-heart-empty:before { content: "\e143"; } .glyphicon-link:before { content: "\e144"; } .glyphicon-phone:before { content: "\e145"; } .glyphicon-pushpin:before { content: "\e146"; } .glyphicon-usd:before { content: "\e148"; } .glyphicon-gbp:before { content: "\e149"; } .glyphicon-sort:before { content: "\e150"; } .glyphicon-sort-by-alphabet:before { content: "\e151"; } .glyphicon-sort-by-alphabet-alt:before { content: "\e152"; } .glyphicon-sort-by-order:before { content: "\e153"; } .glyphicon-sort-by-order-alt:before { content: "\e154"; } .glyphicon-sort-by-attributes:before { content: "\e155"; } .glyphicon-sort-by-attributes-alt:before { content: "\e156"; } .glyphicon-unchecked:before { content: "\e157"; } .glyphicon-expand:before { content: "\e158"; } .glyphicon-collapse-down:before { content: "\e159"; } .glyphicon-collapse-up:before { content: "\e160"; } .glyphicon-log-in:before { content: "\e161"; } .glyphicon-flash:before { content: "\e162"; } .glyphicon-log-out:before { content: "\e163"; } .glyphicon-new-window:before { content: "\e164"; } .glyphicon-record:before { content: "\e165"; } .glyphicon-save:before { content: "\e166"; } .glyphicon-open:before { content: "\e167"; } .glyphicon-saved:before { content: "\e168"; } .glyphicon-import:before { content: "\e169"; } .glyphicon-export:before { content: "\e170"; } .glyphicon-send:before { content: "\e171"; } .glyphicon-floppy-disk:before { content: "\e172"; } .glyphicon-floppy-saved:before { content: "\e173"; } .glyphicon-floppy-remove:before { content: "\e174"; } .glyphicon-floppy-save:before { content: "\e175"; } .glyphicon-floppy-open:before { content: "\e176"; } .glyphicon-credit-card:before { content: "\e177"; } .glyphicon-transfer:before { content: "\e178"; } .glyphicon-cutlery:before { content: "\e179"; } .glyphicon-header:before { content: "\e180"; } .glyphicon-compressed:before { content: "\e181"; } .glyphicon-earphone:before { content: "\e182"; } .glyphicon-phone-alt:before { content: "\e183"; } .glyphicon-tower:before { content: "\e184"; } .glyphicon-stats:before { content: "\e185"; } .glyphicon-sd-video:before { content: "\e186"; } .glyphicon-hd-video:before { content: "\e187"; } .glyphicon-subtitles:before { content: "\e188"; } .glyphicon-sound-stereo:before { content: "\e189"; } .glyphicon-sound-dolby:before { content: "\e190"; } .glyphicon-sound-5-1:before { content: "\e191"; } .glyphicon-sound-6-1:before { content: "\e192"; } .glyphicon-sound-7-1:before { content: "\e193"; } .glyphicon-copyright-mark:before { content: "\e194"; } .glyphicon-registration-mark:before { content: "\e195"; } .glyphicon-cloud-download:before { content: "\e197"; } .glyphicon-cloud-upload:before { content: "\e198"; } .glyphicon-tree-conifer:before { content: "\e199"; } .glyphicon-tree-deciduous:before { content: "\e200"; } * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 10px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.42857143; color: #333; background-color: #fff; } input, button, select, textarea { font-family: inherit; font-size: inherit; line-height: inherit; } a { color: #428bca; text-decoration: none; } a:hover, a:focus { color: #2a6496; text-decoration: underline; } a:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } figure { margin: 0; } img { vertical-align: middle; } .img-responsive, .thumbnail > img, .thumbnail a > img, .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; width: 100% \9; max-width: 100%; height: auto; } .img-rounded { border-radius: 6px; } .img-thumbnail { display: inline-block; width: 100% \9; max-width: 100%; height: auto; padding: 4px; line-height: 1.42857143; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; -webkit-transition: all .2s ease-in-out; -o-transition: all .2s ease-in-out; transition: all .2s ease-in-out; } .img-circle { border-radius: 50%; } hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eee; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } .sr-only-focusable:active, .sr-only-focusable:focus { position: static; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: inherit; font-weight: 500; line-height: 1.1; color: inherit; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { font-weight: normal; line-height: 1; color: #777; } h1, .h1, h2, .h2, h3, .h3 { margin-top: 20px; margin-bottom: 10px; } h1 small, .h1 small, h2 small, .h2 small, h3 small, .h3 small, h1 .small, .h1 .small, h2 .small, .h2 .small, h3 .small, .h3 .small { font-size: 65%; } h4, .h4, h5, .h5, h6, .h6 { margin-top: 10px; margin-bottom: 10px; } h4 small, .h4 small, h5 small, .h5 small, h6 small, .h6 small, h4 .small, .h4 .small, h5 .small, .h5 .small, h6 .small, .h6 .small { font-size: 75%; } h1, .h1 { font-size: 36px; } h2, .h2 { font-size: 30px; } h3, .h3 { font-size: 24px; } h4, .h4 { font-size: 18px; } h5, .h5 { font-size: 14px; } h6, .h6 { font-size: 12px; } p { margin: 0 0 10px; } .lead { margin-bottom: 20px; font-size: 16px; font-weight: 300; line-height: 1.4; } @media (min-width: 768px) { .lead { font-size: 21px; } } small, .small { font-size: 85%; } cite { font-style: normal; } mark, .mark { padding: .2em; background-color: #fcf8e3; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } .text-justify { text-align: justify; } .text-nowrap { white-space: nowrap; } .text-lowercase { text-transform: lowercase; } .text-uppercase { text-transform: uppercase; } .text-capitalize { text-transform: capitalize; } .text-muted { color: #777; } .text-primary { color: #428bca; } a.text-primary:hover { color: #3071a9; } .text-success { color: #3c763d; } a.text-success:hover { color: #2b542c; } .text-info { color: #31708f; } a.text-info:hover { color: #245269; } .text-warning { color: #8a6d3b; } a.text-warning:hover { color: #66512c; } .text-danger { color: #a94442; } a.text-danger:hover { color: #843534; } .bg-primary { color: #fff; background-color: #428bca; } a.bg-primary:hover { background-color: #3071a9; } .bg-success { background-color: #dff0d8; } a.bg-success:hover { background-color: #c1e2b3; } .bg-info { background-color: #d9edf7; } a.bg-info:hover { background-color: #afd9ee; } .bg-warning { background-color: #fcf8e3; } a.bg-warning:hover { background-color: #f7ecb5; } .bg-danger { background-color: #f2dede; } a.bg-danger:hover { background-color: #e4b9b9; } .page-header { padding-bottom: 9px; margin: 40px 0 20px; border-bottom: 1px solid #eee; } ul, ol { margin-top: 0; margin-bottom: 10px; } ul ul, ol ul, ul ol, ol ol { margin-bottom: 0; } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; margin-left: -5px; list-style: none; } .list-inline > li { display: inline-block; padding-right: 5px; padding-left: 5px; } dl { margin-top: 0; margin-bottom: 20px; } dt, dd { line-height: 1.42857143; } dt { font-weight: bold; } dd { margin-left: 0; } @media (min-width: 768px) { .dl-horizontal dt { float: left; width: 160px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 180px; } } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #777; } .initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 10px 20px; margin: 0 0 20px; font-size: 17.5px; border-left: 5px solid #eee; } blockquote p:last-child, blockquote ul:last-child, blockquote ol:last-child { margin-bottom: 0; } blockquote footer, blockquote small, blockquote .small { display: block; font-size: 80%; line-height: 1.42857143; color: #777; } blockquote footer:before, blockquote small:before, blockquote .small:before { content: '\2014 \00A0'; } .blockquote-reverse, blockquote.pull-right { padding-right: 15px; padding-left: 0; text-align: right; border-right: 5px solid #eee; border-left: 0; } .blockquote-reverse footer:before, blockquote.pull-right footer:before, .blockquote-reverse small:before, blockquote.pull-right small:before, .blockquote-reverse .small:before, blockquote.pull-right .small:before { content: ''; } .blockquote-reverse footer:after, blockquote.pull-right footer:after, .blockquote-reverse small:after, blockquote.pull-right small:after, .blockquote-reverse .small:after, blockquote.pull-right .small:after { content: '\00A0 \2014'; } blockquote:before, blockquote:after { content: ""; } address { margin-bottom: 20px; font-style: normal; line-height: 1.42857143; } code, kbd, pre, samp { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; background-color: #f9f2f4; border-radius: 4px; } kbd { padding: 2px 4px; font-size: 90%; color: #fff; background-color: #333; border-radius: 3px; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); } kbd kbd { padding: 0; font-size: 100%; -webkit-box-shadow: none; box-shadow: none; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 1.42857143; color: #333; word-break: break-all; word-wrap: break-word; background-color: #f5f5f5; border: 1px solid #ccc; border-radius: 4px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } @media (min-width: 768px) { .container { width: 750px; } } @media (min-width: 992px) { .container { width: 970px; } } @media (min-width: 1200px) { .container { width: 1170px; } } .container-fluid { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } .row { margin-right: -15px; margin-left: -15px; } .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { position: relative; min-height: 1px; padding-right: 15px; padding-left: 15px; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { float: left; } .col-xs-12 { width: 100%; } .col-xs-11 { width: 91.66666667%; } .col-xs-10 { width: 83.33333333%; } .col-xs-9 { width: 75%; } .col-xs-8 { width: 66.66666667%; } .col-xs-7 { width: 58.33333333%; } .col-xs-6 { width: 50%; } .col-xs-5 { width: 41.66666667%; } .col-xs-4 { width: 33.33333333%; } .col-xs-3 { width: 25%; } .col-xs-2 { width: 16.66666667%; } .col-xs-1 { width: 8.33333333%; } .col-xs-pull-12 { right: 100%; } .col-xs-pull-11 { right: 91.66666667%; } .col-xs-pull-10 { right: 83.33333333%; } .col-xs-pull-9 { right: 75%; } .col-xs-pull-8 { right: 66.66666667%; } .col-xs-pull-7 { right: 58.33333333%; } .col-xs-pull-6 { right: 50%; } .col-xs-pull-5 { right: 41.66666667%; } .col-xs-pull-4 { right: 33.33333333%; } .col-xs-pull-3 { right: 25%; } .col-xs-pull-2 { right: 16.66666667%; } .col-xs-pull-1 { right: 8.33333333%; } .col-xs-pull-0 { right: auto; } .col-xs-push-12 { left: 100%; } .col-xs-push-11 { left: 91.66666667%; } .col-xs-push-10 { left: 83.33333333%; } .col-xs-push-9 { left: 75%; } .col-xs-push-8 { left: 66.66666667%; } .col-xs-push-7 { left: 58.33333333%; } .col-xs-push-6 { left: 50%; } .col-xs-push-5 { left: 41.66666667%; } .col-xs-push-4 { left: 33.33333333%; } .col-xs-push-3 { left: 25%; } .col-xs-push-2 { left: 16.66666667%; } .col-xs-push-1 { left: 8.33333333%; } .col-xs-push-0 { left: auto; } .col-xs-offset-12 { margin-left: 100%; } .col-xs-offset-11 { margin-left: 91.66666667%; } .col-xs-offset-10 { margin-left: 83.33333333%; } .col-xs-offset-9 { margin-left: 75%; } .col-xs-offset-8 { margin-left: 66.66666667%; } .col-xs-offset-7 { margin-left: 58.33333333%; } .col-xs-offset-6 { margin-left: 50%; } .col-xs-offset-5 { margin-left: 41.66666667%; } .col-xs-offset-4 { margin-left: 33.33333333%; } .col-xs-offset-3 { margin-left: 25%; } .col-xs-offset-2 { margin-left: 16.66666667%; } .col-xs-offset-1 { margin-left: 8.33333333%; } .col-xs-offset-0 { margin-left: 0; } @media (min-width: 768px) { .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { float: left; } .col-sm-12 { width: 100%; } .col-sm-11 { width: 91.66666667%; } .col-sm-10 { width: 83.33333333%; } .col-sm-9 { width: 75%; } .col-sm-8 { width: 66.66666667%; } .col-sm-7 { width: 58.33333333%; } .col-sm-6 { width: 50%; } .col-sm-5 { width: 41.66666667%; } .col-sm-4 { width: 33.33333333%; } .col-sm-3 { width: 25%; } .col-sm-2 { width: 16.66666667%; } .col-sm-1 { width: 8.33333333%; } .col-sm-pull-12 { right: 100%; } .col-sm-pull-11 { right: 91.66666667%; } .col-sm-pull-10 { right: 83.33333333%; } .col-sm-pull-9 { right: 75%; } .col-sm-pull-8 { right: 66.66666667%; } .col-sm-pull-7 { right: 58.33333333%; } .col-sm-pull-6 { right: 50%; } .col-sm-pull-5 { right: 41.66666667%; } .col-sm-pull-4 { right: 33.33333333%; } .col-sm-pull-3 { right: 25%; } .col-sm-pull-2 { right: 16.66666667%; } .col-sm-pull-1 { right: 8.33333333%; } .col-sm-pull-0 { right: auto; } .col-sm-push-12 { left: 100%; } .col-sm-push-11 { left: 91.66666667%; } .col-sm-push-10 { left: 83.33333333%; } .col-sm-push-9 { left: 75%; } .col-sm-push-8 { left: 66.66666667%; } .col-sm-push-7 { left: 58.33333333%; } .col-sm-push-6 { left: 50%; } .col-sm-push-5 { left: 41.66666667%; } .col-sm-push-4 { left: 33.33333333%; } .col-sm-push-3 { left: 25%; } .col-sm-push-2 { left: 16.66666667%; } .col-sm-push-1 { left: 8.33333333%; } .col-sm-push-0 { left: auto; } .col-sm-offset-12 { margin-left: 100%; } .col-sm-offset-11 { margin-left: 91.66666667%; } .col-sm-offset-10 { margin-left: 83.33333333%; } .col-sm-offset-9 { margin-left: 75%; } .col-sm-offset-8 { margin-left: 66.66666667%; } .col-sm-offset-7 { margin-left: 58.33333333%; } .col-sm-offset-6 { margin-left: 50%; } .col-sm-offset-5 { margin-left: 41.66666667%; } .col-sm-offset-4 { margin-left: 33.33333333%; } .col-sm-offset-3 { margin-left: 25%; } .col-sm-offset-2 { margin-left: 16.66666667%; } .col-sm-offset-1 { margin-left: 8.33333333%; } .col-sm-offset-0 { margin-left: 0; } } @media (min-width: 992px) { .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { float: left; } .col-md-12 { width: 100%; } .col-md-11 { width: 91.66666667%; } .col-md-10 { width: 83.33333333%; } .col-md-9 { width: 75%; } .col-md-8 { width: 66.66666667%; } .col-md-7 { width: 58.33333333%; } .col-md-6 { width: 50%; } .col-md-5 { width: 41.66666667%; } .col-md-4 { width: 33.33333333%; } .col-md-3 { width: 25%; } .col-md-2 { width: 16.66666667%; } .col-md-1 { width: 8.33333333%; } .col-md-pull-12 { right: 100%; } .col-md-pull-11 { right: 91.66666667%; } .col-md-pull-10 { right: 83.33333333%; } .col-md-pull-9 { right: 75%; } .col-md-pull-8 { right: 66.66666667%; } .col-md-pull-7 { right: 58.33333333%; } .col-md-pull-6 { right: 50%; } .col-md-pull-5 { right: 41.66666667%; } .col-md-pull-4 { right: 33.33333333%; } .col-md-pull-3 { right: 25%; } .col-md-pull-2 { right: 16.66666667%; } .col-md-pull-1 { right: 8.33333333%; } .col-md-pull-0 { right: auto; } .col-md-push-12 { left: 100%; } .col-md-push-11 { left: 91.66666667%; } .col-md-push-10 { left: 83.33333333%; } .col-md-push-9 { left: 75%; } .col-md-push-8 { left: 66.66666667%; } .col-md-push-7 { left: 58.33333333%; } .col-md-push-6 { left: 50%; } .col-md-push-5 { left: 41.66666667%; } .col-md-push-4 { left: 33.33333333%; } .col-md-push-3 { left: 25%; } .col-md-push-2 { left: 16.66666667%; } .col-md-push-1 { left: 8.33333333%; } .col-md-push-0 { left: auto; } .col-md-offset-12 { margin-left: 100%; } .col-md-offset-11 { margin-left: 91.66666667%; } .col-md-offset-10 { margin-left: 83.33333333%; } .col-md-offset-9 { margin-left: 75%; } .col-md-offset-8 { margin-left: 66.66666667%; } .col-md-offset-7 { margin-left: 58.33333333%; } .col-md-offset-6 { margin-left: 50%; } .col-md-offset-5 { margin-left: 41.66666667%; } .col-md-offset-4 { margin-left: 33.33333333%; } .col-md-offset-3 { margin-left: 25%; } .col-md-offset-2 { margin-left: 16.66666667%; } .col-md-offset-1 { margin-left: 8.33333333%; } .col-md-offset-0 { margin-left: 0; } } @media (min-width: 1200px) { .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { float: left; } .col-lg-12 { width: 100%; } .col-lg-11 { width: 91.66666667%; } .col-lg-10 { width: 83.33333333%; } .col-lg-9 { width: 75%; } .col-lg-8 { width: 66.66666667%; } .col-lg-7 { width: 58.33333333%; } .col-lg-6 { width: 50%; } .col-lg-5 { width: 41.66666667%; } .col-lg-4 { width: 33.33333333%; } .col-lg-3 { width: 25%; } .col-lg-2 { width: 16.66666667%; } .col-lg-1 { width: 8.33333333%; } .col-lg-pull-12 { right: 100%; } .col-lg-pull-11 { right: 91.66666667%; } .col-lg-pull-10 { right: 83.33333333%; } .col-lg-pull-9 { right: 75%; } .col-lg-pull-8 { right: 66.66666667%; } .col-lg-pull-7 { right: 58.33333333%; } .col-lg-pull-6 { right: 50%; } .col-lg-pull-5 { right: 41.66666667%; } .col-lg-pull-4 { right: 33.33333333%; } .col-lg-pull-3 { right: 25%; } .col-lg-pull-2 { right: 16.66666667%; } .col-lg-pull-1 { right: 8.33333333%; } .col-lg-pull-0 { right: auto; } .col-lg-push-12 { left: 100%; } .col-lg-push-11 { left: 91.66666667%; } .col-lg-push-10 { left: 83.33333333%; } .col-lg-push-9 { left: 75%; } .col-lg-push-8 { left: 66.66666667%; } .col-lg-push-7 { left: 58.33333333%; } .col-lg-push-6 { left: 50%; } .col-lg-push-5 { left: 41.66666667%; } .col-lg-push-4 { left: 33.33333333%; } .col-lg-push-3 { left: 25%; } .col-lg-push-2 { left: 16.66666667%; } .col-lg-push-1 { left: 8.33333333%; } .col-lg-push-0 { left: auto; } .col-lg-offset-12 { margin-left: 100%; } .col-lg-offset-11 { margin-left: 91.66666667%; } .col-lg-offset-10 { margin-left: 83.33333333%; } .col-lg-offset-9 { margin-left: 75%; } .col-lg-offset-8 { margin-left: 66.66666667%; } .col-lg-offset-7 { margin-left: 58.33333333%; } .col-lg-offset-6 { margin-left: 50%; } .col-lg-offset-5 { margin-left: 41.66666667%; } .col-lg-offset-4 { margin-left: 33.33333333%; } .col-lg-offset-3 { margin-left: 25%; } .col-lg-offset-2 { margin-left: 16.66666667%; } .col-lg-offset-1 { margin-left: 8.33333333%; } .col-lg-offset-0 { margin-left: 0; } } table { background-color: transparent; } th { text-align: left; } .table { width: 100%; max-width: 100%; margin-bottom: 20px; } .table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { padding: 8px; line-height: 1.42857143; vertical-align: top; border-top: 1px solid #ddd; } .table > thead > tr > th { vertical-align: bottom; border-bottom: 2px solid #ddd; } .table > caption + thead > tr:first-child > th, .table > colgroup + thead > tr:first-child > th, .table > thead:first-child > tr:first-child > th, .table > caption + thead > tr:first-child > td, .table > colgroup + thead > tr:first-child > td, .table > thead:first-child > tr:first-child > td { border-top: 0; } .table > tbody + tbody { border-top: 2px solid #ddd; } .table .table { background-color: #fff; } .table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { padding: 5px; } .table-bordered { border: 1px solid #ddd; } .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { border: 1px solid #ddd; } .table-bordered > thead > tr > th, .table-bordered > thead > tr > td { border-bottom-width: 2px; } .table-striped > tbody > tr:nth-child(odd) > td, .table-striped > tbody > tr:nth-child(odd) > th { background-color: #f9f9f9; } .table-hover > tbody > tr:hover > td, .table-hover > tbody > tr:hover > th { background-color: #f5f5f5; } table col[class*="col-"] { position: static; display: table-column; float: none; } table td[class*="col-"], table th[class*="col-"] { position: static; display: table-cell; float: none; } .table > thead > tr > td.active, .table > tbody > tr > td.active, .table > tfoot > tr > td.active, .table > thead > tr > th.active, .table > tbody > tr > th.active, .table > tfoot > tr > th.active, .table > thead > tr.active > td, .table > tbody > tr.active > td, .table > tfoot > tr.active > td, .table > thead > tr.active > th, .table > tbody > tr.active > th, .table > tfoot > tr.active > th { background-color: #f5f5f5; } .table-hover > tbody > tr > td.active:hover, .table-hover > tbody > tr > th.active:hover, .table-hover > tbody > tr.active:hover > td, .table-hover > tbody > tr:hover > .active, .table-hover > tbody > tr.active:hover > th { background-color: #e8e8e8; } .table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { background-color: #dff0d8; } .table-hover > tbody > tr > td.success:hover, .table-hover > tbody > tr > th.success:hover, .table-hover > tbody > tr.success:hover > td, .table-hover > tbody > tr:hover > .success, .table-hover > tbody > tr.success:hover > th { background-color: #d0e9c6; } .table > thead > tr > td.info, .table > tbody > tr > td.info, .table > tfoot > tr > td.info, .table > thead > tr > th.info, .table > tbody > tr > th.info, .table > tfoot > tr > th.info, .table > thead > tr.info > td, .table > tbody > tr.info > td, .table > tfoot > tr.info > td, .table > thead > tr.info > th, .table > tbody > tr.info > th, .table > tfoot > tr.info > th { background-color: #d9edf7; } .table-hover > tbody > tr > td.info:hover, .table-hover > tbody > tr > th.info:hover, .table-hover > tbody > tr.info:hover > td, .table-hover > tbody > tr:hover > .info, .table-hover > tbody > tr.info:hover > th { background-color: #c4e3f3; } .table > thead > tr > td.warning, .table > tbody > tr > td.warning, .table > tfoot > tr > td.warning, .table > thead > tr > th.warning, .table > tbody > tr > th.warning, .table > tfoot > tr > th.warning, .table > thead > tr.warning > td, .table > tbody > tr.warning > td, .table > tfoot > tr.warning > td, .table > thead > tr.warning > th, .table > tbody > tr.warning > th, .table > tfoot > tr.warning > th { background-color: #fcf8e3; } .table-hover > tbody > tr > td.warning:hover, .table-hover > tbody > tr > th.warning:hover, .table-hover > tbody > tr.warning:hover > td, .table-hover > tbody > tr:hover > .warning, .table-hover > tbody > tr.warning:hover > th { background-color: #faf2cc; } .table > thead > tr > td.danger, .table > tbody > tr > td.danger, .table > tfoot > tr > td.danger, .table > thead > tr > th.danger, .table > tbody > tr > th.danger, .table > tfoot > tr > th.danger, .table > thead > tr.danger > td, .table > tbody > tr.danger > td, .table > tfoot > tr.danger > td, .table > thead > tr.danger > th, .table > tbody > tr.danger > th, .table > tfoot > tr.danger > th { background-color: #f2dede; } .table-hover > tbody > tr > td.danger:hover, .table-hover > tbody > tr > th.danger:hover, .table-hover > tbody > tr.danger:hover > td, .table-hover > tbody > tr:hover > .danger, .table-hover > tbody > tr.danger:hover > th { background-color: #ebcccc; } @media screen and (max-width: 767px) { .table-responsive { width: 100%; margin-bottom: 15px; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; border: 1px solid #ddd; } .table-responsive > .table { margin-bottom: 0; } .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { white-space: nowrap; } .table-responsive > .table-bordered { border: 0; } .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { border-bottom: 0; } } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 20px; font-size: 21px; line-height: inherit; color: #333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; max-width: 100%; margin-bottom: 5px; font-weight: bold; } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; line-height: normal; } input[type="file"] { display: block; } input[type="range"] { display: block; width: 100%; } select[multiple], select[size] { height: auto; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } output { display: block; padding-top: 7px; font-size: 14px; line-height: 1.42857143; color: #555; } .form-control { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555; background-color: #fff; background-image: none; border: 1px solid #ccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } .form-control:focus { border-color: #66afe9; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); } .form-control::-moz-placeholder { color: #777; opacity: 1; } .form-control:-ms-input-placeholder { color: #777; } .form-control::-webkit-input-placeholder { color: #777; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { cursor: not-allowed; background-color: #eee; opacity: 1; } textarea.form-control { height: auto; } input[type="search"] { -webkit-appearance: none; } input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] { line-height: 34px; line-height: 1.42857143 \0; } input[type="date"].input-sm, input[type="time"].input-sm, input[type="datetime-local"].input-sm, input[type="month"].input-sm { line-height: 30px; } input[type="date"].input-lg, input[type="time"].input-lg, input[type="datetime-local"].input-lg, input[type="month"].input-lg { line-height: 46px; } .form-group { margin-bottom: 15px; } .radio, .checkbox { position: relative; display: block; min-height: 20px; margin-top: 10px; margin-bottom: 10px; } .radio label, .checkbox label { padding-left: 20px; margin-bottom: 0; font-weight: normal; cursor: pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { position: absolute; margin-top: 4px \9; margin-left: -20px; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; } .radio-inline, .checkbox-inline { display: inline-block; padding-left: 20px; margin-bottom: 0; font-weight: normal; vertical-align: middle; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; } input[type="radio"][disabled], input[type="checkbox"][disabled], input[type="radio"].disabled, input[type="checkbox"].disabled, fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"] { cursor: not-allowed; } .radio-inline.disabled, .checkbox-inline.disabled, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox-inline { cursor: not-allowed; } .radio.disabled label, .checkbox.disabled label, fieldset[disabled] .radio label, fieldset[disabled] .checkbox label { cursor: not-allowed; } .form-control-static { padding-top: 7px; padding-bottom: 7px; margin-bottom: 0; } .form-control-static.input-lg, .form-control-static.input-sm { padding-right: 0; padding-left: 0; } .input-sm, .form-horizontal .form-group-sm .form-control { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-sm { height: 30px; line-height: 30px; } textarea.input-sm, select[multiple].input-sm { height: auto; } .input-lg, .form-horizontal .form-group-lg .form-control { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } select.input-lg { height: 46px; line-height: 46px; } textarea.input-lg, select[multiple].input-lg { height: auto; } .has-feedback { position: relative; } .has-feedback .form-control { padding-right: 42.5px; } .form-control-feedback { position: absolute; top: 25px; right: 0; z-index: 2; display: block; width: 34px; height: 34px; line-height: 34px; text-align: center; } .input-lg + .form-control-feedback { width: 46px; height: 46px; line-height: 46px; } .input-sm + .form-control-feedback { width: 30px; height: 30px; line-height: 30px; } .has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-inline, .has-success .checkbox-inline { color: #3c763d; } .has-success .form-control { border-color: #3c763d; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); } .has-success .form-control:focus { border-color: #2b542c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; } .has-success .input-group-addon { color: #3c763d; background-color: #dff0d8; border-color: #3c763d; } .has-success .form-control-feedback { color: #3c763d; } .has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, .has-warning .checkbox-inline { color: #8a6d3b; } .has-warning .form-control { border-color: #8a6d3b; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); } .has-warning .form-control:focus { border-color: #66512c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; } .has-warning .input-group-addon { color: #8a6d3b; background-color: #fcf8e3; border-color: #8a6d3b; } .has-warning .form-control-feedback { color: #8a6d3b; } .has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-inline, .has-error .checkbox-inline { color: #a94442; } .has-error .form-control { border-color: #a94442; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); } .has-error .form-control:focus { border-color: #843534; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; } .has-error .input-group-addon { color: #a94442; background-color: #f2dede; border-color: #a94442; } .has-error .form-control-feedback { color: #a94442; } .has-feedback label.sr-only ~ .form-control-feedback { top: 0; } .help-block { display: block; margin-top: 5px; margin-bottom: 10px; color: #737373; } @media (min-width: 768px) { .form-inline .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .form-inline .form-control { display: inline-block; width: auto; vertical-align: middle; } .form-inline .input-group { display: inline-table; vertical-align: middle; } .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn, .form-inline .input-group .form-control { width: auto; } .form-inline .input-group > .form-control { width: 100%; } .form-inline .control-label { margin-bottom: 0; vertical-align: middle; } .form-inline .radio, .form-inline .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .form-inline .radio label, .form-inline .checkbox label { padding-left: 0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .form-inline .has-feedback .form-control-feedback { top: 0; } } .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { padding-top: 7px; margin-top: 0; margin-bottom: 0; } .form-horizontal .radio, .form-horizontal .checkbox { min-height: 27px; } .form-horizontal .form-group { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .form-horizontal .control-label { padding-top: 7px; margin-bottom: 0; text-align: right; } } .form-horizontal .has-feedback .form-control-feedback { top: 0; right: 15px; } @media (min-width: 768px) { .form-horizontal .form-group-lg .control-label { padding-top: 14.3px; } } @media (min-width: 768px) { .form-horizontal .form-group-sm .control-label { padding-top: 6px; } } .btn { display: inline-block; padding: 6px 12px; margin-bottom: 0; font-size: 14px; font-weight: normal; line-height: 1.42857143; text-align: center; white-space: nowrap; vertical-align: middle; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; background-image: none; border: 1px solid transparent; border-radius: 4px; } .btn:focus, .btn:active:focus, .btn.active:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn:hover, .btn:focus { color: #333; text-decoration: none; } .btn:active, .btn.active { background-image: none; outline: 0; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } .btn.disabled, .btn[disabled], fieldset[disabled] .btn { pointer-events: none; cursor: not-allowed; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none; opacity: .65; } .btn-default { color: #333; background-color: #fff; border-color: #ccc; } .btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { color: #333; background-color: #e6e6e6; border-color: #adadad; } .btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { background-image: none; } .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: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: #fff; border-color: #ccc; } .btn-default .badge { color: #fff; background-color: #333; } .btn-primary { color: #fff; background-color: #428bca; border-color: #357ebd; } .btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { color: #fff; background-color: #3071a9; border-color: #285e8e; } .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { background-image: none; } .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: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: #428bca; border-color: #357ebd; } .btn-primary .badge { color: #428bca; background-color: #fff; } .btn-success { color: #fff; background-color: #5cb85c; border-color: #4cae4c; } .btn-success:hover, .btn-success:focus, .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { color: #fff; background-color: #449d44; border-color: #398439; } .btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { background-image: none; } .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: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: #5cb85c; border-color: #4cae4c; } .btn-success .badge { color: #5cb85c; background-color: #fff; } .btn-info { color: #fff; background-color: #5bc0de; border-color: #46b8da; } .btn-info:hover, .btn-info:focus, .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { color: #fff; background-color: #31b0d5; border-color: #269abc; } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { background-image: none; } .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: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: #5bc0de; border-color: #46b8da; } .btn-info .badge { color: #5bc0de; background-color: #fff; } .btn-warning { color: #fff; background-color: #f0ad4e; border-color: #eea236; } .btn-warning:hover, .btn-warning:focus, .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { color: #fff; background-color: #ec971f; border-color: #d58512; } .btn-warning:active, .btn-warning.active, .open > .dropdown-toggle.btn-warning { background-image: none; } .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: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: #f0ad4e; border-color: #eea236; } .btn-warning .badge { color: #f0ad4e; background-color: #fff; } .btn-danger { color: #fff; background-color: #d9534f; border-color: #d43f3a; } .btn-danger:hover, .btn-danger:focus, .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { color: #fff; background-color: #c9302c; border-color: #ac2925; } .btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { background-image: none; } .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: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: #d9534f; border-color: #d43f3a; } .btn-danger .badge { color: #d9534f; background-color: #fff; } .btn-link { font-weight: normal; color: #428bca; cursor: pointer; border-radius: 0; } .btn-link, .btn-link:active, .btn-link[disabled], fieldset[disabled] .btn-link { background-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { border-color: transparent; } .btn-link:hover, .btn-link:focus { color: #2a6496; text-decoration: underline; background-color: transparent; } .btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { color: #777; text-decoration: none; } .btn-lg, .btn-group-lg > .btn { padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } .btn-sm, .btn-group-sm > .btn { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-xs, .btn-group-xs > .btn { padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-block { display: block; width: 100%; } .btn-block + .btn-block { margin-top: 5px; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .fade { opacity: 0; -webkit-transition: opacity .15s linear; -o-transition: opacity .15s linear; transition: opacity .15s linear; } .fade.in { opacity: 1; } .collapse { display: none; } .collapse.in { display: block; } tr.collapse.in { display: table-row; } tbody.collapse.in { display: table-row-group; } .collapsing { position: relative; height: 0; overflow: hidden; -webkit-transition: height .35s ease; -o-transition: height .35s ease; transition: height .35s ease; } .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: 4px solid; border-right: 4px solid transparent; border-left: 4px solid transparent; } .dropdown { position: relative; } .dropdown-toggle:focus { outline: 0; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; font-size: 14px; text-align: left; list-style: none; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, .15); border-radius: 4px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); box-shadow: 0 6px 12px rgba(0, 0, 0, .175); } .dropdown-menu.pull-right { right: 0; left: auto; } .dropdown-menu .divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .dropdown-menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 1.42857143; color: #333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { color: #262626; text-decoration: none; background-color: #f5f5f5; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { color: #fff; text-decoration: none; background-color: #428bca; outline: 0; } .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { color: #777; } .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { text-decoration: none; cursor: not-allowed; background-color: transparent; background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); } .open > .dropdown-menu { display: block; } .open > a { outline: 0; } .dropdown-menu-right { right: 0; left: auto; } .dropdown-menu-left { right: auto; left: 0; } .dropdown-header { display: block; padding: 3px 20px; font-size: 12px; line-height: 1.42857143; color: #777; white-space: nowrap; } .dropdown-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 990; } .pull-right > .dropdown-menu { right: 0; left: auto; } .dropup .caret, .navbar-fixed-bottom .dropdown .caret { content: ""; border-top: 0; border-bottom: 4px solid; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 1px; } @media (min-width: 768px) { .navbar-right .dropdown-menu { right: 0; left: auto; } .navbar-right .dropdown-menu-left { right: auto; left: 0; } } .btn-group, .btn-group-vertical { position: relative; display: inline-block; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; float: left; } .btn-group > .btn:hover, .btn-group-vertical > .btn:hover, .btn-group > .btn:focus, .btn-group-vertical > .btn:focus, .btn-group > .btn:active, .btn-group-vertical > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn.active { z-index: 2; } .btn-group > .btn:focus, .btn-group-vertical > .btn:focus { outline: 0; } .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { margin-left: -1px; } .btn-toolbar { margin-left: -5px; } .btn-toolbar .btn-group, .btn-toolbar .input-group { float: left; } .btn-toolbar > .btn, .btn-toolbar > .btn-group, .btn-toolbar > .input-group { margin-left: 5px; } .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { border-radius: 0; } .btn-group > .btn:first-child { margin-left: 0; } .btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; } .btn-group > .btn-group { float: left; } .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group > .btn-group:first-child > .btn:last-child, .btn-group > .btn-group:first-child > .dropdown-toggle { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn-group:last-child > .btn:first-child { border-top-left-radius: 0; border-bottom-left-radius: 0; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .btn-group > .btn + .dropdown-toggle { padding-right: 8px; padding-left: 8px; } .btn-group > .btn-lg + .dropdown-toggle { padding-right: 12px; padding-left: 12px; } .btn-group.open .dropdown-toggle { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } .btn-group.open .dropdown-toggle.btn-link { -webkit-box-shadow: none; box-shadow: none; } .btn .caret { margin-left: 0; } .btn-lg .caret { border-width: 5px 5px 0; border-bottom-width: 0; } .dropup .btn-lg .caret { border-width: 0 5px 5px; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group, .btn-group-vertical > .btn-group > .btn { display: block; float: none; width: 100%; max-width: 100%; } .btn-group-vertical > .btn-group > .btn { float: none; } .btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { margin-top: -1px; margin-left: 0; } .btn-group-vertical > .btn:not(:first-child):not(:last-child) { border-radius: 0; } .btn-group-vertical > .btn:first-child:not(:last-child) { border-top-right-radius: 4px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn:last-child:not(:first-child) { border-top-left-radius: 0; border-top-right-radius: 0; border-bottom-left-radius: 4px; } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { border-top-left-radius: 0; border-top-right-radius: 0; } .btn-group-justified { display: table; width: 100%; table-layout: fixed; border-collapse: separate; } .btn-group-justified > .btn, .btn-group-justified > .btn-group { display: table-cell; float: none; width: 1%; } .btn-group-justified > .btn-group .btn { width: 100%; } .btn-group-justified > .btn-group .dropdown-menu { left: auto; } [data-toggle="buttons"] > .btn > input[type="radio"], [data-toggle="buttons"] > .btn > input[type="checkbox"] { position: absolute; z-index: -1; filter: alpha(opacity=0); opacity: 0; } .input-group { position: relative; display: table; border-collapse: separate; } .input-group[class*="col-"] { float: none; padding-right: 0; padding-left: 0; } .input-group .form-control { position: relative; z-index: 2; float: left; width: 100%; margin-bottom: 0; } .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { height: 46px; line-height: 46px; } textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn, select[multiple].input-group-lg > .form-control, select[multiple].input-group-lg > .input-group-addon, select[multiple].input-group-lg > .input-group-btn > .btn { height: auto; } .input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { height: 30px; line-height: 30px; } textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn, select[multiple].input-group-sm > .form-control, select[multiple].input-group-sm > .input-group-addon, select[multiple].input-group-sm > .input-group-btn > .btn { height: auto; } .input-group-addon, .input-group-btn, .input-group .form-control { display: table-cell; } .input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child), .input-group .form-control:not(:first-child):not(:last-child) { border-radius: 0; } .input-group-addon, .input-group-btn { width: 1%; white-space: nowrap; vertical-align: middle; } .input-group-addon { padding: 6px 12px; font-size: 14px; font-weight: normal; line-height: 1; color: #555; text-align: center; background-color: #eee; border: 1px solid #ccc; border-radius: 4px; } .input-group-addon.input-sm { padding: 5px 10px; font-size: 12px; border-radius: 3px; } .input-group-addon.input-lg { padding: 10px 16px; font-size: 18px; border-radius: 6px; } .input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] { margin-top: 0; } .input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group-addon:first-child { border-right: 0; } .input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child), .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; } .input-group-addon:last-child { border-left: 0; } .input-group-btn { position: relative; font-size: 0; white-space: nowrap; } .input-group-btn > .btn { position: relative; } .input-group-btn > .btn + .btn { margin-left: -1px; } .input-group-btn > .btn:hover, .input-group-btn > .btn:focus, .input-group-btn > .btn:active { z-index: 2; } .input-group-btn:first-child > .btn, .input-group-btn:first-child > .btn-group { margin-right: -1px; } .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { margin-left: -1px; } .nav { padding-left: 0; margin-bottom: 0; list-style: none; } .nav > li { position: relative; display: block; } .nav > li > a { position: relative; display: block; padding: 10px 15px; } .nav > li > a:hover, .nav > li > a:focus { text-decoration: none; background-color: #eee; } .nav > li.disabled > a { color: #777; } .nav > li.disabled > a:hover, .nav > li.disabled > a:focus { color: #777; text-decoration: none; cursor: not-allowed; background-color: transparent; } .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background-color: #eee; border-color: #428bca; } .nav .nav-divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .nav > li > a > img { max-width: none; } .nav-tabs { border-bottom: 1px solid #ddd; } .nav-tabs > li { float: left; margin-bottom: -1px; } .nav-tabs > li > a { margin-right: 2px; line-height: 1.42857143; border: 1px solid transparent; border-radius: 4px 4px 0 0; } .nav-tabs > li > a:hover { border-color: #eee #eee #ddd; } .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { color: #555; cursor: default; background-color: #fff; border: 1px solid #ddd; border-bottom-color: transparent; } .nav-tabs.nav-justified { width: 100%; border-bottom: 0; } .nav-tabs.nav-justified > li { float: none; } .nav-tabs.nav-justified > li > a { margin-bottom: 5px; text-align: center; } .nav-tabs.nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-tabs.nav-justified > li { display: table-cell; width: 1%; } .nav-tabs.nav-justified > li > a { margin-bottom: 0; } } .nav-tabs.nav-justified > li > a { margin-right: 0; border-radius: 4px; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border: 1px solid #ddd; } @media (min-width: 768px) { .nav-tabs.nav-justified > li > a { border-bottom: 1px solid #ddd; border-radius: 4px 4px 0 0; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border-bottom-color: #fff; } } .nav-pills > li { float: left; } .nav-pills > li > a { border-radius: 4px; } .nav-pills > li + li { margin-left: 2px; } .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { color: #fff; background-color: #428bca; } .nav-stacked > li { float: none; } .nav-stacked > li + li { margin-top: 2px; margin-left: 0; } .nav-justified { width: 100%; } .nav-justified > li { float: none; } .nav-justified > li > a { margin-bottom: 5px; text-align: center; } .nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-justified > li { display: table-cell; width: 1%; } .nav-justified > li > a { margin-bottom: 0; } } .nav-tabs-justified { border-bottom: 0; } .nav-tabs-justified > li > a { margin-right: 0; border-radius: 4px; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border: 1px solid #ddd; } @media (min-width: 768px) { .nav-tabs-justified > li > a { border-bottom: 1px solid #ddd; border-radius: 4px 4px 0 0; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border-bottom-color: #fff; } } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-left-radius: 0; border-top-right-radius: 0; } .navbar { position: relative; min-height: 50px; margin-bottom: 20px; border: 1px solid transparent; } @media (min-width: 768px) { .navbar { border-radius: 4px; } } @media (min-width: 768px) { .navbar-header { float: left; } } .navbar-collapse { padding-right: 15px; padding-left: 15px; overflow-x: visible; -webkit-overflow-scrolling: touch; border-top: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); } .navbar-collapse.in { overflow-y: auto; } @media (min-width: 768px) { .navbar-collapse { width: auto; border-top: 0; -webkit-box-shadow: none; box-shadow: none; } .navbar-collapse.collapse { display: block !important; height: auto !important; padding-bottom: 0; overflow: visible !important; } .navbar-collapse.in { overflow-y: visible; } .navbar-fixed-top .navbar-collapse, .navbar-static-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { padding-right: 0; padding-left: 0; } } .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 340px; } @media (max-width: 480px) and (orientation: landscape) { .navbar-fixed-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { max-height: 200px; } } .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .container > .navbar-header, .container-fluid > .navbar-header, .container > .navbar-collapse, .container-fluid > .navbar-collapse { margin-right: 0; margin-left: 0; } } .navbar-static-top { z-index: 1000; border-width: 0 0 1px; } @media (min-width: 768px) { .navbar-static-top { border-radius: 0; } } .navbar-fixed-top, .navbar-fixed-bottom { position: fixed; right: 0; left: 0; z-index: 1030; -webkit-transform: translate3d(0, 0, 0); -o-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } @media (min-width: 768px) { .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } } .navbar-fixed-top { top: 0; border-width: 0 0 1px; } .navbar-fixed-bottom { bottom: 0; margin-bottom: 0; border-width: 1px 0 0; } .navbar-brand { float: left; height: 50px; padding: 15px 15px; font-size: 18px; line-height: 20px; } .navbar-brand:hover, .navbar-brand:focus { text-decoration: none; } @media (min-width: 768px) { .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { margin-left: -15px; } } .navbar-toggle { position: relative; float: right; padding: 9px 10px; margin-top: 8px; margin-right: 15px; margin-bottom: 8px; background-color: transparent; background-image: none; border: 1px solid transparent; border-radius: 4px; } .navbar-toggle:focus { outline: 0; } .navbar-toggle .icon-bar { display: block; width: 22px; height: 2px; border-radius: 1px; } .navbar-toggle .icon-bar + .icon-bar { margin-top: 4px; } @media (min-width: 768px) { .navbar-toggle { display: none; } } .navbar-nav { margin: 7.5px -15px; } .navbar-nav > li > a { padding-top: 10px; padding-bottom: 10px; line-height: 20px; } @media (max-width: 767px) { .navbar-nav .open .dropdown-menu { position: static; float: none; width: auto; margin-top: 0; background-color: transparent; border: 0; -webkit-box-shadow: none; box-shadow: none; } .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { padding: 5px 15px 5px 25px; } .navbar-nav .open .dropdown-menu > li > a { line-height: 20px; } .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-nav .open .dropdown-menu > li > a:focus { background-image: none; } } @media (min-width: 768px) { .navbar-nav { float: left; margin: 0; } .navbar-nav > li { float: left; } .navbar-nav > li > a { padding-top: 15px; padding-bottom: 15px; } .navbar-nav.navbar-right:last-child { margin-right: -15px; } } @media (min-width: 768px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; } } .navbar-form { padding: 10px 15px; margin-top: 8px; margin-right: -15px; margin-bottom: 8px; margin-left: -15px; border-top: 1px solid transparent; border-bottom: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); } @media (min-width: 768px) { .navbar-form .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .navbar-form .form-control { display: inline-block; width: auto; vertical-align: middle; } .navbar-form .input-group { display: inline-table; vertical-align: middle; } .navbar-form .input-group .input-group-addon, .navbar-form .input-group .input-group-btn, .navbar-form .input-group .form-control { width: auto; } .navbar-form .input-group > .form-control { width: 100%; } .navbar-form .control-label { margin-bottom: 0; vertical-align: middle; } .navbar-form .radio, .navbar-form .checkbox { display: inline-block; margin-top: 0; margin-bottom: 0; vertical-align: middle; } .navbar-form .radio label, .navbar-form .checkbox label { padding-left: 0; } .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] { position: relative; margin-left: 0; } .navbar-form .has-feedback .form-control-feedback { top: 0; } } @media (max-width: 767px) { .navbar-form .form-group { margin-bottom: 5px; } } @media (min-width: 768px) { .navbar-form { width: auto; padding-top: 0; padding-bottom: 0; margin-right: 0; margin-left: 0; border: 0; -webkit-box-shadow: none; box-shadow: none; } .navbar-form.navbar-right:last-child { margin-right: -15px; } } .navbar-nav > li > .dropdown-menu { margin-top: 0; border-top-left-radius: 0; border-top-right-radius: 0; } .navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .navbar-btn { margin-top: 8px; margin-bottom: 8px; } .navbar-btn.btn-sm { margin-top: 10px; margin-bottom: 10px; } .navbar-btn.btn-xs { margin-top: 14px; margin-bottom: 14px; } .navbar-text { margin-top: 15px; margin-bottom: 15px; } @media (min-width: 768px) { .navbar-text { float: left; margin-right: 15px; margin-left: 15px; } .navbar-text.navbar-right:last-child { margin-right: 0; } } .navbar-default { background-color: #f8f8f8; border-color: #e7e7e7; } .navbar-default .navbar-brand { color: #777; } .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { color: #5e5e5e; background-color: transparent; } .navbar-default .navbar-text { color: #777; } .navbar-default .navbar-nav > li > a { color: #777; } .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { color: #333; background-color: transparent; } .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { color: #555; background-color: #e7e7e7; } .navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:hover, .navbar-default .navbar-nav > .disabled > a:focus { color: #ccc; background-color: transparent; } .navbar-default .navbar-toggle { border-color: #ddd; } .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { background-color: #ddd; } .navbar-default .navbar-toggle .icon-bar { background-color: #888; } .navbar-default .navbar-collapse, .navbar-default .navbar-form { border-color: #e7e7e7; } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { color: #555; background-color: #e7e7e7; } @media (max-width: 767px) { .navbar-default .navbar-nav .open .dropdown-menu > li > a { color: #777; } .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { color: #333; background-color: transparent; } .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { color: #555; background-color: #e7e7e7; } .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #ccc; background-color: transparent; } } .navbar-default .navbar-link { color: #777; } .navbar-default .navbar-link:hover { color: #333; } .navbar-default .btn-link { color: #777; } .navbar-default .btn-link:hover, .navbar-default .btn-link:focus { color: #333; } .navbar-default .btn-link[disabled]:hover, fieldset[disabled] .navbar-default .btn-link:hover, .navbar-default .btn-link[disabled]:focus, fieldset[disabled] .navbar-default .btn-link:focus { color: #ccc; } .navbar-inverse { background-color: #222; border-color: #080808; } .navbar-inverse .navbar-brand { color: #777; } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { color: #fff; background-color: transparent; } .navbar-inverse .navbar-text { color: #777; } .navbar-inverse .navbar-nav > li > a { color: #777; } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { color: #fff; background-color: transparent; } .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { color: #fff; background-color: #080808; } .navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:hover, .navbar-inverse .navbar-nav > .disabled > a:focus { color: #444; background-color: transparent; } .navbar-inverse .navbar-toggle { border-color: #333; } .navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { background-color: #333; } .navbar-inverse .navbar-toggle .icon-bar { background-color: #fff; } .navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { border-color: #101010; } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:hover, .navbar-inverse .navbar-nav > .open > a:focus { color: #fff; background-color: #080808; } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { border-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu .divider { background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { color: #777; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { color: #fff; background-color: transparent; } .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { color: #fff; background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #444; background-color: transparent; } } .navbar-inverse .navbar-link { color: #777; } .navbar-inverse .navbar-link:hover { color: #fff; } .navbar-inverse .btn-link { color: #777; } .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link:focus { color: #fff; } .navbar-inverse .btn-link[disabled]:hover, fieldset[disabled] .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link[disabled]:focus, fieldset[disabled] .navbar-inverse .btn-link:focus { color: #444; } .breadcrumb { padding: 8px 15px; margin-bottom: 20px; list-style: none; background-color: #f5f5f5; border-radius: 4px; } .breadcrumb > li { display: inline-block; } .breadcrumb > li + li:before { padding: 0 5px; color: #ccc; content: "/\00a0"; } .breadcrumb > .active { color: #777; } .pagination { display: inline-block; padding-left: 0; margin: 20px 0; border-radius: 4px; } .pagination > li { display: inline; } .pagination > li > a, .pagination > li > span { position: relative; float: left; padding: 6px 12px; margin-left: -1px; line-height: 1.42857143; color: #428bca; text-decoration: none; background-color: #fff; border: 1px solid #ddd; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-top-left-radius: 4px; border-bottom-left-radius: 4px; } .pagination > li:last-child > a, .pagination > li:last-child > span { border-top-right-radius: 4px; border-bottom-right-radius: 4px; } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { color: #2a6496; background-color: #eee; border-color: #ddd; } .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 2; color: #fff; cursor: default; background-color: #428bca; border-color: #428bca; } .pagination > .disabled > span, .pagination > .disabled > span:hover, .pagination > .disabled > span:focus, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #777; cursor: not-allowed; background-color: #fff; border-color: #ddd; } .pagination-lg > li > a, .pagination-lg > li > span { padding: 10px 16px; font-size: 18px; } .pagination-lg > li:first-child > a, .pagination-lg > li:first-child > span { border-top-left-radius: 6px; border-bottom-left-radius: 6px; } .pagination-lg > li:last-child > a, .pagination-lg > li:last-child > span { border-top-right-radius: 6px; border-bottom-right-radius: 6px; } .pagination-sm > li > a, .pagination-sm > li > span { padding: 5px 10px; font-size: 12px; } .pagination-sm > li:first-child > a, .pagination-sm > li:first-child > span { border-top-left-radius: 3px; border-bottom-left-radius: 3px; } .pagination-sm > li:last-child > a, .pagination-sm > li:last-child > span { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } .pager { padding-left: 0; margin: 20px 0; text-align: center; list-style: none; } .pager li { display: inline; } .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; background-color: #fff; border: 1px solid #ddd; border-radius: 15px; } .pager li > a:hover, .pager li > a:focus { text-decoration: none; background-color: #eee; } .pager .next > a, .pager .next > span { float: right; } .pager .previous > a, .pager .previous > span { float: left; } .pager .disabled > a, .pager .disabled > a:hover, .pager .disabled > a:focus, .pager .disabled > span { color: #777; cursor: not-allowed; background-color: #fff; } .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } a.label:hover, a.label:focus { color: #fff; text-decoration: none; cursor: pointer; } .label:empty { display: none; } .btn .label { position: relative; top: -1px; } .label-default { background-color: #777; } .label-default[href]:hover, .label-default[href]:focus { background-color: #5e5e5e; } .label-primary { background-color: #428bca; } .label-primary[href]:hover, .label-primary[href]:focus { background-color: #3071a9; } .label-success { background-color: #5cb85c; } .label-success[href]:hover, .label-success[href]:focus { background-color: #449d44; } .label-info { background-color: #5bc0de; } .label-info[href]:hover, .label-info[href]:focus { background-color: #31b0d5; } .label-warning { background-color: #f0ad4e; } .label-warning[href]:hover, .label-warning[href]:focus { background-color: #ec971f; } .label-danger { background-color: #d9534f; } .label-danger[href]:hover, .label-danger[href]:focus { background-color: #c9302c; } .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 12px; font-weight: bold; line-height: 1; color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; background-color: #777; border-radius: 10px; } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } .btn-xs .badge { top: 0; padding: 1px 5px; } a.badge:hover, a.badge:focus { color: #fff; text-decoration: none; cursor: pointer; } a.list-group-item.active > .badge, .nav-pills > .active > a > .badge { color: #428bca; background-color: #fff; } .nav-pills > li > a > .badge { margin-left: 3px; } .jumbotron { padding: 30px; margin-bottom: 30px; color: inherit; background-color: #eee; } .jumbotron h1, .jumbotron .h1 { color: inherit; } .jumbotron p { margin-bottom: 15px; font-size: 21px; font-weight: 200; } .jumbotron > hr { border-top-color: #d5d5d5; } .container .jumbotron { border-radius: 6px; } .jumbotron .container { max-width: 100%; } @media screen and (min-width: 768px) { .jumbotron { padding-top: 48px; padding-bottom: 48px; } .container .jumbotron { padding-right: 60px; padding-left: 60px; } .jumbotron h1, .jumbotron .h1 { font-size: 63px; } } .thumbnail { display: block; padding: 4px; margin-bottom: 20px; line-height: 1.42857143; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; -webkit-transition: all .2s ease-in-out; -o-transition: all .2s ease-in-out; transition: all .2s ease-in-out; } .thumbnail > img, .thumbnail a > img { margin-right: auto; margin-left: auto; } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { border-color: #428bca; } .thumbnail .caption { padding: 9px; color: #333; } .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; } .alert h4 { margin-top: 0; color: inherit; } .alert .alert-link { font-weight: bold; } .alert > p, .alert > ul { margin-bottom: 0; } .alert > p + p { margin-top: 5px; } .alert-dismissable, .alert-dismissible { padding-right: 35px; } .alert-dismissable .close, .alert-dismissible .close { position: relative; top: -2px; right: -21px; color: inherit; } .alert-success { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .alert-success hr { border-top-color: #c9e2b3; } .alert-success .alert-link { color: #2b542c; } .alert-info { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .alert-info hr { border-top-color: #a6e1ec; } .alert-info .alert-link { color: #245269; } .alert-warning { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .alert-warning hr { border-top-color: #f7e1b5; } .alert-warning .alert-link { color: #66512c; } .alert-danger { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .alert-danger hr { border-top-color: #e4b9c0; } .alert-danger .alert-link { color: #843534; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @-o-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } .progress { height: 20px; margin-bottom: 20px; overflow: hidden; background-color: #f5f5f5; border-radius: 4px; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); } .progress-bar { float: left; width: 0; height: 100%; font-size: 12px; line-height: 20px; color: #fff; text-align: center; background-color: #428bca; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); -webkit-transition: width .6s ease; -o-transition: width .6s ease; transition: width .6s ease; } .progress-striped .progress-bar, .progress-bar-striped { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -webkit-background-size: 40px 40px; background-size: 40px 40px; } .progress.active .progress-bar, .progress-bar.active { -webkit-animation: progress-bar-stripes 2s linear infinite; -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } .progress-bar[aria-valuenow="1"], .progress-bar[aria-valuenow="2"] { min-width: 30px; } .progress-bar[aria-valuenow="0"] { min-width: 30px; color: #777; background-color: transparent; background-image: none; -webkit-box-shadow: none; box-shadow: none; } .progress-bar-success { background-color: #5cb85c; } .progress-striped .progress-bar-success { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .progress-bar-info { background-color: #5bc0de; } .progress-striped .progress-bar-info { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .progress-bar-warning { background-color: #f0ad4e; } .progress-striped .progress-bar-warning { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .progress-bar-danger { background-color: #d9534f; } .progress-striped .progress-bar-danger { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); } .media, .media-body { overflow: hidden; zoom: 1; } .media, .media .media { margin-top: 15px; } .media:first-child { margin-top: 0; } .media-object { display: block; } .media-heading { margin: 0 0 5px; } .media > .pull-left { margin-right: 10px; } .media > .pull-right { margin-left: 10px; } .media-list { padding-left: 0; list-style: none; } .list-group { padding-left: 0; margin-bottom: 20px; } .list-group-item { position: relative; display: block; padding: 10px 15px; margin-bottom: -1px; background-color: #fff; border: 1px solid #ddd; } .list-group-item:first-child { border-top-left-radius: 4px; border-top-right-radius: 4px; } .list-group-item:last-child { margin-bottom: 0; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } .list-group-item > .badge { float: right; } .list-group-item > .badge + .badge { margin-right: 5px; } a.list-group-item { color: #555; } a.list-group-item .list-group-item-heading { color: #333; } a.list-group-item:hover, a.list-group-item:focus { color: #555; text-decoration: none; background-color: #f5f5f5; } .list-group-item.disabled, .list-group-item.disabled:hover, .list-group-item.disabled:focus { color: #777; background-color: #eee; } .list-group-item.disabled .list-group-item-heading, .list-group-item.disabled:hover .list-group-item-heading, .list-group-item.disabled:focus .list-group-item-heading { color: inherit; } .list-group-item.disabled .list-group-item-text, .list-group-item.disabled:hover .list-group-item-text, .list-group-item.disabled:focus .list-group-item-text { color: #777; } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { z-index: 2; color: #fff; background-color: #428bca; border-color: #428bca; } .list-group-item.active .list-group-item-heading, .list-group-item.active:hover .list-group-item-heading, .list-group-item.active:focus .list-group-item-heading, .list-group-item.active .list-group-item-heading > small, .list-group-item.active:hover .list-group-item-heading > small, .list-group-item.active:focus .list-group-item-heading > small, .list-group-item.active .list-group-item-heading > .small, .list-group-item.active:hover .list-group-item-heading > .small, .list-group-item.active:focus .list-group-item-heading > .small { color: inherit; } .list-group-item.active .list-group-item-text, .list-group-item.active:hover .list-group-item-text, .list-group-item.active:focus .list-group-item-text { color: #e1edf7; } .list-group-item-success { color: #3c763d; background-color: #dff0d8; } a.list-group-item-success { color: #3c763d; } a.list-group-item-success .list-group-item-heading { color: inherit; } a.list-group-item-success:hover, a.list-group-item-success:focus { color: #3c763d; background-color: #d0e9c6; } a.list-group-item-success.active, a.list-group-item-success.active:hover, a.list-group-item-success.active:focus { color: #fff; background-color: #3c763d; border-color: #3c763d; } .list-group-item-info { color: #31708f; background-color: #d9edf7; } a.list-group-item-info { color: #31708f; } a.list-group-item-info .list-group-item-heading { color: inherit; } a.list-group-item-info:hover, a.list-group-item-info:focus { color: #31708f; background-color: #c4e3f3; } a.list-group-item-info.active, a.list-group-item-info.active:hover, a.list-group-item-info.active:focus { color: #fff; background-color: #31708f; border-color: #31708f; } .list-group-item-warning { color: #8a6d3b; background-color: #fcf8e3; } a.list-group-item-warning { color: #8a6d3b; } a.list-group-item-warning .list-group-item-heading { color: inherit; } a.list-group-item-warning:hover, a.list-group-item-warning:focus { color: #8a6d3b; background-color: #faf2cc; } a.list-group-item-warning.active, a.list-group-item-warning.active:hover, a.list-group-item-warning.active:focus { color: #fff; background-color: #8a6d3b; border-color: #8a6d3b; } .list-group-item-danger { color: #a94442; background-color: #f2dede; } a.list-group-item-danger { color: #a94442; } a.list-group-item-danger .list-group-item-heading { color: inherit; } a.list-group-item-danger:hover, a.list-group-item-danger:focus { color: #a94442; background-color: #ebcccc; } a.list-group-item-danger.active, a.list-group-item-danger.active:hover, a.list-group-item-danger.active:focus { color: #fff; background-color: #a94442; border-color: #a94442; } .list-group-item-heading { margin-top: 0; margin-bottom: 5px; } .list-group-item-text { margin-bottom: 0; line-height: 1.3; } .panel { margin-bottom: 20px; background-color: #fff; border: 1px solid transparent; border-radius: 4px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); box-shadow: 0 1px 1px rgba(0, 0, 0, .05); } .panel-body { padding: 15px; } .panel-heading { padding: 10px 15px; border-bottom: 1px solid transparent; border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel-heading > .dropdown .dropdown-toggle { color: inherit; } .panel-title { margin-top: 0; margin-bottom: 0; font-size: 16px; color: inherit; } .panel-title > a { color: inherit; } .panel-footer { padding: 10px 15px; background-color: #f5f5f5; border-top: 1px solid #ddd; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .list-group { margin-bottom: 0; } .panel > .list-group .list-group-item { border-width: 1px 0; border-radius: 0; } .panel > .list-group:first-child .list-group-item:first-child { border-top: 0; border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel > .list-group:last-child .list-group-item:last-child { border-bottom: 0; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel-heading + .list-group .list-group-item:first-child { border-top-width: 0; } .list-group + .panel-footer { border-top-width: 0; } .panel > .table, .panel > .table-responsive > .table, .panel > .panel-collapse > .table { margin-bottom: 0; } .panel > .table:first-child, .panel > .table-responsive:first-child > .table:first-child { border-top-left-radius: 3px; border-top-right-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, .panel > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { border-top-left-radius: 3px; } .panel > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, .panel > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, .panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, .panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { border-top-right-radius: 3px; } .panel > .table:last-child, .panel > .table-responsive:last-child > .table:last-child { border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { border-bottom-left-radius: 3px; } .panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, .panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, .panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, .panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { border-bottom-right-radius: 3px; } .panel > .panel-body + .table, .panel > .panel-body + .table-responsive { border-top: 1px solid #ddd; } .panel > .table > tbody:first-child > tr:first-child th, .panel > .table > tbody:first-child > tr:first-child td { border-top: 0; } .panel > .table-bordered, .panel > .table-responsive > .table-bordered { border: 0; } .panel > .table-bordered > thead > tr > th:first-child, .panel > .table-responsive > .table-bordered > thead > tr > th:first-child, .panel > .table-bordered > tbody > tr > th:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, .panel > .table-bordered > tfoot > tr > th:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, .panel > .table-bordered > thead > tr > td:first-child, .panel > .table-responsive > .table-bordered > thead > tr > td:first-child, .panel > .table-bordered > tbody > tr > td:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, .panel > .table-bordered > tfoot > tr > td:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .panel > .table-bordered > thead > tr > th:last-child, .panel > .table-responsive > .table-bordered > thead > tr > th:last-child, .panel > .table-bordered > tbody > tr > th:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, .panel > .table-bordered > tfoot > tr > th:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, .panel > .table-bordered > thead > tr > td:last-child, .panel > .table-responsive > .table-bordered > thead > tr > td:last-child, .panel > .table-bordered > tbody > tr > td:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, .panel > .table-bordered > tfoot > tr > td:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .panel > .table-bordered > thead > tr:first-child > td, .panel > .table-responsive > .table-bordered > thead > tr:first-child > td, .panel > .table-bordered > tbody > tr:first-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, .panel > .table-bordered > thead > tr:first-child > th, .panel > .table-responsive > .table-bordered > thead > tr:first-child > th, .panel > .table-bordered > tbody > tr:first-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { border-bottom: 0; } .panel > .table-bordered > tbody > tr:last-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, .panel > .table-bordered > tfoot > tr:last-child > td, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, .panel > .table-bordered > tbody > tr:last-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, .panel > .table-bordered > tfoot > tr:last-child > th, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { border-bottom: 0; } .panel > .table-responsive { margin-bottom: 0; border: 0; } .panel-group { margin-bottom: 20px; } .panel-group .panel { margin-bottom: 0; border-radius: 4px; } .panel-group .panel + .panel { margin-top: 5px; } .panel-group .panel-heading { border-bottom: 0; } .panel-group .panel-heading + .panel-collapse > .panel-body { border-top: 1px solid #ddd; } .panel-group .panel-footer { border-top: 0; } .panel-group .panel-footer + .panel-collapse .panel-body { border-bottom: 1px solid #ddd; } .panel-default { border-color: #ddd; } .panel-default > .panel-heading { color: #333; background-color: #f5f5f5; border-color: #ddd; } .panel-default > .panel-heading + .panel-collapse > .panel-body { border-top-color: #ddd; } .panel-default > .panel-heading .badge { color: #f5f5f5; background-color: #333; } .panel-default > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #ddd; } .panel-primary { border-color: #428bca; } .panel-primary > .panel-heading { color: #fff; background-color: #428bca; border-color: #428bca; } .panel-primary > .panel-heading + .panel-collapse > .panel-body { border-top-color: #428bca; } .panel-primary > .panel-heading .badge { color: #428bca; background-color: #fff; } .panel-primary > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #428bca; } .panel-success { border-color: #d6e9c6; } .panel-success > .panel-heading { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .panel-success > .panel-heading + .panel-collapse > .panel-body { border-top-color: #d6e9c6; } .panel-success > .panel-heading .badge { color: #dff0d8; background-color: #3c763d; } .panel-success > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #d6e9c6; } .panel-info { border-color: #bce8f1; } .panel-info > .panel-heading { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .panel-info > .panel-heading + .panel-collapse > .panel-body { border-top-color: #bce8f1; } .panel-info > .panel-heading .badge { color: #d9edf7; background-color: #31708f; } .panel-info > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #bce8f1; } .panel-warning { border-color: #faebcc; } .panel-warning > .panel-heading { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .panel-warning > .panel-heading + .panel-collapse > .panel-body { border-top-color: #faebcc; } .panel-warning > .panel-heading .badge { color: #fcf8e3; background-color: #8a6d3b; } .panel-warning > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #faebcc; } .panel-danger { border-color: #ebccd1; } .panel-danger > .panel-heading { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .panel-danger > .panel-heading + .panel-collapse > .panel-body { border-top-color: #ebccd1; } .panel-danger > .panel-heading .badge { color: #f2dede; background-color: #a94442; } .panel-danger > .panel-footer + .panel-collapse > .panel-body { border-bottom-color: #ebccd1; } .embed-responsive { position: relative; display: block; height: 0; padding: 0; overflow: hidden; } .embed-responsive .embed-responsive-item, .embed-responsive iframe, .embed-responsive embed, .embed-responsive object { position: absolute; top: 0; bottom: 0; left: 0; width: 100%; height: 100%; border: 0; } .embed-responsive.embed-responsive-16by9 { padding-bottom: 56.25%; } .embed-responsive.embed-responsive-4by3 { padding-bottom: 75%; } .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); } .well blockquote { border-color: #ddd; border-color: rgba(0, 0, 0, .15); } .well-lg { padding: 24px; border-radius: 6px; } .well-sm { padding: 9px; border-radius: 3px; } .close { float: right; font-size: 21px; font-weight: bold; line-height: 1; color: #000; text-shadow: 0 1px 0 #fff; filter: alpha(opacity=20); opacity: .2; } .close:hover, .close:focus { color: #000; text-decoration: none; cursor: pointer; filter: alpha(opacity=50); opacity: .5; } button.close { -webkit-appearance: none; padding: 0; cursor: pointer; background: transparent; border: 0; } .modal-open { overflow: hidden; } .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1050; display: none; overflow: hidden; -webkit-overflow-scrolling: touch; outline: 0; } .modal.fade .modal-dialog { -webkit-transition: -webkit-transform .3s ease-out; -o-transition: -o-transform .3s ease-out; transition: transform .3s ease-out; -webkit-transform: translate3d(0, -25%, 0); -o-transform: translate3d(0, -25%, 0); transform: translate3d(0, -25%, 0); } .modal.in .modal-dialog { -webkit-transform: translate3d(0, 0, 0); -o-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } .modal-open .modal { overflow-x: hidden; overflow-y: auto; } .modal-dialog { position: relative; width: auto; margin: 10px; } .modal-content { position: relative; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #999; border: 1px solid rgba(0, 0, 0, .2); border-radius: 6px; outline: 0; -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); box-shadow: 0 3px 9px rgba(0, 0, 0, .5); } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1040; background-color: #000; } .modal-backdrop.fade { filter: alpha(opacity=0); opacity: 0; } .modal-backdrop.in { filter: alpha(opacity=50); opacity: .5; } .modal-header { min-height: 16.42857143px; padding: 15px; border-bottom: 1px solid #e5e5e5; } .modal-header .close { margin-top: -2px; } .modal-title { margin: 0; line-height: 1.42857143; } .modal-body { position: relative; padding: 15px; } .modal-footer { padding: 15px; text-align: right; border-top: 1px solid #e5e5e5; } .modal-footer .btn + .btn { margin-bottom: 0; margin-left: 5px; } .modal-footer .btn-group .btn + .btn { margin-left: -1px; } .modal-footer .btn-block + .btn-block { margin-left: 0; } .modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; } @media (min-width: 768px) { .modal-dialog { width: 600px; margin: 30px auto; } .modal-content { -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); box-shadow: 0 5px 15px rgba(0, 0, 0, .5); } .modal-sm { width: 300px; } } @media (min-width: 992px) { .modal-lg { width: 900px; } } .tooltip { position: absolute; z-index: 1070; display: block; font-size: 12px; line-height: 1.4; visibility: visible; filter: alpha(opacity=0); opacity: 0; } .tooltip.in { filter: alpha(opacity=90); opacity: .9; } .tooltip.top { padding: 5px 0; margin-top: -3px; } .tooltip.right { padding: 0 5px; margin-left: 3px; } .tooltip.bottom { padding: 5px 0; margin-top: 3px; } .tooltip.left { padding: 0 5px; margin-left: -3px; } .tooltip-inner { max-width: 200px; padding: 3px 8px; color: #fff; text-align: center; text-decoration: none; background-color: #000; border-radius: 4px; } .tooltip-arrow { position: absolute; width: 0; height: 0; border-color: transparent; border-style: solid; } .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; border-width: 5px 5px 0; border-top-color: #000; } .tooltip.top-left .tooltip-arrow { bottom: 0; left: 5px; border-width: 5px 5px 0; border-top-color: #000; } .tooltip.top-right .tooltip-arrow { right: 5px; bottom: 0; border-width: 5px 5px 0; border-top-color: #000; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-width: 5px 5px 5px 0; border-right-color: #000; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-width: 5px 0 5px 5px; border-left-color: #000; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-width: 0 5px 5px; border-bottom-color: #000; } .tooltip.bottom-left .tooltip-arrow { top: 0; left: 5px; border-width: 0 5px 5px; border-bottom-color: #000; } .tooltip.bottom-right .tooltip-arrow { top: 0; right: 5px; border-width: 0 5px 5px; border-bottom-color: #000; } .popover { position: absolute; top: 0; left: 0; z-index: 1060; display: none; max-width: 276px; padding: 1px; text-align: left; white-space: normal; background-color: #fff; -webkit-background-clip: padding-box; background-clip: padding-box; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, .2); border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); box-shadow: 0 5px 10px rgba(0, 0, 0, .2); } .popover.top { margin-top: -10px; } .popover.right { margin-left: 10px; } .popover.bottom { margin-top: 10px; } .popover.left { margin-left: -10px; } .popover-title { padding: 8px 14px; margin: 0; font-size: 14px; font-weight: normal; line-height: 18px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; border-radius: 5px 5px 0 0; } .popover-content { padding: 9px 14px; } .popover > .arrow, .popover > .arrow:after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; } .popover > .arrow { border-width: 11px; } .popover > .arrow:after { content: ""; border-width: 10px; } .popover.top > .arrow { bottom: -11px; left: 50%; margin-left: -11px; border-top-color: #999; border-top-color: rgba(0, 0, 0, .25); border-bottom-width: 0; } .popover.top > .arrow:after { bottom: 1px; margin-left: -10px; content: " "; border-top-color: #fff; border-bottom-width: 0; } .popover.right > .arrow { top: 50%; left: -11px; margin-top: -11px; border-right-color: #999; border-right-color: rgba(0, 0, 0, .25); border-left-width: 0; } .popover.right > .arrow:after { bottom: -10px; left: 1px; content: " "; border-right-color: #fff; border-left-width: 0; } .popover.bottom > .arrow { top: -11px; left: 50%; margin-left: -11px; border-top-width: 0; border-bottom-color: #999; border-bottom-color: rgba(0, 0, 0, .25); } .popover.bottom > .arrow:after { top: 1px; margin-left: -10px; content: " "; border-top-width: 0; border-bottom-color: #fff; } .popover.left > .arrow { top: 50%; right: -11px; margin-top: -11px; border-right-width: 0; border-left-color: #999; border-left-color: rgba(0, 0, 0, .25); } .popover.left > .arrow:after { right: 1px; bottom: -10px; content: " "; border-right-width: 0; border-left-color: #fff; } .carousel { position: relative; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner > .item { position: relative; display: none; -webkit-transition: .6s ease-in-out left; -o-transition: .6s ease-in-out left; transition: .6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { line-height: 1; } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 0; bottom: 0; left: 0; width: 15%; font-size: 20px; color: #fff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, .6); filter: alpha(opacity=50); opacity: .5; } .carousel-control.left { background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); background-repeat: repeat-x; } .carousel-control.right { right: 0; left: auto; background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); background-repeat: repeat-x; } .carousel-control:hover, .carousel-control:focus { color: #fff; text-decoration: none; filter: alpha(opacity=90); outline: 0; opacity: .9; } .carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { position: absolute; top: 50%; z-index: 5; display: inline-block; } .carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left { left: 50%; margin-left: -10px; } .carousel-control .icon-next, .carousel-control .glyphicon-chevron-right { right: 50%; margin-right: -10px; } .carousel-control .icon-prev, .carousel-control .icon-next { width: 20px; height: 20px; margin-top: -10px; font-family: serif; } .carousel-control .icon-prev:before { content: '\2039'; } .carousel-control .icon-next:before { content: '\203a'; } .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; padding-left: 0; margin-left: -30%; text-align: center; list-style: none; } .carousel-indicators li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; cursor: pointer; background-color: #000 \9; background-color: rgba(0, 0, 0, 0); border: 1px solid #fff; border-radius: 10px; } .carousel-indicators .active { width: 12px; height: 12px; margin: 0; background-color: #fff; } .carousel-caption { position: absolute; right: 15%; bottom: 20px; left: 15%; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #fff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, .6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -15px; font-size: 30px; } .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { margin-left: -15px; } .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { margin-right: -15px; } .carousel-caption { right: 20%; left: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .clearfix:before, .clearfix:after, .dl-horizontal dd:before, .dl-horizontal dd:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .btn-toolbar:before, .btn-toolbar:after, .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after, .nav:before, .nav:after, .navbar:before, .navbar:after, .navbar-header:before, .navbar-header:after, .navbar-collapse:before, .navbar-collapse:after, .pager:before, .pager:after, .panel-body:before, .panel-body:after, .modal-footer:before, .modal-footer:after { display: table; content: " "; } .clearfix:after, .dl-horizontal dd:after, .container:after, .container-fluid:after, .row:after, .form-horizontal .form-group:after, .btn-toolbar:after, .btn-group-vertical > .btn-group:after, .nav:after, .navbar:after, .navbar-header:after, .navbar-collapse:after, .pager:after, .panel-body:after, .modal-footer:after { clear: both; } .center-block { display: block; margin-right: auto; margin-left: auto; } .pull-right { float: right !important; } .pull-left { float: left !important; } .hide { display: none !important; } .show { display: block !important; } .invisible { visibility: hidden; } .text-hide { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .hidden { display: none !important; visibility: hidden !important; } .affix { position: fixed; -webkit-transform: translate3d(0, 0, 0); -o-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } @-ms-viewport { width: device-width; } .visible-xs, .visible-sm, .visible-md, .visible-lg { display: none !important; } .visible-xs-block, .visible-xs-inline, .visible-xs-inline-block, .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, .visible-md-block, .visible-md-inline, .visible-md-inline-block, .visible-lg-block, .visible-lg-inline, .visible-lg-inline-block { display: none !important; } @media (max-width: 767px) { .visible-xs { display: block !important; } table.visible-xs { display: table; } tr.visible-xs { display: table-row !important; } th.visible-xs, td.visible-xs { display: table-cell !important; } } @media (max-width: 767px) { .visible-xs-block { display: block !important; } } @media (max-width: 767px) { .visible-xs-inline { display: inline !important; } } @media (max-width: 767px) { .visible-xs-inline-block { display: inline-block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm { display: block !important; } table.visible-sm { display: table; } tr.visible-sm { display: table-row !important; } th.visible-sm, td.visible-sm { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-block { display: block !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline { display: inline !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm-inline-block { display: inline-block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md { display: block !important; } table.visible-md { display: table; } tr.visible-md { display: table-row !important; } th.visible-md, td.visible-md { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-block { display: block !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline { display: inline !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md-inline-block { display: inline-block !important; } } @media (min-width: 1200px) { .visible-lg { display: block !important; } table.visible-lg { display: table; } tr.visible-lg { display: table-row !important; } th.visible-lg, td.visible-lg { display: table-cell !important; } } @media (min-width: 1200px) { .visible-lg-block { display: block !important; } } @media (min-width: 1200px) { .visible-lg-inline { display: inline !important; } } @media (min-width: 1200px) { .visible-lg-inline-block { display: inline-block !important; } } @media (max-width: 767px) { .hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-lg { display: none !important; } } .visible-print { display: none !important; } @media print { .visible-print { display: block !important; } table.visible-print { display: table; } tr.visible-print { display: table-row !important; } th.visible-print, td.visible-print { display: table-cell !important; } } .visible-print-block { display: none !important; } @media print { .visible-print-block { display: block !important; } } .visible-print-inline { display: none !important; } @media print { .visible-print-inline { display: inline !important; } } .visible-print-inline-block { display: none !important; } @media print { .visible-print-inline-block { display: inline-block !important; } } @media print { .hidden-print { display: none !important; } } /*# sourceMappingURL=bootstrap.css.map */ pat-0.12.1/web/res/bootstrap-3.2.0-dist/css/bootstrap.css.map000066400000000000000000006571661415513746300234720ustar00rootroot00000000000000{"version":3,"file":"bootstrap.css","sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"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;EAAW,kBAAA;EHoR9C;AGnRmC;EAAW,kBAAA;EHsR9C;AGrRmC;EAAW,kBAAA;EHwR9C;AGvRmC;EAAW,kBAAA;EH0R9C;AGzRmC;EAAW,kBAAA;EH4R9C;AG3RmC;EAAW,kBAAA;EH8R9C;AG7RmC;EAAW,kBAAA;EHgS9C;AG/RmC;EAAW,kBAAA;EHkS9C;AGjSmC;EAAW,kBAAA;EHoS9C;AGnSmC;EAAW,kBAAA;EHsS9C;AGrSmC;EAAW,kBAAA;EHwS9C;AGvSmC;EAAW,kBAAA;EH0S9C;AGzSmC;EAAW,kBAAA;EH4S9C;AG3SmC;EAAW,kBAAA;EH8S9C;AG7SmC;EAAW,kBAAA;EHgT9C;AG/SmC;EAAW,kBAAA;EHkT9C;AGjTmC;EAAW,kBAAA;EHoT9C;AGnTmC;EAAW,kBAAA;EHsT9C;AGrTmC;EAAW,kBAAA;EHwT9C;AGvTmC;EAAW,kBAAA;EH0T9C;AGzTmC;EAAW,kBAAA;EH4T9C;AG3TmC;EAAW,kBAAA;EH8T9C;AG7TmC;EAAW,kBAAA;EHgU9C;AG/TmC;EAAW,kBAAA;EHkU9C;AGjUmC;EAAW,kBAAA;EHoU9C;AGnUmC;EAAW,kBAAA;EHsU9C;AGrUmC;EAAW,kBAAA;EHwU9C;AGvUmC;EAAW,kBAAA;EH0U9C;AGzUmC;EAAW,kBAAA;EH4U9C;AG3UmC;EAAW,kBAAA;EH8U9C;AG7UmC;EAAW,kBAAA;EHgV9C;AG/UmC;EAAW,kBAAA;EHkV9C;AGjVmC;EAAW,kBAAA;EHoV9C;AGnVmC;EAAW,kBAAA;EHsV9C;AGrVmC;EAAW,kBAAA;EHwV9C;AGvVmC;EAAW,kBAAA;EH0V9C;AGzVmC;EAAW,kBAAA;EH4V9C;AG3VmC;EAAW,kBAAA;EH8V9C;AG7VmC;EAAW,kBAAA;EHgW9C;AG/VmC;EAAW,kBAAA;EHkW9C;AGjWmC;EAAW,kBAAA;EHoW9C;AGnWmC;EAAW,kBAAA;EHsW9C;AGrWmC;EAAW,kBAAA;EHwW9C;AGvWmC;EAAW,kBAAA;EH0W9C;AGzWmC;EAAW,kBAAA;EH4W9C;AG3WmC;EAAW,kBAAA;EH8W9C;AG7WmC;EAAW,kBAAA;EHgX9C;AG/WmC;EAAW,kBAAA;EHkX9C;AGjXmC;EAAW,kBAAA;EHoX9C;AGnXmC;EAAW,kBAAA;EHsX9C;AGrXmC;EAAW,kBAAA;EHwX9C;AGvXmC;EAAW,kBAAA;EH0X9C;AGzXmC;EAAW,kBAAA;EH4X9C;AG3XmC;EAAW,kBAAA;EH8X9C;AG7XmC;EAAW,kBAAA;EHgY9C;AG/XmC;EAAW,kBAAA;EHkY9C;AGjYmC;EAAW,kBAAA;EHoY9C;AGnYmC;EAAW,kBAAA;EHsY9C;AGrYmC;EAAW,kBAAA;EHwY9C;AGvYmC;EAAW,kBAAA;EH0Y9C;AGzYmC;EAAW,kBAAA;EH4Y9C;AG3YmC;EAAW,kBAAA;EH8Y9C;AG7YmC;EAAW,kBAAA;EHgZ9C;AG/YmC;EAAW,kBAAA;EHkZ9C;AGjZmC;EAAW,kBAAA;EHoZ9C;AGnZmC;EAAW,kBAAA;EHsZ9C;AGrZmC;EAAW,kBAAA;EHwZ9C;AGvZmC;EAAW,kBAAA;EH0Z9C;AGzZmC;EAAW,kBAAA;EH4Z9C;AG3ZmC;EAAW,kBAAA;EH8Z9C;AG7ZmC;EAAW,kBAAA;EHga9C;AG/ZmC;EAAW,kBAAA;EHka9C;AGjamC;EAAW,kBAAA;EHoa9C;AGnamC;EAAW,kBAAA;EHsa9C;AGramC;EAAW,kBAAA;EHwa9C;AGvamC;EAAW,kBAAA;EH0a9C;AGzamC;EAAW,kBAAA;EH4a9C;AG3amC;EAAW,kBAAA;EH8a9C;AG7amC;EAAW,kBAAA;EHgb9C;AG/amC;EAAW,kBAAA;EHkb9C;AGjbmC;EAAW,kBAAA;EHob9C;AGnbmC;EAAW,kBAAA;EHsb9C;AGrbmC;EAAW,kBAAA;EHwb9C;AGvbmC;EAAW,kBAAA;EH0b9C;AGzbmC;EAAW,kBAAA;EH4b9C;AG3bmC;EAAW,kBAAA;EH8b9C;AG7bmC;EAAW,kBAAA;EHgc9C;AG/bmC;EAAW,kBAAA;EHkc9C;AGjcmC;EAAW,kBAAA;EHoc9C;AGncmC;EAAW,kBAAA;EHsc9C;AGrcmC;EAAW,kBAAA;EHwc9C;AGvcmC;EAAW,kBAAA;EH0c9C;AGzcmC;EAAW,kBAAA;EH4c9C;AG3cmC;EAAW,kBAAA;EH8c9C;AG7cmC;EAAW,kBAAA;EHgd9C;AG/cmC;EAAW,kBAAA;EHkd9C;AGjdmC;EAAW,kBAAA;EHod9C;AGndmC;EAAW,kBAAA;EHsd9C;AGrdmC;EAAW,kBAAA;EHwd9C;AGvdmC;EAAW,kBAAA;EH0d9C;AGzdmC;EAAW,kBAAA;EH4d9C;AG3dmC;EAAW,kBAAA;EH8d9C;AG7dmC;EAAW,kBAAA;EHge9C;AG/dmC;EAAW,kBAAA;EHke9C;AGjemC;EAAW,kBAAA;EHoe9C;AGnemC;EAAW,kBAAA;EHse9C;AGremC;EAAW,kBAAA;EHwe9C;AGvemC;EAAW,kBAAA;EH0e9C;AGzemC;EAAW,kBAAA;EH4e9C;AG3emC;EAAW,kBAAA;EH8e9C;AG7emC;EAAW,kBAAA;EHgf9C;AG/emC;EAAW,kBAAA;EHkf9C;AGjfmC;EAAW,kBAAA;EHof9C;AGnfmC;EAAW,kBAAA;EHsf9C;AGrfmC;EAAW,kBAAA;EHwf9C;AGvfmC;EAAW,kBAAA;EH0f9C;AGzfmC;EAAW,kBAAA;EH4f9C;AG3fmC;EAAW,kBAAA;EH8f9C;AG7fmC;EAAW,kBAAA;EHggB9C;AG/fmC;EAAW,kBAAA;EHkgB9C;AGjgBmC;EAAW,kBAAA;EHogB9C;AGngBmC;EAAW,kBAAA;EHsgB9C;AGrgBmC;EAAW,kBAAA;EHwgB9C;AGvgBmC;EAAW,kBAAA;EH0gB9C;AGzgBmC;EAAW,kBAAA;EH4gB9C;AG3gBmC;EAAW,kBAAA;EH8gB9C;AG7gBmC;EAAW,kBAAA;EHghB9C;AG/gBmC;EAAW,kBAAA;EHkhB9C;AGjhBmC;EAAW,kBAAA;EHohB9C;AGnhBmC;EAAW,kBAAA;EHshB9C;AGrhBmC;EAAW,kBAAA;EHwhB9C;AGvhBmC;EAAW,kBAAA;EH0hB9C;AGzhBmC;EAAW,kBAAA;EH4hB9C;AG3hBmC;EAAW,kBAAA;EH8hB9C;AG7hBmC;EAAW,kBAAA;EHgiB9C;AG/hBmC;EAAW,kBAAA;EHkiB9C;AGjiBmC;EAAW,kBAAA;EHoiB9C;AGniBmC;EAAW,kBAAA;EHsiB9C;AGriBmC;EAAW,kBAAA;EHwiB9C;AGviBmC;EAAW,kBAAA;EH0iB9C;AGziBmC;EAAW,kBAAA;EH4iB9C;AG3iBmC;EAAW,kBAAA;EH8iB9C;AG7iBmC;EA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A;Efk8CH;Ae97CC;EACE,YAAA;Efg8CH;Ael7CC;EACE,mBAAA;Efo7CH;Aer7CC;EACE,2BAAA;Efu7CH;Aex7CC;EACE,2BAAA;Ef07CH;Ae37CC;EACE,kBAAA;Ef67CH;Ae97CC;EACE,2BAAA;Efg8CH;Aej8CC;EACE,2BAAA;Efm8CH;Aep8CC;EACE,kBAAA;Efs8CH;Aev8CC;EACE,2BAAA;Efy8CH;Ae18CC;EACE,2BAAA;Ef48CH;Ae78CC;EACE,kBAAA;Ef+8CH;Aeh9CC;EACE,2BAAA;Efk9CH;Aen9CC;EACE,0BAAA;Efq9CH;Aet9CC;EACE,iBAAA;Efw9CH;Aa59CD;EE9BI;IACE,aAAA;If6/CH;Eet/CD;IACE,aAAA;Ifw/CD;Eez/CD;IACE,qBAAA;If2/CD;Ee5/CD;IACE,qBAAA;If8/CD;Ee//CD;IACE,YAAA;IfigDD;EelgDD;IACE,qBAAA;IfogDD;EergDD;IACE,qBAAA;IfugDD;EexgDD;IACE,YAAA;If0gDD;Ee3gDD;IACE,qBAAA;If6gDD;Ee9gDD;IACE,qBAAA;IfghDD;EejhDD;IACE,YAAA;IfmhDD;EephDD;IACE,qBAAA;IfshDD;EevhDD;IACE,oBAAA;IfyhDD;Ee3gDD;IACE,aAAA;If6gDD;Ee9gDD;IACE,qBAAA;IfghDD;EejhDD;IACE,qBAAA;IfmhDD;EephDD;IACE,YAAA;IfshDD;EevhDD;IACE,qBAAA;IfyhDD;Ee1hDD;IACE,qBAAA;If4hDD;Ee7hDD;IACE,YAAA;If+hDD;EehiDD;IACE,qBAAA;IfkiDD;EeniDD;IACE,qBAAA;IfqiDD;EetiDD;IACE,YAAA;IfwiDD;EeziDD;IACE,qBAAA;If2iDD;Ee5iDD;IACE,oBAAA;If8iDD;Ee1iDD;IACE,aAAA;If4iDD;Ee5jDD;IACE,YAAA;If8jDD;Ee/jDD;IACE,oBAAA;IfikDD;EelkDD;IACE,oBAAA;IfokDD;EerkDD;IACE,WAAA;IfukDD;EexkDD;IACE,oBAAA;If0kDD;Ee3kDD;IACE,oBAAA;If6kDD;Ee9kDD;IACE,WAAA;IfglDD;EejlDD;IACE,oBAAA;IfmlDD;EeplDD;IACE,oBAAA;IfslDD;EevlDD;IACE,WAAA;IfylDD;Ee1lDD;IACE,oBAAA;If4lDD;Ee7lDD;IACE,mBAAA;If+lDD;Ee3lDD;IACE,YAAA;If6lDD;Ee/kDD;IACE,mBAAA;IfilDD;EellDD;IACE,2BAAA;IfolDD;EerlDD;IACE,2BAAA;IfulDD;EexlDD;IACE,kBAAA;If0lDD;Ee3lDD;IACE,2BAAA;If6lDD;Ee9lDD;IACE,2BAAA;IfgmDD;EejmDD;IACE,kBAAA;IfmmDD;EepmDD;IACE,2BAAA;IfsmDD;EevmDD;IACE,2BAAA;IfymDD;Ee1mDD;IACE,kBAAA;If4mDD;Ee7mDD;IACE,2BAAA;If+mDD;EehnDD;IACE,0BAAA;IfknDD;EennDD;IACE,iBAAA;IfqnDD;EACF;AajnDD;EEvCI;IACE,aAAA;If2pDH;EeppDD;IACE,aAAA;IfspDD;EevpDD;IACE,qBAAA;IfypDD;Ee1pDD;IACE,qBAAA;If4pDD;Ee7pDD;IACE,YAAA;If+pDD;EehqDD;IACE,qBAAA;IfkqDD;EenqDD;IACE,qBAAA;IfqqDD;EetqDD;IACE,YAAA;IfwqDD;EezqDD;IACE,qBAAA;If2qDD;Ee5qDD;IACE,qBAAA;If8qDD;Ee/qDD;IACE,YAAA;IfirDD;EelrDD;IACE,qBAAA;IforDD;EerrDD;IACE,oBAAA;IfurDD;EezqDD;IACE,aAAA;If2qDD;Ee5qDD;IACE,qBAAA;If8qDD;Ee/qDD;IACE,qBAAA;IfirDD;EelrDD;IACE,YAAA;IforDD;EerrDD;IACE,qBAAA;IfurDD;EexrDD;IACE,qBAAA;If0rDD;Ee3rDD;IACE,YAAA;If6rDD;Ee9rDD;IACE,qBAAA;IfgsDD;EejsDD;IACE,qBAAA;IfmsDD;EepsDD;IACE,YAAA;IfssDD;EevsDD;IACE,qBAAA;IfysDD;Ee1sDD;IACE,oBAAA;If4sDD;EexsDD;IACE,aAAA;If0sDD;Ee1tDD;IACE,YAAA;If4tDD;Ee7tDD;IACE,oBAAA;If+tDD;EehuDD;IACE,oBAAA;IfkuDD;EenuDD;IACE,WAAA;IfquDD;EetuDD;IACE,oBAAA;IfwuDD;EezuDD;IACE,oBAAA;If2uDD;Ee5uDD;IACE,WAAA;If8uDD;Ee/uDD;IACE,oBAAA;IfivDD;EelvDD;IACE,oBAAA;IfovDD;EervDD;IACE,WAAA;IfuvDD;EexvDD;IACE,oBAAA;If0vDD;Ee3vDD;IACE,mBAAA;If6vDD;EezvDD;IACE,YAAA;If2vDD;Ee7uDD;IACE,mBAAA;If+uDD;EehvDD;IACE,2BAAA;IfkvDD;EenvDD;IACE,2BAAA;IfqvDD;EetvDD;IACE,kBAAA;IfwvDD;EezvDD;IACE,2BAAA;If2vDD;Ee5vDD;IACE,2BAAA;If8vDD;Ee/vDD;IACE,kBAAA;IfiwDD;EelwDD;IACE,2BAAA;IfowDD;EerwDD;IACE,2BAAA;IfuwDD;EexwDD;IACE,kBAAA;If0wDD;Ee3wDD;IACE,2BAAA;If6wDD;Ee9wDD;IACE,0BAAA;IfgxDD;EejxDD;IACE,iBAAA;IfmxDD;EACF;AaxwDD;EE9CI;IACE,aAAA;IfyzDH;EelzDD;IACE,aAAA;IfozDD;EerzDD;IACE,qBAAA;IfuzDD;EexzDD;IACE,qBAAA;If0zDD;Ee3zDD;IACE,YAAA;If6zDD;Ee9zDD;IACE,qBAAA;Ifg0DD;Eej0DD;IACE,qBAAA;Ifm0DD;Eep0DD;IACE,YAAA;Ifs0DD;Eev0DD;IACE,qBAAA;Ify0DD;Ee10DD;IACE,qBAAA;If40DD;Ee70DD;IACE,YAAA;If+0DD;Eeh1DD;IACE,qBAAA;Ifk1DD;Een1DD;IACE,oBAAA;Ifq1DD;Eev0DD;IACE,aAAA;Ify0DD;Ee10DD;IACE,qBAAA;If40DD;Ee70DD;IACE,qBAAA;If+0DD;Eeh1DD;IACE,YAAA;Ifk1DD;Een1DD;IACE,qBAAA;Ifq1DD;Eet1DD;IACE,qBAAA;Ifw1DD;Eez1DD;IACE,YAAA;If21DD;Ee51DD;IACE,qBAAA;If81DD;Ee/1DD;IACE,qBAAA;Ifi2DD;Eel2DD;IACE,YAAA;Ifo2DD;Eer2DD;IACE,qBAAA;Ifu2DD;Eex2DD;IACE,oBAAA;If02DD;Eet2DD;IACE,aAAA;Ifw2DD;Eex3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AAA;EACI,uBAAA;ErBqsFT;AoB9qFD;EClBI,gBAAA;EACA,2BAAA;ErBmsFH;AoB/qFD;ECrDE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBuuFD;AqBruFC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBuuFP;AqBruFC;;;EAGE,wBAAA;ErBuuFH;AqBluFG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErB8uFT;AoBptFD;ECrBI,gBAAA;EACA,2BAAA;ErB4uFH;AoBptFD;ECzDE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBgxFD;AqB9wFC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBgxFP;AqB9wFC;;;EAGE,wBAAA;ErBgxFH;AqB3wFG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBuxFT;AoBzvFD;ECzBI,gBAAA;EACA,2BAAA;ErBqxFH;AoBzvFD;EC7DE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErByzFD;AqBvzFC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErByzFP;AqBvzFC;;;EAGE,wBAAA;ErByzFH;AqBpzFG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBg0FT;AoB9xFD;EC7BI,gBAAA;EACA,2BAAA;ErB8zFH;AoB9xFD;ECjEE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBk2FD;AqBh2FC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBk2FP;AqBh2FC;;;EAGE,wBAAA;ErBk2FH;AqB71FG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBy2FT;AoBn0FD;ECjCI,gBAAA;EACA,2BAAA;ErBu2FH;AoBn0FD;ECrEE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErB24FD;AqBz4FC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErB24FP;AqBz4FC;;;EAGE,wBAAA;ErB24FH;AqBt4FG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBk5FT;AoBx2FD;ECrCI,gBAAA;EACA,2BAAA;ErBg5FH;AoBn2FD;EACE,gBAAA;EACA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","sourcesContent":[null,"/*! normalize.css v3.0.1 | MIT License | git.io/normalize */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS text size adjust after orientation change, without disabling\n// user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background: transparent;\n}\n\n//\n// Improve readability when focused and also mouse hovered in all browsers.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n -moz-box-sizing: content-box;\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome\n// (include `-moz` to future-proof).\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n -moz-box-sizing: content-box;\n -webkit-box-sizing: content-box; // 2\n box-sizing: content-box;\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","//\n// Basic print styles\n// --------------------------------------------------\n// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css\n\n@media print {\n\n * {\n text-shadow: none !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n background: transparent !important;\n box-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links for images, or javascript/internal links\n a[href^=\"javascript:\"]:after,\n a[href^=\"#\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245\n // Once fixed, we can just straight up remove this.\n select {\n background: #fff !important;\n }\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .table {\n td,\n th {\n background-color: #fff !important;\n }\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\2a\"; } }\n.glyphicon-plus { &:before { content: \"\\2b\"; } }\n.glyphicon-euro { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: underline;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n &::-moz-placeholder { color: @color; // Firefox\n opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // Default\n outline: thin dotted;\n // WebKit\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n width: 100% \\9; // Force IE10 and below to size SVG images correctly\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\n// Undo browser default styling\ncite {\n font-style: normal;\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @grid-float-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Quotes\nblockquote:before,\nblockquote:after {\n content: \"\";\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: (@gutter / -2);\n margin-right: (@gutter / -2);\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) when (@index = 1) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: (@grid-gutter-width / 2);\n padding-right: (@grid-gutter-width / 2);\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) when (@index = 1) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-child(odd) {\n > td,\n > th {\n background-color: @table-bg-accent;\n }\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n > td,\n > th {\n background-color: @table-bg-hover;\n }\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n overflow-x: auto;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n -webkit-overflow-scrolling: touch;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\n// Set the height of file controls to match text inputs\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius;\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Disabled and read-only inputs\n //\n // HTML5 says that controls under a fieldset > legend:first-child won't be\n // disabled if the fieldset is disabled. Due to implementation difficulty, we\n // don't honor that edge case; we style them as disabled anyway.\n &[disabled],\n &[readonly],\n fieldset[disabled] & {\n cursor: not-allowed;\n background-color: @input-bg-disabled;\n opacity: 1; // iOS fix for unreadable disabled content\n }\n\n // Reset height for `textarea`s\n textarea& {\n height: auto;\n }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned.\n// As a workaround, we set a pixel line-height that matches the\n// given height of the input. Since this fucks up everything else, we have to\n// appropriately reset it for Internet Explorer and the size variations.\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n line-height: @input-height-base;\n // IE8+ misaligns the text within date inputs, so we reset\n line-height: @line-height-base ~\"\\0\";\n\n &.input-sm {\n line-height: @input-height-small;\n }\n &.input-lg {\n line-height: @input-height-large;\n }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n margin-bottom: 15px;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n position: relative;\n display: block;\n min-height: @line-height-computed; // clear the floating input if there is no label text\n margin-top: 10px;\n margin-bottom: 10px;\n\n label {\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because