Compare commits
38 commits
Author | SHA1 | Date | |
---|---|---|---|
89120bdaef | |||
4a625bf3eb | |||
3bcffbe251 | |||
0083c6ec17 | |||
84f0229044 | |||
077cecdf1a | |||
573d9650d0 | |||
5d22bd3fbf | |||
0b1f8ea046 | |||
724d8581a8 | |||
5db3f5eff9 | |||
4cd94a74c2 | |||
cda6fde9bf | |||
8e0f7acb57 | |||
dc8ecc9ede | |||
73afd8124f | |||
4cccf95aa8 | |||
972503292d | |||
e2593e5f24 | |||
2472aebec4 | |||
cbe8ea987f | |||
4ff47fc46d | |||
105160334d | |||
b4c17876c6 | |||
3c84c6194d | |||
0c6e3e7771 | |||
885cae4721 | |||
25652847c4 | |||
5592b2e676 | |||
b41180c7b0 | |||
a3940abed8 | |||
85cbab1d70 | |||
c0c6762313 | |||
24cbed411e | |||
e11cd88ee2 | |||
b618d4f886 | |||
1129bd68be | |||
f88068c08d |
102
admin/api.go
|
@ -1,102 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DispatchFunction func([]string, []byte) (interface{}, error)
|
||||
|
||||
var apiRoutes = map[string]*(map[string]DispatchFunction){
|
||||
"version": &ApiVersionRouting,
|
||||
"ca": &ApiCARouting,
|
||||
"events": &ApiEventsRouting,
|
||||
"exercices": &ApiExercicesRouting,
|
||||
"themes": &ApiThemesRouting,
|
||||
"teams": &ApiTeamsRouting,
|
||||
}
|
||||
|
||||
type apiRouting struct{}
|
||||
|
||||
func ApiHandler() http.Handler {
|
||||
return apiRouting{}
|
||||
}
|
||||
|
||||
func (a apiRouting) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Handling %s request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
|
||||
// Extract URL arguments
|
||||
var sURL = strings.Split(r.URL.Path, "/")[1:]
|
||||
if len(sURL) > 1 && sURL[len(sURL)-1] == "" {
|
||||
// Remove trailing /
|
||||
sURL = sURL[:len(sURL)-1]
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
var ret interface{}
|
||||
var err error = nil
|
||||
|
||||
// Read the body
|
||||
if r.ContentLength < 0 || r.ContentLength > 6553600 {
|
||||
http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}", err), http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
var body []byte
|
||||
if r.ContentLength > 0 {
|
||||
tmp := make([]byte, 1024)
|
||||
for {
|
||||
n, err := r.Body.Read(tmp)
|
||||
for j := 0; j < n; j++ {
|
||||
body = append(body, tmp[j])
|
||||
}
|
||||
if err != nil || n <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Route request
|
||||
if len(sURL) > 0 {
|
||||
if h, ok := apiRoutes[sURL[0]]; ok {
|
||||
if f, ok := (*h)[r.Method]; ok {
|
||||
ret, err = f(sURL[1:], body)
|
||||
} else {
|
||||
err = errors.New(fmt.Sprintf("Invalid action (%s) provided for %s.", r.Method, sURL[0]))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = errors.New("No action provided.")
|
||||
}
|
||||
|
||||
// Format response
|
||||
resStatus := http.StatusOK
|
||||
if err != nil {
|
||||
ret = map[string]string{"errmsg": err.Error()}
|
||||
resStatus = http.StatusBadRequest
|
||||
log.Println(r.RemoteAddr, resStatus, err.Error())
|
||||
}
|
||||
|
||||
if ret == nil {
|
||||
ret = map[string]string{"errmsg": "Page not found"}
|
||||
resStatus = http.StatusNotFound
|
||||
}
|
||||
|
||||
if str, found := ret.(string); found {
|
||||
w.WriteHeader(resStatus)
|
||||
io.WriteString(w, str)
|
||||
} else if bts, found := ret.([]byte); found {
|
||||
w.WriteHeader(resStatus)
|
||||
w.Write(bts)
|
||||
} else if j, err := json.Marshal(ret); err != nil {
|
||||
http.Error(w, fmt.Sprintf("{\"errmsg\":\"%q\"}", err), http.StatusInternalServerError)
|
||||
} else {
|
||||
w.WriteHeader(resStatus)
|
||||
w.Write(j)
|
||||
}
|
||||
}
|
31
admin/api/certificate.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.Path("/ca").Methods("GET").HandlerFunc(apiHandler(genCA))
|
||||
|
||||
rt := router.PathPrefix("/teams/{tid}/certificate").Subrouter()
|
||||
rt.Path("/").Methods("GET").HandlerFunc(apiHandler(teamHandler(GetTeamCertificate)))
|
||||
rt.Path("/generate").Methods("GET").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) { return team.GenerateCert(), nil })))
|
||||
rt.Path("/revoke").Methods("GET").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) { return team.RevokeCert(), nil })))
|
||||
}
|
||||
|
||||
func genCA(args map[string]string, body []byte) (interface{}, error) {
|
||||
return fic.GenerateCA(), nil
|
||||
}
|
||||
|
||||
func GetTeamCertificate(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
if fd, err := os.Open("../PKI/pkcs/" + team.Name + ".p12"); err == nil {
|
||||
return ioutil.ReadAll(fd)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
17
admin/api/events.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.Path("/events/").Methods("GET").HandlerFunc(apiHandler(getEvents))
|
||||
}
|
||||
|
||||
func getEvents(args map[string]string, body []byte) (interface{}, error) {
|
||||
if evts, err := fic.GetEvents(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return evts, nil
|
||||
}
|
||||
}
|
136
admin/api/exercice.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.Path("/exercices/").Methods("GET").HandlerFunc(apiHandler(listExercices))
|
||||
re := router.Path("/exercices/{eid:[0-9]+}").Subrouter()
|
||||
re.Methods("GET").HandlerFunc(apiHandler(exerciceHandler(showExercice)))
|
||||
re.Methods("PUT").HandlerFunc(apiHandler(updateExercice))
|
||||
re.Methods("DELETE").HandlerFunc(apiHandler(deleteExercice))
|
||||
}
|
||||
|
||||
func listExercices(args map[string]string, body []byte) (interface{}, error) {
|
||||
// List all exercices
|
||||
return fic.GetExercices()
|
||||
}
|
||||
|
||||
func showExercice(exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
return exercice, nil
|
||||
}
|
||||
|
||||
func deleteExercice(args map[string]string, body []byte) (interface{}, error) {
|
||||
if eid, err := strconv.Atoi(args["eid"]); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return exercice.Delete()
|
||||
}
|
||||
}
|
||||
|
||||
type uploadedExercice struct {
|
||||
Title string
|
||||
Statement string
|
||||
Depend *int64
|
||||
Gain int
|
||||
VideoURI string
|
||||
}
|
||||
|
||||
func updateExercice(args map[string]string, body []byte) (interface{}, error) {
|
||||
if eid, err := strconv.Atoi(args["eid"]); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// Update an exercice
|
||||
var ue uploadedExercice
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ue.Title) == 0 {
|
||||
return nil, errors.New("Exercice's title not filled")
|
||||
}
|
||||
|
||||
if ue.Depend != nil {
|
||||
if _, err := fic.GetExercice(*ue.Depend); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
exercice.Title = ue.Title
|
||||
exercice.Statement = ue.Statement
|
||||
exercice.Depend = ue.Depend
|
||||
exercice.Gain = int64(ue.Gain)
|
||||
exercice.VideoURI = ue.VideoURI
|
||||
|
||||
return exercice.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func createExercice(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
|
||||
// Create a new exercice
|
||||
var ue uploadedExercice
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ue.Title) == 0 {
|
||||
return nil, errors.New("Title not filled")
|
||||
}
|
||||
|
||||
var depend *fic.Exercice = nil
|
||||
if ue.Depend != nil {
|
||||
if d, err := fic.GetExercice(*ue.Depend); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
depend = &d
|
||||
}
|
||||
}
|
||||
|
||||
return theme.AddExercice(ue.Title, ue.Statement, depend, ue.Gain, ue.VideoURI)
|
||||
}
|
||||
|
||||
type uploadedKey struct {
|
||||
Name string
|
||||
Key string
|
||||
}
|
||||
|
||||
func createExerciceKey(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
var uk uploadedKey
|
||||
if err := json.Unmarshal(body, &uk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uk.Key) == 0 {
|
||||
return nil, errors.New("Key not filled")
|
||||
}
|
||||
|
||||
return exercice.AddRawKey(uk.Name, uk.Key)
|
||||
}
|
||||
|
||||
type uploadedHint struct {
|
||||
Title string
|
||||
Content string
|
||||
Cost int64
|
||||
}
|
||||
|
||||
func createExerciceHint(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
var uh uploadedHint
|
||||
if err := json.Unmarshal(body, &uh); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uh.Content) == 0 {
|
||||
return nil, errors.New("Hint's content not filled")
|
||||
}
|
||||
|
||||
return exercice.AddHint(uh.Title, uh.Content, uh.Cost)
|
||||
}
|
120
admin/api/file.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha512"
|
||||
"encoding/base32"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var CloudDAVBase string
|
||||
var CloudUsername string
|
||||
var CloudPassword string
|
||||
|
||||
type uploadedFile struct {
|
||||
URI string
|
||||
Digest []byte
|
||||
Path string
|
||||
Parts []string
|
||||
}
|
||||
|
||||
func createExerciceFile(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
var uf uploadedFile
|
||||
if err := json.Unmarshal(body, &uf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var hash [sha512.Size]byte
|
||||
var logStr string
|
||||
var fromURI string
|
||||
var getFile func(string) (error)
|
||||
|
||||
if uf.URI != "" {
|
||||
hash = sha512.Sum512([]byte(uf.URI))
|
||||
logStr = "Import file from Cloud: " + uf.URI + " =>"
|
||||
fromURI = uf.URI
|
||||
getFile = func(dest string) error { return getCloudFile(uf.URI, dest); }
|
||||
} else if uf.Path != "" && len(uf.Parts) > 0 {
|
||||
hash = sha512.Sum512([]byte(uf.Path))
|
||||
logStr = fmt.Sprintf("Import file from local FS: %s =>", uf.Parts)
|
||||
fromURI = uf.Path
|
||||
getFile = func(dest string) error {
|
||||
if fdto, err := os.Create(dest); err != nil {
|
||||
return err
|
||||
} else {
|
||||
writer := bufio.NewWriter(fdto)
|
||||
for _, partname := range uf.Parts {
|
||||
if fdfrm, err := os.Open(partname); err != nil {
|
||||
return err
|
||||
} else {
|
||||
reader := bufio.NewReader(fdfrm)
|
||||
reader.WriteTo(writer)
|
||||
writer.Flush()
|
||||
fdfrm.Close()
|
||||
}
|
||||
}
|
||||
fdto.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
} else if uf.Path != "" {
|
||||
hash = sha512.Sum512([]byte(uf.Path))
|
||||
logStr = "Import file from local FS: " + uf.Path + " =>"
|
||||
fromURI = uf.Path
|
||||
getFile = func(dest string) error { return os.Symlink(uf.Path, dest); }
|
||||
} else {
|
||||
return nil, errors.New("URI or path not filled")
|
||||
}
|
||||
|
||||
pathname := path.Join(fic.FilesDir, strings.ToLower(base32.StdEncoding.EncodeToString(hash[:])), path.Base(fromURI))
|
||||
|
||||
if _, err := os.Stat(pathname); os.IsNotExist(err) {
|
||||
log.Println(logStr, pathname)
|
||||
if err := os.MkdirAll(path.Dir(pathname), 0777); err != nil {
|
||||
return nil, err
|
||||
} else if err := getFile(pathname); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return exercice.ImportFile(pathname, fromURI)
|
||||
}
|
||||
|
||||
func getCloudFile(pathname string, dest string) error {
|
||||
client := http.Client{}
|
||||
if req, err := http.NewRequest("GET", CloudDAVBase+pathname, nil); err != nil {
|
||||
return err
|
||||
} else {
|
||||
req.SetBasicAuth(CloudUsername, CloudPassword)
|
||||
if resp, err := client.Do(req); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
|
||||
if fd, err := os.Create(dest); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer fd.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(resp.Status)
|
||||
} else {
|
||||
writer := bufio.NewWriter(fd)
|
||||
reader := bufio.NewReader(resp.Body)
|
||||
reader.WriteTo(writer)
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
131
admin/api/handlers.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
|
||||
type DispatchFunction func([]string, []byte) (interface{}, error)
|
||||
|
||||
func apiHandler(f func (map[string]string,[]byte) (interface{}, error)) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Handling %s request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
var ret interface{}
|
||||
var err error = nil
|
||||
|
||||
// Read the body
|
||||
if r.ContentLength < 0 || r.ContentLength > 6553600 {
|
||||
http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}", err), http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
var body []byte
|
||||
if r.ContentLength > 0 {
|
||||
tmp := make([]byte, 1024)
|
||||
for {
|
||||
n, err := r.Body.Read(tmp)
|
||||
for j := 0; j < n; j++ {
|
||||
body = append(body, tmp[j])
|
||||
}
|
||||
if err != nil || n <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret, err = f(mux.Vars(r), body)
|
||||
|
||||
// Format response
|
||||
resStatus := http.StatusOK
|
||||
if err != nil {
|
||||
ret = map[string]string{"errmsg": err.Error()}
|
||||
resStatus = http.StatusBadRequest
|
||||
log.Println(r.RemoteAddr, resStatus, err.Error())
|
||||
}
|
||||
|
||||
if ret == nil {
|
||||
ret = map[string]string{"errmsg": "Page not found"}
|
||||
resStatus = http.StatusNotFound
|
||||
}
|
||||
|
||||
if str, found := ret.(string); found {
|
||||
w.WriteHeader(resStatus)
|
||||
io.WriteString(w, str)
|
||||
} else if bts, found := ret.([]byte); found {
|
||||
w.WriteHeader(resStatus)
|
||||
w.Write(bts)
|
||||
} else if j, err := json.Marshal(ret); err != nil {
|
||||
http.Error(w, fmt.Sprintf("{\"errmsg\":\"%q\"}", err), http.StatusInternalServerError)
|
||||
} else {
|
||||
w.WriteHeader(resStatus)
|
||||
w.Write(j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func teamHandler(f func(fic.Team,map[string]string,[]byte) (interface{}, error)) func (map[string]string,[]byte) (interface{}, error) {
|
||||
return func (args map[string]string, body []byte) (interface{}, error) {
|
||||
if tid, err := strconv.Atoi(string(args["tid"])); err != nil {
|
||||
if team, err := fic.GetTeamByInitialName(args["tid"]); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(team, args, body)
|
||||
}
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(team, args, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func themeHandler(f func(fic.Theme,map[string]string,[]byte) (interface{}, error)) func (map[string]string,[]byte) (interface{}, error) {
|
||||
return func (args map[string]string, body []byte) (interface{}, error) {
|
||||
if tid, err := strconv.Atoi(string(args["tid"])); err != nil {
|
||||
return nil, err
|
||||
} else if theme, err := fic.GetTheme(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(theme, args, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func exerciceHandler(f func(fic.Exercice,map[string]string,[]byte) (interface{}, error)) func (map[string]string,[]byte) (interface{}, error) {
|
||||
return func (args map[string]string, body []byte) (interface{}, error) {
|
||||
if eid, err := strconv.Atoi(string(args["eid"])); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(exercice, args, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func themedExerciceHandler(f func(fic.Theme,fic.Exercice,map[string]string,[]byte) (interface{}, error)) func (fic.Theme,map[string]string,[]byte) (interface{}, error) {
|
||||
return func (theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
|
||||
if eid, err := strconv.Atoi(string(args["eid"])); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(theme, exercice, args, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func notFound(args map[string]string, body []byte) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
14
admin/api/router.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var api_router = mux.NewRouter().StrictSlash(true)
|
||||
|
||||
var router = api_router.PathPrefix("/api/").Subrouter()
|
||||
|
||||
|
||||
func Router() *mux.Router {
|
||||
return api_router
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
156
admin/api/team.go
Normal file
|
@ -0,0 +1,156 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rts := router.PathPrefix("/teams").Subrouter()
|
||||
router.Path("/teams.json").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return fic.ExportTeams() }))
|
||||
rts.Path("/").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return fic.GetTeams() }))
|
||||
rts.Path("/binding").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return bindingTeams() }))
|
||||
rts.Path("/nginx").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return nginxGenTeam() }))
|
||||
rts.Path("/nginx-members").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return nginxGenMember() }))
|
||||
rts.Path("/binding").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return fic.GetTries(nil, nil) }))
|
||||
|
||||
rts.Path("/0/my.json").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return fic.MyJSONTeam(nil, true) }))
|
||||
rts.Path("/0/wait.json").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return fic.MyJSONTeam(nil, false) }))
|
||||
rts.Path("/0/stats.json").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return fic.GetTeamsStats(nil) }))
|
||||
rts.Path("/0/tries").Methods("GET").HandlerFunc(apiHandler(
|
||||
func(map[string]string,[]byte) (interface{}, error) {
|
||||
return fic.GetTries(nil, nil) }))
|
||||
|
||||
rt := rts.PathPrefix("/{tid}").Subrouter()
|
||||
rt.Path("/").Methods("GET").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
return team, nil })))
|
||||
rt.Path("/").Methods("DELETE").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
return team.Delete() })))
|
||||
rt.Path("/my.json").Methods("GET").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
return fic.MyJSONTeam(&team, true) })))
|
||||
rt.Path("/wait.json").Methods("GET").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
return fic.MyJSONTeam(&team, false) })))
|
||||
rt.Path("/stats.json").Methods("GET").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
return team.GetStats() })))
|
||||
rt.Path("/tries").Methods("GET").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
return fic.GetTries(&team, nil) })))
|
||||
rt.Path("/members").Methods("GET").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
return team.GetMembers() })))
|
||||
rt.Path("/name").Methods("GET").HandlerFunc(apiHandler(teamHandler(
|
||||
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
return team.Name, nil })))
|
||||
}
|
||||
|
||||
func nginxGenMember() (string, error) {
|
||||
if teams, err := fic.GetTeams(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
if members, err := team.GetMembers(); err == nil {
|
||||
for _, member := range members {
|
||||
ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%s\"; }\n", member.Nickname, team.InitialName)
|
||||
}
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func nginxGenTeam() (string, error) {
|
||||
if teams, err := fic.GetTeams(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
ret += fmt.Sprintf(" if ($ssl_client_s_dn ~ \"/C=FR/ST=France/O=Epita/OU=SRS/CN=%s\") { set $team \"%s\"; }\n", team.InitialName, team.InitialName)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func bindingTeams() (string, error) {
|
||||
if teams, err := fic.GetTeams(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
if members, err := team.GetMembers(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
var mbs []string
|
||||
for _, member := range members {
|
||||
mbs = append(mbs, fmt.Sprintf("%s %s", member.Firstname, member.Lastname))
|
||||
}
|
||||
ret += fmt.Sprintf("%d;%s;%s\n", team.Id, team.Name, strings.Join(mbs, ";"))
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
type uploadedTeam struct {
|
||||
Name string
|
||||
Color uint32
|
||||
}
|
||||
|
||||
type uploadedMember struct {
|
||||
Firstname string
|
||||
Lastname string
|
||||
Nickname string
|
||||
Company string
|
||||
}
|
||||
|
||||
func createTeam(args map[string]string, body []byte) (interface{}, error) {
|
||||
var ut uploadedTeam
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fic.CreateTeam(ut.Name, ut.Color)
|
||||
}
|
||||
|
||||
func addTeamMember(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
|
||||
var members []uploadedMember
|
||||
if err := json.Unmarshal(body, &members); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, member := range members {
|
||||
team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company)
|
||||
}
|
||||
|
||||
return team.GetMembers()
|
||||
}
|
161
admin/api/theme.go
Normal file
|
@ -0,0 +1,161 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.Path("/themes/").Methods("GET").HandlerFunc(apiHandler(listThemes))
|
||||
router.Path("/themes/").Methods("POST").HandlerFunc(apiHandler(createTheme))
|
||||
router.Path("/themes.json").Methods("GET").HandlerFunc(apiHandler(exportThemes))
|
||||
router.Path("/themes/files-bindings").Methods("GET").HandlerFunc(apiHandler(bindingFiles))
|
||||
|
||||
rt := router.PathPrefix("/themes/{tid:[0-9]+}").Subrouter()
|
||||
rt.Path("/").Methods("GET").HandlerFunc(apiHandler(themeHandler(showTheme)))
|
||||
rt.Path("/").Methods("PUT").HandlerFunc(apiHandler(themeHandler(updateTheme)))
|
||||
rt.Path("/").Methods("DELETE").HandlerFunc(apiHandler(themeHandler(deleteTheme)))
|
||||
|
||||
rtes := rt.PathPrefix("/exercices").Subrouter()
|
||||
rtes.Path("/").Methods("GET").HandlerFunc(apiHandler(themeHandler(listThemedExercices)))
|
||||
rtes.Path("/").Methods("POST").HandlerFunc(apiHandler(themeHandler(createExercice)))
|
||||
|
||||
rte := rtes.PathPrefix("/{eid:[0-9]+}").Subrouter()
|
||||
rte.Path("/").Methods("GET").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(showThemedExercice))))
|
||||
rte.Path("/").Methods("PUT").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(updateThemedExercice))))
|
||||
rte.Path("/").Methods("DELETE").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(deleteThemedExercice))))
|
||||
|
||||
rtef := rte.Path("/files").Subrouter()
|
||||
rtef.Methods("GET").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(listThemedExerciceFiles))))
|
||||
rtef.Methods("POST").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(createExerciceFile))))
|
||||
|
||||
rteh := rte.Path("/hints").Subrouter()
|
||||
rteh.Methods("GET").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(listThemedExerciceHints))))
|
||||
rteh.Methods("POST").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(createExerciceHint))))
|
||||
|
||||
rtek := rte.Path("/keys").Subrouter()
|
||||
rtek.Methods("GET").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(listThemedExerciceKeys))))
|
||||
rtek.Methods("POST").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(createExerciceKey))))
|
||||
}
|
||||
|
||||
func bindingFiles(args map[string]string, body []byte) (interface{}, error) {
|
||||
if files, err := fic.GetFiles(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, file := range files {
|
||||
ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getExercice(args []string) (fic.Exercice, error) {
|
||||
if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return fic.Exercice{}, err
|
||||
} else if theme, err := fic.GetTheme(tid); err != nil {
|
||||
return fic.Exercice{}, err
|
||||
} else if eid, err := strconv.Atoi(string(args[1])); err != nil {
|
||||
return fic.Exercice{}, err
|
||||
} else {
|
||||
return theme.GetExercice(eid)
|
||||
}
|
||||
}
|
||||
|
||||
func listThemes(args map[string]string, body []byte) (interface{}, error) {
|
||||
return fic.GetThemes()
|
||||
}
|
||||
|
||||
func exportThemes(args map[string]string, body []byte) (interface{}, error) {
|
||||
return fic.ExportThemes()
|
||||
}
|
||||
|
||||
func showTheme(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
|
||||
return theme, nil
|
||||
}
|
||||
|
||||
func listThemedExercices(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
|
||||
return theme.GetExercices()
|
||||
}
|
||||
|
||||
func showThemedExercice(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
return exercice, nil
|
||||
}
|
||||
|
||||
func listThemedExerciceFiles(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
return exercice.GetFiles()
|
||||
}
|
||||
|
||||
func listThemedExerciceHints(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
return exercice.GetHints()
|
||||
}
|
||||
|
||||
func listThemedExerciceKeys(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
return exercice.GetKeys()
|
||||
}
|
||||
|
||||
|
||||
type uploadedTheme struct {
|
||||
Name string
|
||||
Authors string
|
||||
}
|
||||
|
||||
func createTheme(args map[string]string, body []byte) (interface{}, error) {
|
||||
var ut uploadedTheme
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ut.Name) == 0 {
|
||||
return nil, errors.New("Theme's name not filled")
|
||||
}
|
||||
|
||||
return fic.CreateTheme(ut.Name, ut.Authors)
|
||||
}
|
||||
|
||||
func updateTheme(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
|
||||
var ut fic.Theme
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ut.Id = theme.Id
|
||||
|
||||
if len(ut.Name) == 0 {
|
||||
return nil, errors.New("Theme's name not filled")
|
||||
}
|
||||
|
||||
return ut.Update()
|
||||
}
|
||||
|
||||
func updateThemedExercice(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
// Update an exercice
|
||||
var ue fic.Exercice
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ue.Id = exercice.Id
|
||||
|
||||
if len(ue.Title) == 0 {
|
||||
return nil, errors.New("Exercice's title not filled")
|
||||
}
|
||||
|
||||
if _, err := ue.Update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ue, nil
|
||||
}
|
||||
|
||||
func deleteTheme(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
|
||||
return theme.Delete()
|
||||
}
|
||||
|
||||
func deleteThemedExercice(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
|
||||
return exercice.Delete()
|
||||
}
|
11
admin/api/version.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package api
|
||||
|
||||
import ()
|
||||
|
||||
func init() {
|
||||
router.Path("/version").Methods("GET").HandlerFunc(apiHandler(showVersion))
|
||||
}
|
||||
|
||||
func showVersion(args map[string]string, body []byte) (interface{}, error) {
|
||||
return map[string]interface{}{"version": 0.1}, nil
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func CertificateAPI(team fic.Team, args []string) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if args[0] == "generate" {
|
||||
return team.GenerateCert(), nil
|
||||
} else if args[0] == "revoke" {
|
||||
return team.RevokeCert(), nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
} else if fd, err := os.Open("../PKI/pkcs/" + team.Name + ".p12"); err == nil {
|
||||
return ioutil.ReadAll(fd)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var ApiCARouting = map[string]DispatchFunction{
|
||||
"GET": genCA,
|
||||
}
|
||||
|
||||
func genCA(args []string, body []byte) (interface{}, error) {
|
||||
return fic.GenerateCA(), nil
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var ApiEventsRouting = map[string]DispatchFunction{
|
||||
"GET": getEvents,
|
||||
}
|
||||
|
||||
func getEvents(args []string, body []byte) (interface{}, error) {
|
||||
if evts, err := fic.GetEvents(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return evts, nil
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var ApiExercicesRouting = map[string]DispatchFunction{
|
||||
"GET": listExercice,
|
||||
"PATCH": updateExercice,
|
||||
"DELETE": deletionExercice,
|
||||
}
|
||||
|
||||
func listExercice(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if eid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return fic.GetExercice(int64(eid))
|
||||
}
|
||||
} else {
|
||||
// List all exercices
|
||||
return fic.GetExercices()
|
||||
}
|
||||
}
|
||||
|
||||
func deletionExercice(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if eid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return exercice.Delete()
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
type uploadedExercice struct {
|
||||
Title string
|
||||
Statement string
|
||||
Hint string
|
||||
Depend *int64
|
||||
Gain int
|
||||
VideoURI string
|
||||
}
|
||||
|
||||
func updateExercice(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if eid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// Update an exercice
|
||||
var ue uploadedExercice
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ue.Title) == 0 {
|
||||
return nil, errors.New("Exercice's title not filled")
|
||||
}
|
||||
|
||||
if ue.Depend != nil {
|
||||
if _, err := fic.GetExercice(*ue.Depend); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
exercice.Title = ue.Title
|
||||
exercice.Statement = ue.Statement
|
||||
exercice.Hint = ue.Hint
|
||||
exercice.Depend = ue.Depend
|
||||
exercice.Gain = int64(ue.Gain)
|
||||
exercice.VideoURI = ue.VideoURI
|
||||
|
||||
return exercice.Update()
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func createExercice(theme fic.Theme, args []string, body []byte) (interface{}, error) {
|
||||
if len(args) >= 1 {
|
||||
if eid, err := strconv.Atoi(args[0]); err != nil {
|
||||
return nil, err
|
||||
} else if exercice, err := theme.GetExercice(eid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if args[1] == "files" {
|
||||
return createExerciceFile(theme, exercice, args[2:], body)
|
||||
} else if args[1] == "keys" {
|
||||
return createExerciceKey(theme, exercice, args[2:], body)
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
} else {
|
||||
// Create a new exercice
|
||||
var ue uploadedExercice
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ue.Title) == 0 {
|
||||
return nil, errors.New("Title not filled")
|
||||
}
|
||||
|
||||
var depend *fic.Exercice = nil
|
||||
if ue.Depend != nil {
|
||||
if d, err := fic.GetExercice(*ue.Depend); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
depend = &d
|
||||
}
|
||||
}
|
||||
|
||||
return theme.AddExercice(ue.Title, ue.Statement, ue.Hint, depend, ue.Gain, ue.VideoURI)
|
||||
}
|
||||
}
|
||||
|
||||
type uploadedKey struct {
|
||||
Name string
|
||||
Key string
|
||||
}
|
||||
|
||||
func createExerciceKey(theme fic.Theme, exercice fic.Exercice, args []string, body []byte) (interface{}, error) {
|
||||
var uk uploadedKey
|
||||
if err := json.Unmarshal(body, &uk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uk.Key) == 0 {
|
||||
return nil, errors.New("Key not filled")
|
||||
}
|
||||
|
||||
return exercice.AddRawKey(uk.Name, uk.Key)
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha512"
|
||||
"encoding/base32"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
type uploadedFile struct {
|
||||
URI string
|
||||
}
|
||||
|
||||
func createExerciceFile(theme fic.Theme, exercice fic.Exercice, args []string, body []byte) (interface{}, error) {
|
||||
var uf uploadedFile
|
||||
if err := json.Unmarshal(body, &uf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uf.URI) == 0 {
|
||||
return nil, errors.New("URI not filled")
|
||||
}
|
||||
|
||||
hash := sha512.Sum512([]byte(uf.URI))
|
||||
pathname := path.Join(fic.FilesDir, strings.ToLower(base32.StdEncoding.EncodeToString(hash[:])), path.Base(uf.URI))
|
||||
|
||||
if _, err := os.Stat(pathname); os.IsNotExist(err) {
|
||||
log.Println("Import file from Cloud:", uf.URI, "=>", pathname)
|
||||
if err := os.MkdirAll(path.Dir(pathname), 0777); err != nil {
|
||||
return nil, err
|
||||
} else if err := getCloudFile(uf.URI, pathname); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return exercice.ImportFile(pathname, uf.URI)
|
||||
}
|
||||
|
||||
func getCloudFile(pathname string, dest string) error {
|
||||
client := http.Client{}
|
||||
if req, err := http.NewRequest("GET", CloudDAVBase+pathname, nil); err != nil {
|
||||
return err
|
||||
} else {
|
||||
req.SetBasicAuth(CloudUsername, CloudPassword)
|
||||
if resp, err := client.Do(req); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer resp.Body.Close()
|
||||
|
||||
if fd, err := os.Create(dest); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer fd.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(resp.Status)
|
||||
} else {
|
||||
writer := bufio.NewWriter(fd)
|
||||
reader := bufio.NewReader(resp.Body)
|
||||
reader.WriteTo(writer)
|
||||
writer.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var ApiTeamsRouting = map[string]DispatchFunction{
|
||||
"GET": listTeam,
|
||||
"PUT": creationTeamMembers,
|
||||
"POST": creationTeam,
|
||||
"DELETE": deletionTeam,
|
||||
}
|
||||
|
||||
func nginxGenTeam() (string, error) {
|
||||
if teams, err := fic.GetTeams(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
ret += fmt.Sprintf(" if ($ssl_client_s_dn ~ \"/C=FR/ST=France/O=Epita/OU=SRS/CN=%s\") { set $team \"%s\"; }\n", team.InitialName, team.InitialName)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func bindingTeams() (string, error) {
|
||||
if teams, err := fic.GetTeams(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, team := range teams {
|
||||
if members, err := team.GetMembers(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
var mbs []string
|
||||
for _, member := range members {
|
||||
mbs = append(mbs, fmt.Sprintf("%s %s", member.Firstname, member.Lastname))
|
||||
}
|
||||
ret += fmt.Sprintf("%d;%s;%s\n", team.Id, team.Name, strings.Join(mbs, ";"))
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
type uploadedTeam struct {
|
||||
Name string
|
||||
Color uint32
|
||||
}
|
||||
|
||||
type uploadedMember struct {
|
||||
Firstname string
|
||||
Lastname string
|
||||
Nickname string
|
||||
Company string
|
||||
}
|
||||
|
||||
func listTeam(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) >= 2 {
|
||||
var team *fic.Team
|
||||
if tid, err := strconv.Atoi(args[0]); err != nil {
|
||||
if t, err := fic.GetTeamByInitialName(args[0]); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
team = &t
|
||||
}
|
||||
} else {
|
||||
if tid == 0 {
|
||||
team = nil
|
||||
} else if t, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
team = &t
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if args[1] == "my.json" {
|
||||
return fic.MyJSONTeam(team, true)
|
||||
} else if args[1] == "wait.json" {
|
||||
return fic.MyJSONTeam(team, false)
|
||||
} else if team != nil && args[1] == "members" {
|
||||
return team.GetMembers()
|
||||
} else if args[1] == "certificate" && team != nil {
|
||||
return CertificateAPI(*team, args[2:])
|
||||
} else if team != nil && args[1] == "name" {
|
||||
return team.Name, nil
|
||||
}
|
||||
} else if len(args) == 1 {
|
||||
if args[0] == "teams.json" {
|
||||
return fic.ExportTeams()
|
||||
} else if args[0] == "nginx" {
|
||||
return nginxGenTeam()
|
||||
} else if args[0] == "binding" {
|
||||
return bindingTeams()
|
||||
} else if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return fic.GetTeamByInitialName(args[0])
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return team, nil
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// List all teams
|
||||
return fic.GetTeams()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func creationTeam(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
// List given team
|
||||
if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
var members []uploadedMember
|
||||
if err := json.Unmarshal(body, &members); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, member := range members {
|
||||
team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company)
|
||||
}
|
||||
|
||||
return team.GetMembers()
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// Create a new team
|
||||
var ut uploadedTeam
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fic.CreateTeam(ut.Name, ut.Color)
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func creationTeamMembers(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
// List given team
|
||||
if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
var member uploadedMember
|
||||
if err := json.Unmarshal(body, &member); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company)
|
||||
|
||||
return team.GetMembers()
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// Create a new team
|
||||
var members []uploadedMember
|
||||
if err := json.Unmarshal(body, &members); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if team, err := fic.CreateTeam("", 0); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, member := range members {
|
||||
if _, err := team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return team, nil
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func deletionTeam(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 1 {
|
||||
if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return nil, err
|
||||
} else if team, err := fic.GetTeam(tid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return team.Delete()
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var ApiThemesRouting = map[string]DispatchFunction{
|
||||
"GET": listTheme,
|
||||
"PATCH": updateTheme,
|
||||
"POST": creationTheme,
|
||||
"DELETE": deletionTheme,
|
||||
}
|
||||
|
||||
func bindingFiles() (string, error) {
|
||||
if files, err := fic.GetFiles(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
ret := ""
|
||||
for _, file := range files {
|
||||
ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getTheme(args []string) (fic.Theme, error) {
|
||||
if tid, err := strconv.Atoi(string(args[0])); err != nil {
|
||||
return fic.Theme{}, err
|
||||
} else {
|
||||
return fic.GetTheme(tid)
|
||||
}
|
||||
}
|
||||
|
||||
func getExercice(args []string) (fic.Exercice, error) {
|
||||
if theme, err := getTheme(args); err != nil {
|
||||
return fic.Exercice{}, err
|
||||
} else if eid, err := strconv.Atoi(string(args[1])); err != nil {
|
||||
return fic.Exercice{}, err
|
||||
} else {
|
||||
return theme.GetExercice(eid)
|
||||
}
|
||||
}
|
||||
|
||||
func listTheme(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 3 {
|
||||
if e, err := getExercice(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if args[2] == "files" {
|
||||
return e.GetFiles()
|
||||
} else if args[2] == "keys" {
|
||||
return e.GetKeys()
|
||||
}
|
||||
}
|
||||
} else if len(args) == 2 {
|
||||
if args[1] == "exercices" {
|
||||
if theme, err := getTheme(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return theme.GetExercices()
|
||||
}
|
||||
} else {
|
||||
return getExercice(args)
|
||||
}
|
||||
} else if len(args) == 1 {
|
||||
if args[0] == "files-bindings" {
|
||||
return bindingFiles()
|
||||
} else if args[0] == "themes.json" {
|
||||
return fic.ExportThemes()
|
||||
} else {
|
||||
return getTheme(args)
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// List all themes
|
||||
return fic.GetThemes()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type uploadedTheme struct {
|
||||
Name string
|
||||
Authors string
|
||||
}
|
||||
|
||||
func creationTheme(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) >= 1 {
|
||||
if theme, err := getTheme(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return createExercice(theme, args[1:], body)
|
||||
}
|
||||
} else if len(args) == 0 {
|
||||
// Create a new theme
|
||||
var ut uploadedTheme
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ut.Name) == 0 {
|
||||
return nil, errors.New("Theme's name not filled")
|
||||
}
|
||||
|
||||
return fic.CreateTheme(ut.Name, ut.Authors)
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateTheme(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 2 {
|
||||
// Update an exercice
|
||||
var ue fic.Exercice
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ue.Title) == 0 {
|
||||
return nil, errors.New("Exercice's title not filled")
|
||||
}
|
||||
|
||||
if _, err := ue.Update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ue, nil
|
||||
} else if len(args) == 1 {
|
||||
// Update a theme
|
||||
var ut fic.Theme
|
||||
if err := json.Unmarshal(body, &ut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ut.Name) == 0 {
|
||||
return nil, errors.New("Theme's name not filled")
|
||||
}
|
||||
|
||||
return ut.Update()
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func deletionTheme(args []string, body []byte) (interface{}, error) {
|
||||
if len(args) == 2 {
|
||||
if exercice, err := getExercice(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return exercice.Delete()
|
||||
}
|
||||
} else if len(args) == 1 {
|
||||
if theme, err := getTheme(args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return theme.Delete()
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package main
|
||||
|
||||
import ()
|
||||
|
||||
var ApiVersionRouting = map[string]DispatchFunction{
|
||||
"GET": showVersion,
|
||||
}
|
||||
|
||||
func showVersion(args []string, body []byte) (interface{}, error) {
|
||||
return map[string]interface{}{"version": 0.1}, nil
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
BASEURL="http://localhost:8081"
|
||||
BASEURI="https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016"
|
||||
BASEFILE="/files"
|
||||
CLOUDPASS=fic:'f>t\nV33R|(+?$i*'
|
||||
BASEURI="https://owncloud.srs.epita.fr/remote.php/webdav/FIC 2017"
|
||||
BASEFILE="/mnt/fic/"
|
||||
CLOUDPASS=nemunaire:'p0WJ$&I#OWEtC5UI4@dD'
|
||||
|
||||
new_theme() {
|
||||
NAME=`echo $1 | sed 's/"/\\\\"/g'`
|
||||
|
@ -16,12 +16,11 @@ new_exercice() {
|
|||
THEME="$1"
|
||||
TITLE=`echo "$2" | sed 's/"/\\\\"/g'`
|
||||
STATEMENT=`echo "$3" | sed 's/"/\\\\"/g' | sed ':a;N;$!ba;s/\n/<br>/g'`
|
||||
HINT=`echo "$4" | sed 's/"/\\\\"/g' | sed ':a;N;$!ba;s/\n/<br>/g'`
|
||||
DEPEND="$5"
|
||||
GAIN="$6"
|
||||
VIDEO="$7"
|
||||
DEPEND="$4"
|
||||
GAIN="$5"
|
||||
VIDEO="$6"
|
||||
|
||||
curl -f -s -d "{\"title\": \"$TITLE\", \"statement\": \"$STATEMENT\", \"hint\": \"$HINT\", \"depend\": $DEPEND, \"gain\": $GAIN, \"videoURI\": \"$VIDEO\"}" "${BASEURL}/api/themes/$THEME" |
|
||||
curl -f -s -d "{\"title\": \"$TITLE\", \"statement\": \"$STATEMENT\", \"depend\": $DEPEND, \"gain\": $GAIN, \"videoURI\": \"$VIDEO\"}" "${BASEURL}/api/themes/$THEME/exercices/" |
|
||||
grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
}
|
||||
|
||||
|
@ -29,8 +28,28 @@ new_file() {
|
|||
THEME="$1"
|
||||
EXERCICE="$2"
|
||||
URI="$3"
|
||||
ARGS="$4"
|
||||
|
||||
curl -f -s -d "{\"URI\": \"${BASEFILE}${URI}\"}" "${BASEURL}/api/themes/$THEME/$EXERCICE/files" |
|
||||
echo "$ARGS" | while read arg
|
||||
do
|
||||
[ -z "${PARTS}" ] || PARTS="${PARTS},"
|
||||
PARTS="${PARTS}\"$arg\""
|
||||
done
|
||||
|
||||
# curl -f -s -d "{\"URI\": \"${BASEFILE}${URI}\"}" "${BASEURL}/api/themes/$THEME/$EXERCICE/files" |
|
||||
curl -f -s -d @- "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/files" <<EOF | grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
{"path": "${BASEFILE}${URI}", "parts": [${PARTS}]}
|
||||
EOF
|
||||
}
|
||||
|
||||
new_hint() {
|
||||
THEME="$1"
|
||||
EXERCICE="$2"
|
||||
TITLE=`echo "$3" | sed 's/"/\\\\"/g'`
|
||||
CONTENT=`echo "$4" | sed 's/"/\\\\"/g' | sed ':a;N;$!ba;s/\n/<br>/g'`
|
||||
COST="$5"
|
||||
|
||||
curl -f -s -d "{\"title\": \"$TITLE\", \"content\": \"$CONTENT\", \"cost\": $COST}" "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/hints" |
|
||||
grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
}
|
||||
|
||||
|
@ -40,42 +59,65 @@ new_key() {
|
|||
NAME="$3"
|
||||
KEY=`echo $4 | sed 's/"/\\\\"/g' | sed 's#\\\\#\\\\\\\\#g'`
|
||||
|
||||
curl -f -s -d "{\"name\": \"$NAME\", \"key\": \"$KEY\"}" "${BASEURL}/api/themes/$THEME/$EXERCICE/keys" |
|
||||
curl -f -s -d "{\"name\": \"$NAME\", \"key\": \"$KEY\"}" "${BASEURL}/api/themes/$THEME/exercices/$EXERCICE/keys" |
|
||||
grep -Eo '"id":[0-9]+,' | grep -Eo "[0-9]+"
|
||||
}
|
||||
|
||||
get_dir_from_cloud() {
|
||||
curl -f -s -X PROPFIND -u "${CLOUDPASS}" "${BASEURI}$1" | xmllint --format - | grep 'd:href' | sed -E 's/^.*>(.*)<.*$/\1/'
|
||||
}
|
||||
get_dir() {
|
||||
ls "${BASEFILE}$1" 2> /dev/null
|
||||
}
|
||||
#alias get_dir=get_dir_from_cloud
|
||||
|
||||
get_file_from_cloud() {
|
||||
curl -f -s -u "${CLOUDPASS}" "${BASEURI}$1" | tr -d '\r'
|
||||
}
|
||||
get_file() {
|
||||
cat "${BASEFILE}$1" 2> /dev/null | tr -d '\r'
|
||||
}
|
||||
#alias get_file=get_file_from_cloud
|
||||
|
||||
unhtmlentities() {
|
||||
cat | sed -E 's/%20/ /g' | sed -E "s/%27/'/g" | sed -E 's/%c3%a9/é/g' | sed -E 's/%c3%a8/è/g'
|
||||
}
|
||||
|
||||
# Theme
|
||||
curl -f -s -X PROPFIND -u "${CLOUDPASS}" "${BASEURI}" | xmllint --format - | grep 'd:href' | sed -E 's/^.*>(.*)<.*$/\1/' | sed 1d | tac | while read f; do basename "$f"; done | while read THEME_URI
|
||||
get_dir "" | while read f; do basename "$f"; done | while read THEME_URI
|
||||
do
|
||||
THM_BASEURI="/${THEME_URI}/"
|
||||
THEME_NAME=$(echo "${THEME_URI}" | sed -E 's/%20/ /g' | sed -E 's/%c3%a9/é/g' | sed -E 's/%c3%a8/è/g')
|
||||
THEME_AUTHORS=$(curl -f -s -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}/AUTHORS.txt" | sed 's/$/,/' | xargs)
|
||||
THEME_NAME=$(echo "${THEME_URI#*-}" | unhtmlentities)
|
||||
THEME_AUTHORS=$(get_file "${THM_BASEURI}/AUTHORS.txt" | sed 's/$/, /' | tr -d '\n' | sed 's/, $//')
|
||||
THEME_ID=`new_theme "$THEME_NAME" "$THEME_AUTHORS"`
|
||||
if [ -z "$THEME_ID" ]; then
|
||||
echo -e "\e[31;01m!!! An error occured during theme add\e[00m"
|
||||
continue
|
||||
continue
|
||||
else
|
||||
echo -e "\e[33m>>> New theme created:\e[00m $THEME_ID - $THEME_NAME"
|
||||
fi
|
||||
|
||||
LAST=null
|
||||
EXO_NUM=0
|
||||
curl -f -s -X PROPFIND -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}" | xmllint --format - | grep 'd:href' | sed -E 's/^.*>(.*)<.*$/\1/' | sed -E 's/%20/ /g' | sed -E 's/%c3%a9/é/g' | sed -E 's/%c3%a8/è/g' | sed 1d | while read f; do basename "$f"; done | while read EXO_NAME
|
||||
get_dir "${THM_BASEURI}" | while read f; do basename "$f"; done | while read EXO_URI
|
||||
do
|
||||
if ! echo $EXO_NAME | grep Exercice > /dev/null
|
||||
then
|
||||
continue
|
||||
fi
|
||||
case ${EXO_URI} in
|
||||
[0-9]-*)
|
||||
;;
|
||||
*)
|
||||
continue;;
|
||||
esac
|
||||
|
||||
EXO_NUM=$((EXO_NUM + 1))
|
||||
#EXO_NUM=$((EXO_NUM + 1))
|
||||
EXO_NUM=${EXO_URI%-*}
|
||||
EXO_NAME=$(echo "${EXO_URI#*-}" | unhtmlentities)
|
||||
echo
|
||||
echo -e "\e[36m--- Filling exercice ${EXO_NUM} in theme ${THEME_NAME}\e[00m"
|
||||
|
||||
EXO_BASEURI="${EXO_NAME}/"
|
||||
EXO_BASEURI="${EXO_URI}/"
|
||||
|
||||
FILES=$(curl -f -s -X PROPFIND -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}${EXO_BASEURI}" | xmllint --format - | grep 'd:href' | sed -E 's/^.*>(.*)<.*$/\1/' | sed 1d | while read f; do basename $f; done)
|
||||
|
||||
EXO_VIDEO=$(echo "$FILES" | grep -E "\.(mov|mkv|mp4|avi|flv|ogv|webm)$" | tail -1)
|
||||
EXO_VIDEO=$(get_dir "${THM_BASEURI}${EXO_BASEURI}/resolution/" | grep -E "\.(mov|mkv|mp4|avi|flv|ogv|webm)$" | while read f; do basename $f; done | tail -1)
|
||||
[ -n "$EXO_VIDEO" ] && EXO_VIDEO="/resolution${THM_BASEURI}${EXO_BASEURI}resolution/${EXO_VIDEO}"
|
||||
|
||||
if [ "${LAST}" = "null" ]; then
|
||||
echo ">>> Assuming this exercice has no dependency"
|
||||
|
@ -86,10 +128,9 @@ do
|
|||
EXO_GAIN=$((3 * (2 ** $EXO_NUM) - 1))
|
||||
echo ">>> Using default gain: ${EXO_GAIN} points"
|
||||
|
||||
EXO_DESC=$(curl -f -s -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}${EXO_BASEURI}/description.txt")
|
||||
EXO_HINT=$(curl -f -s -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}${EXO_BASEURI}/hint.txt")
|
||||
EXO_SCENARIO=$(get_file "${THM_BASEURI}${EXO_BASEURI}/scenario.txt")
|
||||
|
||||
EXO_ID=`new_exercice "${THEME_ID}" "${EXO_NAME}" "${EXO_DESC}" "${EXO_HINT}" "${LAST}" "${EXO_GAIN}" "${THM_BASEURI}${EXO_BASEURI}${EXO_VIDEO}"`
|
||||
EXO_ID=`new_exercice "${THEME_ID}" "${EXO_NAME}" "${EXO_SCENARIO}" "${LAST}" "${EXO_GAIN}" "${EXO_VIDEO}"`
|
||||
if [ -z "$EXO_ID" ]; then
|
||||
echo -e "\e[31;01m!!! An error occured during exercice add.\e[00m"
|
||||
continue
|
||||
|
@ -99,7 +140,7 @@ do
|
|||
|
||||
|
||||
# Keys
|
||||
curl -f -s -u "${CLOUDPASS}" "${BASEURI}${THM_BASEURI}${EXO_BASEURI}/keys.txt" | while read KEYLINE
|
||||
get_file "${THM_BASEURI}${EXO_BASEURI}/flags.txt" | while read KEYLINE
|
||||
do
|
||||
KEY_NAME=$(echo "$KEYLINE" | cut -d : -f 1)
|
||||
KEY_RAW=$(echo "$KEYLINE" | cut -d : -f 2-)
|
||||
|
@ -117,16 +158,45 @@ do
|
|||
done
|
||||
|
||||
|
||||
# Files
|
||||
for f in $FILES; do echo $f; done | grep -vEi "(ressources|readme|description.txt|hint.txt|keys.txt|${EXO_VIDEO})" |
|
||||
while read FBASE
|
||||
# Hints
|
||||
EXO_HINT=$(get_file "${THM_BASEURI}${EXO_BASEURI}/hint.txt")
|
||||
if [ -n "$EXO_HINT" ]; then
|
||||
HINT_ID=`new_hint "${THEME_ID}" "${EXO_ID}" "Astuce #1" "${EXO_HINT}" "1"`
|
||||
if [ -z "$HINT_ID" ]; then
|
||||
echo -e "\e[31;01m!!! An error occured during hint import!\e[00m (title=Astuce $1;content=${EXO_HINT};cost=1)"
|
||||
else
|
||||
echo -e "\e[32m>>> New key added:\e[00m $KEY_ID - $KEY_NAME"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Files: splited
|
||||
get_dir "${THM_BASEURI}${EXO_BASEURI}files/" | grep -v DIGESTS.txt | grep '[0-9][0-9]$' | sed -E 's/\.?([0-9][0-9])$//' | sort | uniq | while read f; do basename "$f"; done | while read FILE_URI
|
||||
do
|
||||
echo "Import file ${BASEURI}${THM_BASEURI}${EXO_BASEURI}${FBASE}"
|
||||
FILE_ID=`new_file "${THEME_ID}" "${EXO_ID}" "${THM_BASEURI}${EXO_BASEURI}${FBASE}"`
|
||||
PARTS=
|
||||
for part in "$(get_dir "${THM_BASEURI}${EXO_BASEURI}files/" | grep "${FILE_URI}" | sort)"
|
||||
do
|
||||
PARTS="${PARTS}${BASEFILE}${THM_BASEURI}${EXO_BASEURI}files/${part}\n"
|
||||
done
|
||||
echo "Import splited file ${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI} from `echo ${PART} | tr '\n' ' '`"
|
||||
|
||||
FILE_ID=`new_file "${THEME_ID}" "${EXO_ID}" "${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI}" "${PARTS}"`
|
||||
if [ -z "$FILE_ID" ]; then
|
||||
echo -e "\e[31;01m!!! An error occured during file import! Please check path.\e[00m"
|
||||
else
|
||||
echo -e "\e[32m>>> New file added:\e[00m $FILE_ID - $FBASE"
|
||||
echo -e "\e[32m>>> New file added:\e[00m $FILE_ID - $FILE_URI"
|
||||
fi
|
||||
done
|
||||
|
||||
# Files: entire
|
||||
get_dir "${THM_BASEURI}${EXO_BASEURI}files/" | grep -v DIGESTS.txt | grep -v '[0-9][0-9]$' | while read f; do basename "$f"; done | while read FILE_URI
|
||||
do
|
||||
echo "Import file ${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI}"
|
||||
FILE_ID=`new_file "${THEME_ID}" "${EXO_ID}" "${THM_BASEURI}${EXO_BASEURI}files/${FILE_URI}"`
|
||||
if [ -z "$FILE_ID" ]; then
|
||||
echo -e "\e[31;01m!!! An error occured during file import! Please check path.\e[00m"
|
||||
else
|
||||
echo -e "\e[32m>>> New file added:\e[00m $FILE_ID - $FILE_URI"
|
||||
fi
|
||||
done
|
||||
|
||||
|
|
|
@ -1,7 +1,48 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
BASEURL="http://localhost:8081"
|
||||
PART_FILE="Challenge_Liste des participants.csv"
|
||||
BASEURL="http://127.0.0.1:8081/admin"
|
||||
GEN_CERTS=0
|
||||
EXTRA_TEAMS=0
|
||||
CSV_SPLITER=","
|
||||
CSV_COL_LASTNAME=1
|
||||
CSV_COL_FIRSTNAME=2
|
||||
CSV_COL_NICKNAME=3
|
||||
CSV_COL_COMPANY=7
|
||||
CSV_COL_TEAM=7
|
||||
|
||||
usage() {
|
||||
echo "$0 [options] csv_file"
|
||||
echo " -B -baseurl BASEURL URL to administration endpoint (default: $BASEURL)"
|
||||
echo " -S -csv-spliter SEP CSV separator (default: $CSV_SPLITER)"
|
||||
echo " -e -extra-teams NBS Number of extra teams to generate (default: ${EXTRA_TEAMS})"
|
||||
echo " -c -generate-certificate Should team certificates be generated? (default: no)"
|
||||
}
|
||||
|
||||
# Parse options
|
||||
while [ "${1:0:1}" = "-" ]
|
||||
do
|
||||
case "$1" in
|
||||
-B|-baseurl)
|
||||
BASEURL=$2
|
||||
shift;;
|
||||
-S|-csv-spliter)
|
||||
CSV_SPLITER=$2
|
||||
shift;;
|
||||
-e|-extra-teams)
|
||||
EXTRA_TEAMS=$2
|
||||
shift;;
|
||||
-c|-generate-certificates)
|
||||
GEN_CERTS=1;;
|
||||
*)
|
||||
echo "Unknown option '$1'"
|
||||
usage
|
||||
exit 1;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$#" -lt 1 ] && { usage; exit 1; }
|
||||
PART_FILE="$1"
|
||||
|
||||
new_team() {
|
||||
head -n "$1" team-names.txt | tail -1 | sed -E 's/^.*\|\[\[([^|]+\|)?([^|]+)\]\][^|]*\|([A-Fa-f0-9]{1,2})\|([A-Fa-f0-9]{1,2})\|([A-Fa-f0-9]{1,2})\|([0-9]{1,3})\|([0-9]{1,3})\|([0-9]{1,3})\|.*$/\6 \7 \8 \2/' |
|
||||
|
@ -10,7 +51,11 @@ new_team() {
|
|||
R=`echo $line | cut -d " " -f 1`
|
||||
G=`echo $line | cut -d " " -f 2`
|
||||
B=`echo $line | cut -d " " -f 3`
|
||||
N=`echo $line | cut -d " " -f 4`
|
||||
if [ -z "$2" ]; then
|
||||
N=`echo $line | cut -d " " -f 4`
|
||||
else
|
||||
N=`echo -n $2 | tr -d '\r\n'`
|
||||
fi
|
||||
|
||||
COLOR=$((($R*256 + $G) * 256 + $B))
|
||||
|
||||
|
@ -20,7 +65,7 @@ new_team() {
|
|||
|
||||
TNUM=0
|
||||
|
||||
for i in `seq 12`
|
||||
for i in $(seq $EXTRA_TEAMS)
|
||||
do
|
||||
TNUM=$(($TNUM + 1))
|
||||
|
||||
|
@ -28,31 +73,32 @@ do
|
|||
|
||||
TID=`new_team $TNUM`
|
||||
|
||||
if ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
||||
if [ "${GEN_CERTS}" -eq 1 ] && ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
||||
then
|
||||
curl -s -f "${BASEURL}/api/teams/${TID}/certificate/generate"
|
||||
fi
|
||||
echo
|
||||
done
|
||||
|
||||
TMAX=`sed "1d" "$PART_FILE" | cut -d \; -f 15 | sort | uniq | wc -l`
|
||||
TMAX=`cat "$PART_FILE" | cut -d "${CSV_SPLITER}" -f $CSV_COL_TEAM | sort | uniq | wc -l`
|
||||
TMAX=$(($TMAX + $TNUM))
|
||||
sed "1d" "$PART_FILE" | cut -d \; -f 15 | sort | uniq | while read TEAMID
|
||||
cat "$PART_FILE" | cut -d "${CSV_SPLITER}" -f $CSV_COL_TEAM | sort | uniq | while read TEAMID
|
||||
do
|
||||
TNUM=$(($TNUM + 1))
|
||||
|
||||
echo "Doing team $TNUM/$TMAX ("$(($TNUM*100/$TMAX))"%)..."
|
||||
|
||||
TID=`new_team $TNUM`
|
||||
TID=`new_team "${TNUM}" "${TEAMID}"`
|
||||
|
||||
(
|
||||
if ! (
|
||||
echo -n "["
|
||||
HAS_MEMBER=1
|
||||
grep ";$TEAMID\$" "$PART_FILE" | while read MEMBER
|
||||
grep "${CSV_SPLITER}${TEAMID}\$" "$PART_FILE" | while read MEMBER
|
||||
do
|
||||
LASTNAME=`echo $MEMBER | cut -d ";" -f 2`
|
||||
FIRSTNAME=`echo $MEMBER | cut -d ";" -f 3`
|
||||
COMPANY=`echo $MEMBER | cut -d ";" -f 4`
|
||||
LASTNAME=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_LASTNAME | tr -d "\r\n"`
|
||||
FIRSTNAME=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_FIRSTNAME | tr -d "\r\n"`
|
||||
NICKNAME=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_NICKNAME | tr -d "\r\n"`
|
||||
COMPANY=`echo $MEMBER | cut -d "${CSV_SPLITER}" -f $CSV_COL_COMPANY | tr -d "\r\n"`
|
||||
|
||||
if [ $HAS_MEMBER = 0 ]
|
||||
then
|
||||
|
@ -65,15 +111,16 @@ do
|
|||
{
|
||||
"firstname": "$FIRSTNAME",
|
||||
"lastname": "$LASTNAME",
|
||||
"nickname": "",
|
||||
"nickname": "$NICKNAME",
|
||||
"company": "$COMPANY"
|
||||
}
|
||||
EOF
|
||||
done
|
||||
echo "]"
|
||||
) | curl -s -d @- "${BASEURL}/api/teams/${TID}" > /dev/null
|
||||
|
||||
if ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
||||
) | curl -f -s -d @- "${BASEURL}/api/teams/${TID}"
|
||||
then
|
||||
echo "An error occured"
|
||||
elif [ "${GEN_CERTS}" -eq 1 ] && ! curl -s -f "${BASEURL}/api/teams/${TID}/certificate" > /dev/null
|
||||
then
|
||||
curl -s -f "${BASEURL}/api/teams/${TID}/certificate/generate"
|
||||
fi
|
||||
|
|
54
admin/index.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
const indextpl = `<!DOCTYPE html>
|
||||
<html ng-app="FICApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge Forensic - Administration</title>
|
||||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||||
<base href="{{.urlbase}}">
|
||||
<script src="/js/d3.v3.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse navbar-static-top">
|
||||
<div class="container">
|
||||
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="{{.urlbase}}">
|
||||
<img alt="FIC" src="{{.urlbase}}img/fic.png" style="height: 100%">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="{{.urlbase}}teams">Équipes</a></li>
|
||||
<li><a href="{{.urlbase}}themes">Thèmes</a></li>
|
||||
<li><a href="{{.urlbase}}exercices">Exercices</a></li>
|
||||
<li><a href="{{.urlbase}}events">Événements</a></li>
|
||||
</ul>
|
||||
|
||||
<p id="clock" class="navbar-text navbar-right" ng-controller="CountdownController">
|
||||
<span id="hours">{{"{{ time.hours | time }}"}}</span>
|
||||
<span class="point">:</span>
|
||||
<span id="min">{{"{{ time.minutes | time }}"}}</span>
|
||||
<span class="point">:</span>
|
||||
<span id="sec">{{"{{ time.seconds | time }}"}}</span>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12" ng-view></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
<script src="/js/angular.min.js"></script>
|
||||
<script src="{{.urlbase}}js/angular-resource.min.js"></script>
|
||||
<script src="/js/angular-route.min.js"></script>
|
||||
<script src="{{.urlbase}}js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
|
@ -6,35 +6,36 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/api"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var PKIDir string
|
||||
var SubmissionDir string
|
||||
var BaseURL string
|
||||
var CloudDAVBase string
|
||||
var CloudUsername string
|
||||
var CloudPassword string
|
||||
|
||||
func main() {
|
||||
var bind = flag.String("bind", "0.0.0.0:8081", "Bind port/socket")
|
||||
var staticDir string
|
||||
var bind = flag.String("bind", "127.0.0.1:8081", "Bind port/socket")
|
||||
var dsn = flag.String("dsn", "fic:fic@/fic", "DSN to connect to the MySQL server")
|
||||
flag.StringVar(&BaseURL, "baseurl", "http://fic.srs.epita.fr/", "URL prepended to each URL")
|
||||
var baseURL = flag.String("baseurl", "/", "URL prepended to each URL")
|
||||
flag.StringVar(&SubmissionDir, "submission", "./submissions/", "Base directory where save submissions")
|
||||
flag.StringVar(&PKIDir, "pki", "./pki/", "Base directory where found PKI scripts")
|
||||
flag.StringVar(&fic.FilesDir, "files", "./FILES/", "Base directory where found challenges files, local part")
|
||||
flag.StringVar(&CloudDAVBase, "clouddav", "https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016",
|
||||
flag.StringVar(&api.CloudDAVBase, "clouddav", "https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016",
|
||||
"Base directory where found challenges files, cloud part")
|
||||
flag.StringVar(&CloudUsername, "clouduser", "fic", "Username used to sync")
|
||||
flag.StringVar(&CloudPassword, "cloudpass", "", "Password used to sync")
|
||||
flag.StringVar(&api.CloudUsername, "clouduser", "fic", "Username used to sync")
|
||||
flag.StringVar(&api.CloudPassword, "cloudpass", "", "Password used to sync")
|
||||
flag.Parse()
|
||||
|
||||
var staticDir string
|
||||
log.SetPrefix("[admin] ")
|
||||
|
||||
var err error
|
||||
log.Println("Checking paths...")
|
||||
if staticDir, err = filepath.Abs("./static/"); err != nil {
|
||||
if staticDir, err = filepath.Abs(staticDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if fic.FilesDir, err = filepath.Abs(fic.FilesDir); err != nil {
|
||||
|
@ -61,17 +62,20 @@ func main() {
|
|||
log.Fatal("Cannot create database: ", err)
|
||||
}
|
||||
|
||||
log.Println("Changing base url...")
|
||||
if file, err := os.OpenFile(path.Join(staticDir, "index.html"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0644)); err != nil {
|
||||
log.Println("Unable to open index.html: ", err)
|
||||
} else if indexTmpl, err := template.New("index").Parse(indextpl); err != nil {
|
||||
log.Println("Cannot create template: ", err)
|
||||
} else if err := indexTmpl.Execute(file, map[string]string{"urlbase": path.Clean(path.Join(*baseURL, "nuke"))[:len(path.Clean(path.Join(*baseURL, "nuke"))) - 4]}); err != nil {
|
||||
log.Println("An error occurs during template execution: ", err)
|
||||
}
|
||||
|
||||
|
||||
os.Chdir(PKIDir)
|
||||
|
||||
log.Println("Registering handlers...")
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/api/", http.StripPrefix("/api", ApiHandler()))
|
||||
mux.Handle("/teams/", StaticHandler(staticDir))
|
||||
mux.Handle("/themes/", StaticHandler(staticDir))
|
||||
mux.Handle("/", http.FileServer(http.Dir(staticDir)))
|
||||
|
||||
log.Println(fmt.Sprintf("Ready, listening on %s", *bind))
|
||||
if err := http.ListenAndServe(*bind, mux); err != nil {
|
||||
if err := http.ListenAndServe(*bind, http.StripPrefix(*baseURL, api.Router())); err != nil {
|
||||
log.Fatal("Unable to listen and serve: ", err)
|
||||
}
|
||||
}
|
||||
|
|
BIN
admin/static/img/epita.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
admin/static/img/fic.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
admin/static/img/srs.png
Normal file
After Width: | Height: | Size: 29 KiB |
|
@ -2,15 +2,40 @@
|
|||
<html ng-app="FICApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge Forensic FIC 2016 - Administration</title>
|
||||
<title>Challenge Forensic - Administration</title>
|
||||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||||
<base href="/">
|
||||
<script src="/js/d3.v3.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1>Challenge FIC Epita!</h1>
|
||||
<nav class="navbar navbar-inverse navbar-static-top">
|
||||
<div class="container">
|
||||
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img alt="FIC" src="/img/fic.png" style="height: 100%">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="/teams">Équipes</a></li>
|
||||
<li><a href="/themes">Thèmes</a></li>
|
||||
<li><a href="/exercices">Exercices</a></li>
|
||||
<li><a href="/events">Événements</a></li>
|
||||
</ul>
|
||||
|
||||
<p id="clock" class="navbar-text navbar-right" ng-controller="CountdownController">
|
||||
<span id="hours">{{ time.hours | time }}</span>
|
||||
<span class="point">:</span>
|
||||
<span id="min">{{ time.minutes | time }}</span>
|
||||
<span class="point">:</span>
|
||||
<span id="sec">{{ time.seconds | time }}</span>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12" ng-view></div>
|
||||
</div>
|
||||
|
|
15
admin/static/js/angular-route.min.js
vendored
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
AngularJS v1.4.8
|
||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(p,c,C){'use strict';function v(r,h,g){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,f,b,d,y){function z(){k&&(g.cancel(k),k=null);l&&(l.$destroy(),l=null);m&&(k=g.leave(m),k.then(function(){k=null}),m=null)}function x(){var b=r.current&&r.current.locals;if(c.isDefined(b&&b.$template)){var b=a.$new(),d=r.current;m=y(b,function(b){g.enter(b,null,m||f).then(function(){!c.isDefined(t)||t&&!a.$eval(t)||h()});z()});l=d.scope=b;l.$emit("$viewContentLoaded");
|
||||
l.$eval(w)}else z()}var l,m,k,t=b.autoscroll,w=b.onload||"";a.$on("$routeChangeSuccess",x);x()}}}function A(c,h,g){return{restrict:"ECA",priority:-400,link:function(a,f){var b=g.current,d=b.locals;f.html(d.$template);var y=c(f.contents());b.controller&&(d.$scope=a,d=h(b.controller,d),b.controllerAs&&(a[b.controllerAs]=d),f.data("$ngControllerController",d),f.children().data("$ngControllerController",d));y(a)}}}p=c.module("ngRoute",["ng"]).provider("$route",function(){function r(a,f){return c.extend(Object.create(a),
|
||||
f)}function h(a,c){var b=c.caseInsensitiveMatch,d={originalPath:a,regexp:a},g=d.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,c,b,d){a="?"===d?d:null;d="*"===d?d:null;g.push({name:b,optional:!!a});c=c||"";return""+(a?"":c)+"(?:"+(a?c:"")+(d&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");d.regexp=new RegExp("^"+a+"$",b?"i":"");return d}var g={};this.when=function(a,f){var b=c.copy(f);c.isUndefined(b.reloadOnSearch)&&(b.reloadOnSearch=!0);
|
||||
c.isUndefined(b.caseInsensitiveMatch)&&(b.caseInsensitiveMatch=this.caseInsensitiveMatch);g[a]=c.extend(b,a&&h(a,b));if(a){var d="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";g[d]=c.extend({redirectTo:a},h(d,b))}return this};this.caseInsensitiveMatch=!1;this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce",function(a,f,b,d,h,p,x){function l(b){var e=s.current;
|
||||
(v=(n=k())&&e&&n.$$route===e.$$route&&c.equals(n.pathParams,e.pathParams)&&!n.reloadOnSearch&&!w)||!e&&!n||a.$broadcast("$routeChangeStart",n,e).defaultPrevented&&b&&b.preventDefault()}function m(){var u=s.current,e=n;if(v)u.params=e.params,c.copy(u.params,b),a.$broadcast("$routeUpdate",u);else if(e||u)w=!1,(s.current=e)&&e.redirectTo&&(c.isString(e.redirectTo)?f.path(t(e.redirectTo,e.params)).search(e.params).replace():f.url(e.redirectTo(e.pathParams,f.path(),f.search())).replace()),d.when(e).then(function(){if(e){var a=
|
||||
c.extend({},e.resolve),b,f;c.forEach(a,function(b,e){a[e]=c.isString(b)?h.get(b):h.invoke(b,null,null,e)});c.isDefined(b=e.template)?c.isFunction(b)&&(b=b(e.params)):c.isDefined(f=e.templateUrl)&&(c.isFunction(f)&&(f=f(e.params)),c.isDefined(f)&&(e.loadedTemplateUrl=x.valueOf(f),b=p(f)));c.isDefined(b)&&(a.$template=b);return d.all(a)}}).then(function(f){e==s.current&&(e&&(e.locals=f,c.copy(e.params,b)),a.$broadcast("$routeChangeSuccess",e,u))},function(b){e==s.current&&a.$broadcast("$routeChangeError",
|
||||
e,u,b)})}function k(){var a,b;c.forEach(g,function(d,g){var q;if(q=!b){var h=f.path();q=d.keys;var l={};if(d.regexp)if(h=d.regexp.exec(h)){for(var k=1,m=h.length;k<m;++k){var n=q[k-1],p=h[k];n&&p&&(l[n.name]=p)}q=l}else q=null;else q=null;q=a=q}q&&(b=r(d,{params:c.extend({},f.search(),a),pathParams:a}),b.$$route=d)});return b||g[null]&&r(g[null],{params:{},pathParams:{}})}function t(a,b){var d=[];c.forEach((a||"").split(":"),function(a,c){if(0===c)d.push(a);else{var f=a.match(/(\w+)(?:[?*])?(.*)/),
|
||||
g=f[1];d.push(b[g]);d.push(f[2]||"");delete b[g]}});return d.join("")}var w=!1,n,v,s={routes:g,reload:function(){w=!0;a.$evalAsync(function(){l();m()})},updateParams:function(a){if(this.current&&this.current.$$route)a=c.extend({},this.current.params,a),f.path(t(this.current.$$route.originalPath,a)),f.search(a);else throw B("norout");}};a.$on("$locationChangeStart",l);a.$on("$locationChangeSuccess",m);return s}]});var B=c.$$minErr("ngRoute");p.provider("$routeParams",function(){this.$get=function(){return{}}});
|
||||
p.directive("ngView",v);p.directive("ngView",A);v.$inject=["$route","$anchorScroll","$animate"];A.$inject=["$compile","$controller","$route"]})(window,window.angular);
|
||||
//# sourceMappingURL=angular-route.min.js.map
|
1
admin/static/js/angular-route.min.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../frontend/static/js/angular-route.min.js
|
295
admin/static/js/angular.min.js
vendored
|
@ -1,295 +0,0 @@
|
|||
/*
|
||||
AngularJS v1.4.8
|
||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(S,X,u){'use strict';function G(a){return function(){var b=arguments[0],d;d="["+(a?a+":":"")+b+"] http://errors.angularjs.org/1.4.8/"+(a?a+"/":"")+b;for(b=1;b<arguments.length;b++){d=d+(1==b?"?":"&")+"p"+(b-1)+"=";var c=encodeURIComponent,e;e=arguments[b];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;d+=c(e)}return Error(d)}}function za(a){if(null==a||Xa(a))return!1;if(I(a)||E(a)||B&&a instanceof B)return!0;
|
||||
var b="length"in Object(a)&&a.length;return Q(b)&&(0<=b&&b-1 in a||"function"==typeof a.item)}function n(a,b,d){var c,e;if(a)if(z(a))for(c in a)"prototype"==c||"length"==c||"name"==c||a.hasOwnProperty&&!a.hasOwnProperty(c)||b.call(d,a[c],c,a);else if(I(a)||za(a)){var f="object"!==typeof a;c=0;for(e=a.length;c<e;c++)(f||c in a)&&b.call(d,a[c],c,a)}else if(a.forEach&&a.forEach!==n)a.forEach(b,d,a);else if(nc(a))for(c in a)b.call(d,a[c],c,a);else if("function"===typeof a.hasOwnProperty)for(c in a)a.hasOwnProperty(c)&&
|
||||
b.call(d,a[c],c,a);else for(c in a)qa.call(a,c)&&b.call(d,a[c],c,a);return a}function oc(a,b,d){for(var c=Object.keys(a).sort(),e=0;e<c.length;e++)b.call(d,a[c[e]],c[e]);return c}function pc(a){return function(b,d){a(d,b)}}function Td(){return++nb}function Mb(a,b,d){for(var c=a.$$hashKey,e=0,f=b.length;e<f;++e){var g=b[e];if(H(g)||z(g))for(var h=Object.keys(g),k=0,l=h.length;k<l;k++){var m=h[k],r=g[m];d&&H(r)?da(r)?a[m]=new Date(r.valueOf()):Ma(r)?a[m]=new RegExp(r):r.nodeName?a[m]=r.cloneNode(!0):
|
||||
Nb(r)?a[m]=r.clone():(H(a[m])||(a[m]=I(r)?[]:{}),Mb(a[m],[r],!0)):a[m]=r}}c?a.$$hashKey=c:delete a.$$hashKey;return a}function M(a){return Mb(a,ra.call(arguments,1),!1)}function Ud(a){return Mb(a,ra.call(arguments,1),!0)}function ea(a){return parseInt(a,10)}function Ob(a,b){return M(Object.create(a),b)}function x(){}function Ya(a){return a}function na(a){return function(){return a}}function qc(a){return z(a.toString)&&a.toString!==sa}function q(a){return"undefined"===typeof a}function y(a){return"undefined"!==
|
||||
typeof a}function H(a){return null!==a&&"object"===typeof a}function nc(a){return null!==a&&"object"===typeof a&&!rc(a)}function E(a){return"string"===typeof a}function Q(a){return"number"===typeof a}function da(a){return"[object Date]"===sa.call(a)}function z(a){return"function"===typeof a}function Ma(a){return"[object RegExp]"===sa.call(a)}function Xa(a){return a&&a.window===a}function Za(a){return a&&a.$evalAsync&&a.$watch}function $a(a){return"boolean"===typeof a}function sc(a){return a&&Q(a.length)&&
|
||||
Vd.test(sa.call(a))}function Nb(a){return!(!a||!(a.nodeName||a.prop&&a.attr&&a.find))}function Wd(a){var b={};a=a.split(",");var d;for(d=0;d<a.length;d++)b[a[d]]=!0;return b}function ta(a){return F(a.nodeName||a[0]&&a[0].nodeName)}function ab(a,b){var d=a.indexOf(b);0<=d&&a.splice(d,1);return d}function bb(a,b){function d(a,b){var d=b.$$hashKey,e;if(I(a)){e=0;for(var f=a.length;e<f;e++)b.push(c(a[e]))}else if(nc(a))for(e in a)b[e]=c(a[e]);else if(a&&"function"===typeof a.hasOwnProperty)for(e in a)a.hasOwnProperty(e)&&
|
||||
(b[e]=c(a[e]));else for(e in a)qa.call(a,e)&&(b[e]=c(a[e]));d?b.$$hashKey=d:delete b.$$hashKey;return b}function c(a){if(!H(a))return a;var b=e.indexOf(a);if(-1!==b)return f[b];if(Xa(a)||Za(a))throw Aa("cpws");var b=!1,c;I(a)?(c=[],b=!0):sc(a)?c=new a.constructor(a):da(a)?c=new Date(a.getTime()):Ma(a)?(c=new RegExp(a.source,a.toString().match(/[^\/]*$/)[0]),c.lastIndex=a.lastIndex):z(a.cloneNode)?c=a.cloneNode(!0):(c=Object.create(rc(a)),b=!0);e.push(a);f.push(c);return b?d(a,c):c}var e=[],f=[];if(b){if(sc(b))throw Aa("cpta");
|
||||
if(a===b)throw Aa("cpi");I(b)?b.length=0:n(b,function(a,c){"$$hashKey"!==c&&delete b[c]});e.push(a);f.push(b);return d(a,b)}return c(a)}function ia(a,b){if(I(a)){b=b||[];for(var d=0,c=a.length;d<c;d++)b[d]=a[d]}else if(H(a))for(d in b=b||{},a)if("$"!==d.charAt(0)||"$"!==d.charAt(1))b[d]=a[d];return b||a}function ma(a,b){if(a===b)return!0;if(null===a||null===b)return!1;if(a!==a&&b!==b)return!0;var d=typeof a,c;if(d==typeof b&&"object"==d)if(I(a)){if(!I(b))return!1;if((d=a.length)==b.length){for(c=
|
||||
0;c<d;c++)if(!ma(a[c],b[c]))return!1;return!0}}else{if(da(a))return da(b)?ma(a.getTime(),b.getTime()):!1;if(Ma(a))return Ma(b)?a.toString()==b.toString():!1;if(Za(a)||Za(b)||Xa(a)||Xa(b)||I(b)||da(b)||Ma(b))return!1;d=$();for(c in a)if("$"!==c.charAt(0)&&!z(a[c])){if(!ma(a[c],b[c]))return!1;d[c]=!0}for(c in b)if(!(c in d)&&"$"!==c.charAt(0)&&y(b[c])&&!z(b[c]))return!1;return!0}return!1}function cb(a,b,d){return a.concat(ra.call(b,d))}function tc(a,b){var d=2<arguments.length?ra.call(arguments,2):
|
||||
[];return!z(b)||b instanceof RegExp?b:d.length?function(){return arguments.length?b.apply(a,cb(d,arguments,0)):b.apply(a,d)}:function(){return arguments.length?b.apply(a,arguments):b.call(a)}}function Xd(a,b){var d=b;"string"===typeof a&&"$"===a.charAt(0)&&"$"===a.charAt(1)?d=u:Xa(b)?d="$WINDOW":b&&X===b?d="$DOCUMENT":Za(b)&&(d="$SCOPE");return d}function db(a,b){if("undefined"===typeof a)return u;Q(b)||(b=b?2:null);return JSON.stringify(a,Xd,b)}function uc(a){return E(a)?JSON.parse(a):a}function vc(a,
|
||||
b){var d=Date.parse("Jan 01, 1970 00:00:00 "+a)/6E4;return isNaN(d)?b:d}function Pb(a,b,d){d=d?-1:1;var c=vc(b,a.getTimezoneOffset());b=a;a=d*(c-a.getTimezoneOffset());b=new Date(b.getTime());b.setMinutes(b.getMinutes()+a);return b}function ua(a){a=B(a).clone();try{a.empty()}catch(b){}var d=B("<div>").append(a).html();try{return a[0].nodeType===Na?F(d):d.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+F(b)})}catch(c){return F(d)}}function wc(a){try{return decodeURIComponent(a)}catch(b){}}
|
||||
function xc(a){var b={};n((a||"").split("&"),function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=wc(e),y(e)&&(f=y(f)?wc(f):!0,qa.call(b,e)?I(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function Qb(a){var b=[];n(a,function(a,c){I(a)?n(a,function(a){b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))}):b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))});return b.length?b.join("&"):""}function ob(a){return ja(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,
|
||||
"=").replace(/%2B/gi,"+")}function ja(a,b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function Yd(a,b){var d,c,e=Oa.length;for(c=0;c<e;++c)if(d=Oa[c]+b,E(d=a.getAttribute(d)))return d;return null}function Zd(a,b){var d,c,e={};n(Oa,function(b){b+="app";!d&&a.hasAttribute&&a.hasAttribute(b)&&(d=a,c=a.getAttribute(b))});n(Oa,function(b){b+="app";var e;!d&&(e=a.querySelector("["+b.replace(":",
|
||||
"\\:")+"]"))&&(d=e,c=e.getAttribute(b))});d&&(e.strictDi=null!==Yd(d,"strict-di"),b(d,c?[c]:[],e))}function yc(a,b,d){H(d)||(d={});d=M({strictDi:!1},d);var c=function(){a=B(a);if(a.injector()){var c=a[0]===X?"document":ua(a);throw Aa("btstrpd",c.replace(/</,"<").replace(/>/,">"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);b.unshift("ng");c=eb(b,d.strictDi);c.invoke(["$rootScope",
|
||||
"$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;S&&e.test(S.name)&&(d.debugInfoEnabled=!0,S.name=S.name.replace(e,""));if(S&&!f.test(S.name))return c();S.name=S.name.replace(f,"");fa.resumeBootstrap=function(a){n(a,function(a){b.push(a)});return c()};z(fa.resumeDeferredBootstrap)&&fa.resumeDeferredBootstrap()}function $d(){S.name="NG_ENABLE_DEBUG_INFO!"+S.name;S.location.reload()}
|
||||
function ae(a){a=fa.element(a).injector();if(!a)throw Aa("test");return a.get("$$testability")}function zc(a,b){b=b||"_";return a.replace(be,function(a,c){return(c?b:"")+a.toLowerCase()})}function ce(){var a;if(!Ac){var b=pb();(oa=q(b)?S.jQuery:b?S[b]:u)&&oa.fn.on?(B=oa,M(oa.fn,{scope:Pa.scope,isolateScope:Pa.isolateScope,controller:Pa.controller,injector:Pa.injector,inheritedData:Pa.inheritedData}),a=oa.cleanData,oa.cleanData=function(b){var c;if(Rb)Rb=!1;else for(var e=0,f;null!=(f=b[e]);e++)(c=
|
||||
oa._data(f,"events"))&&c.$destroy&&oa(f).triggerHandler("$destroy");a(b)}):B=N;fa.element=B;Ac=!0}}function qb(a,b,d){if(!a)throw Aa("areq",b||"?",d||"required");return a}function Qa(a,b,d){d&&I(a)&&(a=a[a.length-1]);qb(z(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Ra(a,b){if("hasOwnProperty"===a)throw Aa("badname",b);}function Bc(a,b,d){if(!b)return a;b=b.split(".");for(var c,e=a,f=b.length,g=0;g<f;g++)c=b[g],a&&(a=(e=a)[c]);return!d&&
|
||||
z(a)?tc(e,a):a}function rb(a){for(var b=a[0],d=a[a.length-1],c,e=1;b!==d&&(b=b.nextSibling);e++)if(c||a[e]!==b)c||(c=B(ra.call(a,0,e))),c.push(b);return c||a}function $(){return Object.create(null)}function de(a){function b(a,b,c){return a[b]||(a[b]=c())}var d=G("$injector"),c=G("ng");a=b(a,"angular",Object);a.$$minErr=a.$$minErr||G;return b(a,"module",function(){var a={};return function(f,g,h){if("hasOwnProperty"===f)throw c("badname","module");g&&a.hasOwnProperty(f)&&(a[f]=null);return b(a,f,function(){function a(b,
|
||||
d,e,f){f||(f=c);return function(){f[e||"push"]([b,d,arguments]);return v}}function b(a,d){return function(b,e){e&&z(e)&&(e.$$moduleName=f);c.push([a,d,arguments]);return v}}if(!g)throw d("nomod",f);var c=[],e=[],t=[],A=a("$injector","invoke","push",e),v={_invokeQueue:c,_configBlocks:e,_runBlocks:t,requires:g,name:f,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),decorator:b("$provide",
|
||||
"decorator"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),config:A,run:function(a){t.push(a);return this}};h&&A(h);return v})}})}function ee(a){M(a,{bootstrap:yc,copy:bb,extend:M,merge:Ud,equals:ma,element:B,forEach:n,injector:eb,noop:x,bind:tc,toJson:db,fromJson:uc,identity:Ya,isUndefined:q,isDefined:y,isString:E,isFunction:z,isObject:H,isNumber:Q,isElement:Nb,isArray:I,
|
||||
version:fe,isDate:da,lowercase:F,uppercase:sb,callbacks:{counter:0},getTestability:ae,$$minErr:G,$$csp:Ba,reloadWithDebugInfo:$d});Sb=de(S);Sb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:ge});a.provider("$compile",Cc).directive({a:he,input:Dc,textarea:Dc,form:ie,script:je,select:ke,style:le,option:me,ngBind:ne,ngBindHtml:oe,ngBindTemplate:pe,ngClass:qe,ngClassEven:re,ngClassOdd:se,ngCloak:te,ngController:ue,ngForm:ve,ngHide:we,ngIf:xe,ngInclude:ye,ngInit:ze,ngNonBindable:Ae,
|
||||
ngPluralize:Be,ngRepeat:Ce,ngShow:De,ngStyle:Ee,ngSwitch:Fe,ngSwitchWhen:Ge,ngSwitchDefault:He,ngOptions:Ie,ngTransclude:Je,ngModel:Ke,ngList:Le,ngChange:Me,pattern:Ec,ngPattern:Ec,required:Fc,ngRequired:Fc,minlength:Gc,ngMinlength:Gc,maxlength:Hc,ngMaxlength:Hc,ngValue:Ne,ngModelOptions:Oe}).directive({ngInclude:Pe}).directive(tb).directive(Ic);a.provider({$anchorScroll:Qe,$animate:Re,$animateCss:Se,$$animateQueue:Te,$$AnimateRunner:Ue,$browser:Ve,$cacheFactory:We,$controller:Xe,$document:Ye,$exceptionHandler:Ze,
|
||||
$filter:Jc,$$forceReflow:$e,$interpolate:af,$interval:bf,$http:cf,$httpParamSerializer:df,$httpParamSerializerJQLike:ef,$httpBackend:ff,$xhrFactory:gf,$location:hf,$log:jf,$parse:kf,$rootScope:lf,$q:mf,$$q:nf,$sce:of,$sceDelegate:pf,$sniffer:qf,$templateCache:rf,$templateRequest:sf,$$testability:tf,$timeout:uf,$window:vf,$$rAF:wf,$$jqLite:xf,$$HashMap:yf,$$cookieReader:zf})}])}function fb(a){return a.replace(Af,function(a,d,c,e){return e?c.toUpperCase():c}).replace(Bf,"Moz$1")}function Kc(a){a=a.nodeType;
|
||||
return 1===a||!a||9===a}function Lc(a,b){var d,c,e=b.createDocumentFragment(),f=[];if(Tb.test(a)){d=d||e.appendChild(b.createElement("div"));c=(Cf.exec(a)||["",""])[1].toLowerCase();c=ka[c]||ka._default;d.innerHTML=c[1]+a.replace(Df,"<$1></$2>")+c[2];for(c=c[0];c--;)d=d.lastChild;f=cb(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";n(f,function(a){e.appendChild(a)});return e}function N(a){if(a instanceof N)return a;var b;E(a)&&(a=U(a),
|
||||
b=!0);if(!(this instanceof N)){if(b&&"<"!=a.charAt(0))throw Ub("nosel");return new N(a)}if(b){b=X;var d;a=(d=Ef.exec(a))?[b.createElement(d[1])]:(d=Lc(a,b))?d.childNodes:[]}Mc(this,a)}function Vb(a){return a.cloneNode(!0)}function ub(a,b){b||vb(a);if(a.querySelectorAll)for(var d=a.querySelectorAll("*"),c=0,e=d.length;c<e;c++)vb(d[c])}function Nc(a,b,d,c){if(y(c))throw Ub("offargs");var e=(c=wb(a))&&c.events,f=c&&c.handle;if(f)if(b){var g=function(b){var c=e[b];y(d)&&ab(c||[],d);y(d)&&c&&0<c.length||
|
||||
(a.removeEventListener(b,f,!1),delete e[b])};n(b.split(" "),function(a){g(a);xb[a]&&g(xb[a])})}else for(b in e)"$destroy"!==b&&a.removeEventListener(b,f,!1),delete e[b]}function vb(a,b){var d=a.ng339,c=d&&gb[d];c&&(b?delete c.data[b]:(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),Nc(a)),delete gb[d],a.ng339=u))}function wb(a,b){var d=a.ng339,d=d&&gb[d];b&&!d&&(a.ng339=d=++Ff,d=gb[d]={events:{},data:{},handle:u});return d}function Wb(a,b,d){if(Kc(a)){var c=y(d),e=!c&&b&&!H(b),f=!b;a=(a=wb(a,
|
||||
!e))&&a.data;if(c)a[b]=d;else{if(f)return a;if(e)return a&&a[b];M(a,b)}}}function yb(a,b){return a.getAttribute?-1<(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+b+" "):!1}function zb(a,b){b&&a.setAttribute&&n(b.split(" "),function(b){a.setAttribute("class",U((" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+U(b)+" "," ")))})}function Ab(a,b){if(b&&a.setAttribute){var d=(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");n(b.split(" "),
|
||||
function(a){a=U(a);-1===d.indexOf(" "+a+" ")&&(d+=a+" ")});a.setAttribute("class",U(d))}}function Mc(a,b){if(b)if(b.nodeType)a[a.length++]=b;else{var d=b.length;if("number"===typeof d&&b.window!==b){if(d)for(var c=0;c<d;c++)a[a.length++]=b[c]}else a[a.length++]=b}}function Oc(a,b){return Bb(a,"$"+(b||"ngController")+"Controller")}function Bb(a,b,d){9==a.nodeType&&(a=a.documentElement);for(b=I(b)?b:[b];a;){for(var c=0,e=b.length;c<e;c++)if(y(d=B.data(a,b[c])))return d;a=a.parentNode||11===a.nodeType&&
|
||||
a.host}}function Pc(a){for(ub(a,!0);a.firstChild;)a.removeChild(a.firstChild)}function Xb(a,b){b||ub(a);var d=a.parentNode;d&&d.removeChild(a)}function Gf(a,b){b=b||S;if("complete"===b.document.readyState)b.setTimeout(a);else B(b).on("load",a)}function Qc(a,b){var d=Cb[b.toLowerCase()];return d&&Rc[ta(a)]&&d}function Hf(a,b){var d=function(c,d){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=b[d||c.type],g=f?f.length:0;if(g){if(q(c.immediatePropagationStopped)){var h=c.stopImmediatePropagation;
|
||||
c.stopImmediatePropagation=function(){c.immediatePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};var k=f.specialHandlerWrapper||If;1<g&&(f=ia(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||k(a,c,f[l])}};d.elem=a;return d}function If(a,b,d){d.call(a,b)}function Jf(a,b,d){var c=b.relatedTarget;c&&(c===a||Kf.call(a,c))||d.call(a,b)}function xf(){this.$get=function(){return M(N,
|
||||
{hasClass:function(a,b){a.attr&&(a=a[0]);return yb(a,b)},addClass:function(a,b){a.attr&&(a=a[0]);return Ab(a,b)},removeClass:function(a,b){a.attr&&(a=a[0]);return zb(a,b)}})}}function Ca(a,b){var d=a&&a.$$hashKey;if(d)return"function"===typeof d&&(d=a.$$hashKey()),d;d=typeof a;return d="function"==d||"object"==d&&null!==a?a.$$hashKey=d+":"+(b||Td)():d+":"+a}function Sa(a,b){if(b){var d=0;this.nextUid=function(){return++d}}n(a,this.put,this)}function Lf(a){return(a=a.toString().replace(Sc,"").match(Tc))?
|
||||
"function("+(a[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function eb(a,b){function d(a){return function(b,c){if(H(b))n(b,pc(a));else return a(b,c)}}function c(a,b){Ra(a,"service");if(z(b)||I(b))b=t.instantiate(b);if(!b.$get)throw Da("pget",a);return r[a+"Provider"]=b}function e(a,b){return function(){var c=v.invoke(b,this);if(q(c))throw Da("undef",a);return c}}function f(a,b,d){return c(a,{$get:!1!==d?e(a,b):b})}function g(a){qb(q(a)||I(a),"modulesToLoad","not an array");var b=[],c;n(a,function(a){function d(a){var b,
|
||||
c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=t.get(e[0]);f[e[1]].apply(f,e[2])}}if(!m.get(a)){m.put(a,!0);try{E(a)?(c=Sb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):z(a)?b.push(t.invoke(a)):I(a)?b.push(t.invoke(a)):Qa(a,"module")}catch(e){throw I(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Da("modulerr",a,e.stack||e.message||e);}}});return b}function h(a,c){function d(b,e){if(a.hasOwnProperty(b)){if(a[b]===
|
||||
k)throw Da("cdep",b+" <- "+l.join(" <- "));return a[b]}try{return l.unshift(b),a[b]=k,a[b]=c(b,e)}catch(f){throw a[b]===k&&delete a[b],f;}finally{l.shift()}}function e(a,c,f,g){"string"===typeof f&&(g=f,f=null);var h=[],k=eb.$$annotate(a,b,g),l,m,t;m=0;for(l=k.length;m<l;m++){t=k[m];if("string"!==typeof t)throw Da("itkn",t);h.push(f&&f.hasOwnProperty(t)?f[t]:d(t,g))}I(a)&&(a=a[l]);return a.apply(c,h)}return{invoke:e,instantiate:function(a,b,c){var d=Object.create((I(a)?a[a.length-1]:a).prototype||
|
||||
null);a=e(a,d,b,c);return H(a)||z(a)?a:d},get:d,annotate:eb.$$annotate,has:function(b){return r.hasOwnProperty(b+"Provider")||a.hasOwnProperty(b)}}}b=!0===b;var k={},l=[],m=new Sa([],!0),r={$provide:{provider:d(c),factory:d(f),service:d(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:d(function(a,b){return f(a,na(b),!1)}),constant:d(function(a,b){Ra(a,"constant");r[a]=b;A[a]=b}),decorator:function(a,b){var c=t.get(a+"Provider"),d=c.$get;c.$get=function(){var a=
|
||||
v.invoke(d,c);return v.invoke(b,null,{$delegate:a})}}}},t=r.$injector=h(r,function(a,b){fa.isString(b)&&l.push(b);throw Da("unpr",l.join(" <- "));}),A={},v=A.$injector=h(A,function(a,b){var c=t.get(a+"Provider",b);return v.invoke(c.$get,c,u,a)});n(g(a),function(a){a&&v.invoke(a)});return v}function Qe(){var a=!0;this.disableAutoScrolling=function(){a=!1};this.$get=["$window","$location","$rootScope",function(b,d,c){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===ta(a))return b=
|
||||
a,!0});return b}function f(a){if(a){a.scrollIntoView();var c;c=g.yOffset;z(c)?c=c():Nb(c)?(c=c[0],c="fixed"!==b.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):Q(c)||(c=0);c&&(a=a.getBoundingClientRect().top,b.scrollBy(0,a-c))}else b.scrollTo(0,0)}function g(a){a=E(a)?a:d.hash();var b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=b.document;a&&c.$watch(function(){return d.hash()},function(a,b){a===b&&""===a||Gf(function(){c.$evalAsync(g)})});
|
||||
return g}]}function hb(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;I(a)&&(a=a.join(" "));I(b)&&(b=b.join(" "));return a+" "+b}function Mf(a){E(a)&&(a=a.split(" "));var b=$();n(a,function(a){a.length&&(b[a]=!0)});return b}function Ea(a){return H(a)?a:{}}function Nf(a,b,d,c){function e(a){try{a.apply(null,ra.call(arguments,1))}finally{if(v--,0===v)for(;T.length;)try{T.pop()()}catch(b){d.error(b)}}}function f(){L=null;g();h()}function g(){a:{try{p=m.state;break a}catch(a){}p=void 0}p=q(p)?
|
||||
null:p;ma(p,J)&&(p=J);J=p}function h(){if(w!==k.url()||C!==p)w=k.url(),C=p,n(aa,function(a){a(k.url(),p)})}var k=this,l=a.location,m=a.history,r=a.setTimeout,t=a.clearTimeout,A={};k.isMock=!1;var v=0,T=[];k.$$completeOutstandingRequest=e;k.$$incOutstandingRequestCount=function(){v++};k.notifyWhenNoOutstandingRequests=function(a){0===v?a():T.push(a)};var p,C,w=l.href,ga=b.find("base"),L=null;g();C=p;k.url=function(b,d,e){q(e)&&(e=null);l!==a.location&&(l=a.location);m!==a.history&&(m=a.history);if(b){var f=
|
||||
C===e;if(w===b&&(!c.history||f))return k;var h=w&&Fa(w)===Fa(b);w=b;C=e;if(!c.history||h&&f){if(!h||L)L=b;d?l.replace(b):h?(d=l,e=b.indexOf("#"),e=-1===e?"":b.substr(e),d.hash=e):l.href=b;l.href!==b&&(L=b)}else m[d?"replaceState":"pushState"](e,"",b),g(),C=p;return k}return L||l.href.replace(/%27/g,"'")};k.state=function(){return p};var aa=[],D=!1,J=null;k.onUrlChange=function(b){if(!D){if(c.history)B(a).on("popstate",f);B(a).on("hashchange",f);D=!0}aa.push(b);return b};k.$$applicationDestroyed=function(){B(a).off("hashchange popstate",
|
||||
f)};k.$$checkUrlChange=h;k.baseHref=function(){var a=ga.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};k.defer=function(a,b){var c;v++;c=r(function(){delete A[c];e(a)},b||0);A[c]=!0;return c};k.defer.cancel=function(a){return A[a]?(delete A[a],t(a),e(x),!0):!1}}function Ve(){this.$get=["$window","$log","$sniffer","$document",function(a,b,d,c){return new Nf(a,c,b,d)}]}function We(){this.$get=function(){function a(a,c){function e(a){a!=r&&(t?t==a&&(t=a.n):t=a,f(a.n,a.p),f(a,r),r=a,
|
||||
r.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(a in b)throw G("$cacheFactory")("iid",a);var g=0,h=M({},c,{id:a}),k=$(),l=c&&c.capacity||Number.MAX_VALUE,m=$(),r=null,t=null;return b[a]={put:function(a,b){if(!q(b)){if(l<Number.MAX_VALUE){var c=m[a]||(m[a]={key:a});e(c)}a in k||g++;k[a]=b;g>l&&this.remove(t.key);return b}},get:function(a){if(l<Number.MAX_VALUE){var b=m[a];if(!b)return;e(b)}return k[a]},remove:function(a){if(l<Number.MAX_VALUE){var b=m[a];if(!b)return;b==r&&(r=b.p);b==t&&
|
||||
(t=b.n);f(b.n,b.p);delete m[a]}a in k&&(delete k[a],g--)},removeAll:function(){k=$();g=0;m=$();r=t=null},destroy:function(){m=h=k=null;delete b[a]},info:function(){return M({},h,{size:g})}}}var b={};a.info=function(){var a={};n(b,function(b,e){a[e]=b.info()});return a};a.get=function(a){return b[a]};return a}}function rf(){this.$get=["$cacheFactory",function(a){return a("templates")}]}function Cc(a,b){function d(a,b,c){var d=/^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/,e={};n(a,function(a,f){var g=a.match(d);
|
||||
if(!g)throw ha("iscp",b,f,a,c?"controller bindings definition":"isolate scope definition");e[f]={mode:g[1][0],collection:"*"===g[2],optional:"?"===g[3],attrName:g[4]||f}});return e}function c(a){var b=a.charAt(0);if(!b||b!==F(b))throw ha("baddir",a);if(a!==a.trim())throw ha("baddir",a);}var e={},f=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,g=/(([\w\-]+)(?:\:([^;]+))?;?)/,h=Wd("ngSrc,ngSrcset,src,srcset"),k=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,l=/^(on[a-z]+|formaction)$/;this.directive=function t(b,f){Ra(b,"directive");
|
||||
E(b)?(c(b),qb(f,"directiveFactory"),e.hasOwnProperty(b)||(e[b]=[],a.factory(b+"Directive",["$injector","$exceptionHandler",function(a,c){var f=[];n(e[b],function(e,g){try{var h=a.invoke(e);z(h)?h={compile:na(h)}:!h.compile&&h.link&&(h.compile=na(h.link));h.priority=h.priority||0;h.index=g;h.name=h.name||b;h.require=h.require||h.controller&&h.name;h.restrict=h.restrict||"EA";var k=h,l=h,m=h.name,t={isolateScope:null,bindToController:null};H(l.scope)&&(!0===l.bindToController?(t.bindToController=d(l.scope,
|
||||
m,!0),t.isolateScope={}):t.isolateScope=d(l.scope,m,!1));H(l.bindToController)&&(t.bindToController=d(l.bindToController,m,!0));if(H(t.bindToController)){var v=l.controller,R=l.controllerAs;if(!v)throw ha("noctrl",m);var V;a:if(R&&E(R))V=R;else{if(E(v)){var n=Uc.exec(v);if(n){V=n[3];break a}}V=void 0}if(!V)throw ha("noident",m);}var s=k.$$bindings=t;H(s.isolateScope)&&(h.$$isolateBindings=s.isolateScope);h.$$moduleName=e.$$moduleName;f.push(h)}catch(u){c(u)}});return f}])),e[b].push(f)):n(b,pc(t));
|
||||
return this};this.aHrefSanitizationWhitelist=function(a){return y(a)?(b.aHrefSanitizationWhitelist(a),this):b.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(a){return y(a)?(b.imgSrcSanitizationWhitelist(a),this):b.imgSrcSanitizationWhitelist()};var m=!0;this.debugInfoEnabled=function(a){return y(a)?(m=a,this):m};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,
|
||||
b,c,d,p,C,w,ga,L,aa,D){function J(a,b){try{a.addClass(b)}catch(c){}}function K(a,b,c,d,e){a instanceof B||(a=B(a));n(a,function(b,c){b.nodeType==Na&&b.nodeValue.match(/\S+/)&&(a[c]=B(b).wrap("<span></span>").parent()[0])});var f=O(a,b,a,c,d,e);K.$$addScopeClass(a);var g=null;return function(b,c,d){qb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var h=d.parentBoundTranscludeFn,k=d.transcludeControllers;d=d.futureParentElement;h&&h.$$boundTransclude&&(h=h.$$boundTransclude);g||(g=(d=
|
||||
d&&d[0])?"foreignobject"!==ta(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html"!==g?B(Yb(g,B("<div>").append(a).html())):c?Pa.clone.call(a):a;if(k)for(var l in k)d.data("$"+l+"Controller",k[l].instance);K.$$addScopeInfo(d,b);c&&c(d,b);f&&f(b,d,d,h);return d}}function O(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,m,t,w,D;if(p)for(D=Array(c.length),m=0;m<h.length;m+=3)f=h[m],D[f]=c[f];else D=c;m=0;for(t=h.length;m<t;)k=D[h[m++]],c=h[m++],f=h[m++],c?(c.scope?(l=a.$new(),K.$$addScopeInfo(B(k),
|
||||
l)):l=a,w=c.transcludeOnThisElement?R(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?R(a,b):null,c(f,l,k,d,w)):f&&f(a,k.childNodes,u,e)}for(var h=[],k,l,m,t,p,w=0;w<a.length;w++){k=new fa;l=V(a[w],[],k,0===w?d:u,e);(f=l.length?Z(l,a[w],k,b,c,null,[],[],f):null)&&f.scope&&K.$$addScopeClass(k.$$element);k=f&&f.terminal||!(m=a[w].childNodes)||!m.length?null:O(m,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(w,f,k),t=!0,p=p||f;f=null}return t?g:null}function R(a,
|
||||
b,c){return function(d,e,f,g,h){d||(d=a.$new(!1,h),d.$$transcluded=!0);return b(d,e,{parentBoundTranscludeFn:c,transcludeControllers:f,futureParentElement:g})}}function V(a,b,c,d,e){var h=c.$attr,k;switch(a.nodeType){case 1:P(b,va(ta(a)),"E",d,e);for(var l,m,t,p=a.attributes,w=0,D=p&&p.length;w<D;w++){var K=!1,A=!1;l=p[w];k=l.name;m=U(l.value);l=va(k);if(t=ka.test(l))k=k.replace(Vc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});(l=l.match(la))&&G(l[1])&&(K=k,A=k.substr(0,k.length-
|
||||
5)+"end",k=k.substr(0,k.length-6));l=va(k.toLowerCase());h[l]=k;if(t||!c.hasOwnProperty(l))c[l]=m,Qc(a,l)&&(c[l]=!0);W(a,b,m,l,t);P(b,l,"A",d,e,K,A)}a=a.className;H(a)&&(a=a.animVal);if(E(a)&&""!==a)for(;k=g.exec(a);)l=va(k[2]),P(b,l,"C",d,e)&&(c[l]=U(k[3])),a=a.substr(k.index+k[0].length);break;case Na:if(11===Ha)for(;a.parentNode&&a.nextSibling&&a.nextSibling.nodeType===Na;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);N(b,a.nodeValue);break;case 8:try{if(k=f.exec(a.nodeValue))l=
|
||||
va(k[1]),P(b,l,"M",d,e)&&(c[l]=U(k[2]))}catch(R){}}b.sort(Ia);return b}function Ta(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ha("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return B(d)}function s(a,b,c){return function(d,e,f,g,h){e=Ta(e[0],b,c);return a(d,e,f,g,h)}}function Z(a,b,d,e,f,g,h,l,m){function t(a,b,c,d){if(a){c&&(a=s(a,c,d));a.require=q.require;a.directiveName=x;if(O===
|
||||
q||q.$$isolateScope)a=ca(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=s(b,c,d));b.require=q.require;b.directiveName=x;if(O===q||q.$$isolateScope)b=ca(b,{isolateScope:!0});l.push(b)}}function p(a,b,c,d){var e;if(E(b)){var f=b.match(k);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;e||(d="$"+b+"Controller",e=g?c.inheritedData(d):c.data(d));if(!e&&!f)throw ha("ctreq",b,a);}else if(I(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=p(a,b[g],c,d);return e||
|
||||
null}function w(a,b,c,d,e,f){var g=$(),h;for(h in d){var k=d[h],l={$scope:k===O||k.$$isolateScope?e:f,$element:a,$attrs:b,$transclude:c},m=k.controller;"@"==m&&(m=b[k.name]);l=C(m,l,!0,k.controllerAs);g[k.name]=l;aa||a.data("$"+k.name+"Controller",l.instance)}return g}function D(a,c,e,f,g){function k(a,b,c){var d;Za(a)||(c=b,b=a,a=u);aa&&(d=v);c||(c=aa?V.parent():V);return g(a,b,d,c,Ta)}var m,t,A,v,C,V,Ga;b===e?(f=d,V=d.$$element):(V=B(e),f=new fa(V,d));A=c;O?t=c.$new(!0):R&&(A=c.$parent);g&&(C=k,
|
||||
C.$$boundTransclude=g);T&&(v=w(V,f,C,T,t,c));O&&(K.$$addScopeInfo(V,t,!0,!(J&&(J===O||J===O.$$originalDirective))),K.$$addScopeClass(V,!0),t.$$isolateBindings=O.$$isolateBindings,(Ga=ba(c,f,t,t.$$isolateBindings,O))&&t.$on("$destroy",Ga));for(var n in v){Ga=T[n];var ga=v[n],L=Ga.$$bindings.bindToController;ga.identifier&&L&&(m=ba(A,f,ga.instance,L,Ga));var q=ga();q!==ga.instance&&(ga.instance=q,V.data("$"+Ga.name+"Controller",q),m&&m(),m=ba(A,f,ga.instance,L,Ga))}F=0;for(M=h.length;F<M;F++)m=h[F],
|
||||
ea(m,m.isolateScope?t:c,V,f,m.require&&p(m.directiveName,m.require,V,v),C);var Ta=c;O&&(O.template||null===O.templateUrl)&&(Ta=t);a&&a(Ta,e.childNodes,u,g);for(F=l.length-1;0<=F;F--)m=l[F],ea(m,m.isolateScope?t:c,V,f,m.require&&p(m.directiveName,m.require,V,v),C)}m=m||{};for(var A=-Number.MAX_VALUE,R=m.newScopeDirective,T=m.controllerDirectives,O=m.newIsolateScopeDirective,J=m.templateDirective,n=m.nonTlbTranscludeDirective,ga=!1,L=!1,aa=m.hasElementTranscludeDirective,Z=d.$$element=B(b),q,x,P,Ia=
|
||||
e,G,F=0,M=a.length;F<M;F++){q=a[F];var N=q.$$start,Q=q.$$end;N&&(Z=Ta(b,N,Q));P=u;if(A>q.priority)break;if(P=q.scope)q.templateUrl||(H(P)?(Ua("new/isolated scope",O||R,q,Z),O=q):Ua("new/isolated scope",O,q,Z)),R=R||q;x=q.name;!q.templateUrl&&q.controller&&(P=q.controller,T=T||$(),Ua("'"+x+"' controller",T[x],q,Z),T[x]=q);if(P=q.transclude)ga=!0,q.$$tlb||(Ua("transclusion",n,q,Z),n=q),"element"==P?(aa=!0,A=q.priority,P=Z,Z=d.$$element=B(X.createComment(" "+x+": "+d[x]+" ")),b=Z[0],Y(f,ra.call(P,0),
|
||||
b),Ia=K(P,e,A,g&&g.name,{nonTlbTranscludeDirective:n})):(P=B(Vb(b)).contents(),Z.empty(),Ia=K(P,e,u,u,{needsNewScope:q.$$isolateScope||q.$$newScope}));if(q.template)if(L=!0,Ua("template",J,q,Z),J=q,P=z(q.template)?q.template(Z,d):q.template,P=ja(P),q.replace){g=q;P=Tb.test(P)?Xc(Yb(q.templateNamespace,U(P))):[];b=P[0];if(1!=P.length||1!==b.nodeType)throw ha("tplrt",x,"");Y(f,Z,b);P={$attr:{}};var Wc=V(b,[],P),W=a.splice(F+1,a.length-(F+1));(O||R)&&y(Wc,O,R);a=a.concat(Wc).concat(W);S(d,P);M=a.length}else Z.html(P);
|
||||
if(q.templateUrl)L=!0,Ua("template",J,q,Z),J=q,q.replace&&(g=q),D=Of(a.splice(F,a.length-F),Z,d,f,ga&&Ia,h,l,{controllerDirectives:T,newScopeDirective:R!==q&&R,newIsolateScopeDirective:O,templateDirective:J,nonTlbTranscludeDirective:n}),M=a.length;else if(q.compile)try{G=q.compile(Z,d,Ia),z(G)?t(null,G,N,Q):G&&t(G.pre,G.post,N,Q)}catch(da){c(da,ua(Z))}q.terminal&&(D.terminal=!0,A=Math.max(A,q.priority))}D.scope=R&&!0===R.scope;D.transcludeOnThisElement=ga;D.templateOnThisElement=L;D.transclude=Ia;
|
||||
m.hasElementTranscludeDirective=aa;return D}function y(a,b,c){for(var d=0,e=a.length;d<e;d++)a[d]=Ob(a[d],{$$isolateScope:b,$$newScope:c})}function P(b,d,f,g,h,k,l){if(d===h)return null;h=null;if(e.hasOwnProperty(d)){var m;d=a.get(d+"Directive");for(var p=0,w=d.length;p<w;p++)try{m=d[p],(q(g)||g>m.priority)&&-1!=m.restrict.indexOf(f)&&(k&&(m=Ob(m,{$$start:k,$$end:l})),b.push(m),h=m)}catch(D){c(D)}}return h}function G(b){if(e.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,f=c.length;d<f;d++)if(b=
|
||||
c[d],b.multiElement)return!0;return!1}function S(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;n(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});n(b,function(b,f){"class"==f?(J(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function Of(a,b,c,e,f,g,h,k){var l=[],m,t,p=b[0],w=a.shift(),D=Ob(w,{templateUrl:null,
|
||||
transclude:null,replace:null,$$originalDirective:w}),A=z(w.templateUrl)?w.templateUrl(b,c):w.templateUrl,K=w.templateNamespace;b.empty();d(A).then(function(d){var T,v;d=ja(d);if(w.replace){d=Tb.test(d)?Xc(Yb(K,U(d))):[];T=d[0];if(1!=d.length||1!==T.nodeType)throw ha("tplrt",w.name,A);d={$attr:{}};Y(e,b,T);var C=V(T,[],d);H(w.scope)&&y(C,!0);a=C.concat(a);S(c,d)}else T=p,b.html(d);a.unshift(D);m=Z(a,T,c,f,b,w,g,h,k);n(e,function(a,c){a==T&&(e[c]=b[0])});for(t=O(b[0].childNodes,f);l.length;){d=l.shift();
|
||||
v=l.shift();var ga=l.shift(),L=l.shift(),C=b[0];if(!d.$$destroyed){if(v!==p){var q=v.className;k.hasElementTranscludeDirective&&w.replace||(C=Vb(T));Y(ga,B(v),C);J(B(C),q)}v=m.transcludeOnThisElement?R(d,m.transclude,L):L;m(t,d,C,e,v)}}l=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(l?l.push(b,c,d,a):(m.transcludeOnThisElement&&(a=R(b,m.transclude,e)),m(t,b,c,d,a)))}}function Ia(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function Ua(a,
|
||||
b,c,d){function e(a){return a?" (module: "+a+")":""}if(b)throw ha("multidir",b.name,e(b.$$moduleName),c.name,e(c.$$moduleName),a,ua(d));}function N(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&K.$$addBindingClass(a);return function(a,c){var e=c.parent();b||K.$$addBindingClass(e);K.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function Yb(a,b){a=F(a||"html");switch(a){case "svg":case "math":var c=X.createElement("div");
|
||||
c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function Q(a,b){if("srcdoc"==b)return L.HTML;var c=ta(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return L.RESOURCE_URL}function W(a,c,d,e,f){var g=Q(a,e);f=h[e]||f;var k=b(d,!0,g,f);if(k){if("multiple"===e&&"select"===ta(a))throw ha("selmulti",ua(a));c.push({priority:100,compile:function(){return{pre:function(a,c,h){c=h.$$observers||(h.$$observers=$());if(l.test(e))throw ha("nodomevents");
|
||||
var m=h[e];m!==d&&(k=m&&b(m,!0,g,f),d=m);k&&(h[e]=k(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,a)}))}}}})}}function Y(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=X.createDocumentFragment();a.appendChild(d);
|
||||
B.hasData(d)&&(B.data(c,B.data(d)),oa?(Rb=!0,oa.cleanData([d])):delete B.cache[d[B.expando]]);d=1;for(e=b.length;d<e;d++)f=b[d],B(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function ca(a,b){return M(function(){return a.apply(null,arguments)},a,b)}function ea(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,ua(d))}}function ba(a,c,d,e,f){var g=[];n(e,function(e,h){var k=e.attrName,l=e.optional,m,t,w,D;switch(e.mode){case "@":l||qa.call(c,k)||(d[h]=c[k]=void 0);c.$observe(k,function(a){E(a)&&
|
||||
(d[h]=a)});c.$$observers[k].$$scope=a;E(c[k])&&(d[h]=b(c[k])(a));break;case "=":if(!qa.call(c,k)){if(l)break;c[k]=void 0}if(l&&!c[k])break;t=p(c[k]);D=t.literal?ma:function(a,b){return a===b||a!==a&&b!==b};w=t.assign||function(){m=d[h]=t(a);throw ha("nonassign",c[k],f.name);};m=d[h]=t(a);l=function(b){D(b,d[h])||(D(b,m)?w(a,b=d[h]):d[h]=b);return m=b};l.$stateful=!0;l=e.collection?a.$watchCollection(c[k],l):a.$watch(p(c[k],l),null,t.literal);g.push(l);break;case "&":t=c.hasOwnProperty(k)?p(c[k]):
|
||||
x;if(t===x&&l)break;d[h]=function(b){return t(a,b)}}});return g.length&&function(){for(var a=0,b=g.length;a<b;++a)g[a]()}}var fa=function(a,b){if(b){var c=Object.keys(b),d,e,f;d=0;for(e=c.length;d<e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a};fa.prototype={$normalize:va,$addClass:function(a){a&&0<a.length&&aa.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&aa.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=Yc(a,b);c&&c.length&&aa.addClass(this.$$element,
|
||||
c);(c=Yc(b,a))&&c.length&&aa.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=Qc(this.$$element[0],a),g=Zc[a],h=a;f?(this.$$element.prop(a,b),e=f):g&&(this[g]=b,h=g);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=zc(a,"-"));f=ta(this.$$element);if("a"===f&&"href"===a||"img"===f&&"src"===a)this[a]=b=D(b,"src"===a);else if("img"===f&&"srcset"===a){for(var f="",g=U(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(g)?k:/(,)/,g=g.split(k),k=Math.floor(g.length/2),l=0;l<
|
||||
k;l++)var m=2*l,f=f+D(U(g[m]),!0),f=f+(" "+U(g[m+1]));g=U(g[2*l]).split(/\s/);f+=D(U(g[0]),!0);2===g.length&&(f+=" "+U(g[1]));this[a]=b=f}!1!==d&&(null===b||q(b)?this.$$element.removeAttr(e):this.$$element.attr(e,b));(a=this.$$observers)&&n(a[h],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=$()),e=d[a]||(d[a]=[]);e.push(b);w.$evalAsync(function(){e.$$inter||!c.hasOwnProperty(a)||q(c[a])||b(c[a])});return function(){ab(e,b)}}};var da=b.startSymbol(),
|
||||
ia=b.endSymbol(),ja="{{"==da||"}}"==ia?Ya:function(a){return a.replace(/\{\{/g,da).replace(/}}/g,ia)},ka=/^ngAttr[A-Z]/,la=/^(.+)Start$/;K.$$addBindingInfo=m?function(a,b){var c=a.data("$binding")||[];I(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:x;K.$$addBindingClass=m?function(a){J(a,"ng-binding")}:x;K.$$addScopeInfo=m?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:x;K.$$addScopeClass=m?function(a,b){J(a,b?"ng-isolate-scope":"ng-scope")}:x;return K}]}function va(a){return fb(a.replace(Vc,
|
||||
""))}function Yc(a,b){var d="",c=a.split(/\s+/),e=b.split(/\s+/),f=0;a:for(;f<c.length;f++){for(var g=c[f],h=0;h<e.length;h++)if(g==e[h])continue a;d+=(0<d.length?" ":"")+g}return d}function Xc(a){a=B(a);var b=a.length;if(1>=b)return a;for(;b--;)8===a[b].nodeType&&Pf.call(a,b,1);return a}function Xe(){var a={},b=!1;this.register=function(b,c){Ra(b,"controller");H(b)?M(a,b):a[b]=c};this.allowGlobals=function(){b=!0};this.$get=["$injector","$window",function(d,c){function e(a,b,c,d){if(!a||!H(a.$scope))throw G("$controller")("noscp",
|
||||
d,b);a.$scope[b]=c}return function(f,g,h,k){var l,m,r;h=!0===h;k&&E(k)&&(r=k);if(E(f)){k=f.match(Uc);if(!k)throw Qf("ctrlfmt",f);m=k[1];r=r||k[3];f=a.hasOwnProperty(m)?a[m]:Bc(g.$scope,m,!0)||(b?Bc(c,m,!0):u);Qa(f,m,!0)}if(h)return h=(I(f)?f[f.length-1]:f).prototype,l=Object.create(h||null),r&&e(g,r,l,m||f.name),M(function(){var a=d.invoke(f,l,g,m);a!==l&&(H(a)||z(a))&&(l=a,r&&e(g,r,l,m||f.name));return l},{instance:l,identifier:r});l=d.instantiate(f,g,m);r&&e(g,r,l,m||f.name);return l}}]}function Ye(){this.$get=
|
||||
["$window",function(a){return B(a.document)}]}function Ze(){this.$get=["$log",function(a){return function(b,d){a.error.apply(a,arguments)}}]}function Zb(a){return H(a)?da(a)?a.toISOString():db(a):a}function df(){this.$get=function(){return function(a){if(!a)return"";var b=[];oc(a,function(a,c){null===a||q(a)||(I(a)?n(a,function(a,d){b.push(ja(c)+"="+ja(Zb(a)))}):b.push(ja(c)+"="+ja(Zb(a))))});return b.join("&")}}}function ef(){this.$get=function(){return function(a){function b(a,e,f){null===a||q(a)||
|
||||
(I(a)?n(a,function(a,c){b(a,e+"["+(H(a)?c:"")+"]")}):H(a)&&!da(a)?oc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):d.push(ja(e)+"="+ja(Zb(a))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function $b(a,b){if(E(a)){var d=a.replace(Rf,"").trim();if(d){var c=b("Content-Type");(c=c&&0===c.indexOf($c))||(c=(c=d.match(Sf))&&Tf[c[0]].test(d));c&&(a=uc(d))}}return a}function ad(a){var b=$(),d;E(a)?n(a.split("\n"),function(a){d=a.indexOf(":");var e=F(U(a.substr(0,d)));a=U(a.substr(d+1));e&&
|
||||
(b[e]=b[e]?b[e]+", "+a:a)}):H(a)&&n(a,function(a,d){var f=F(d),g=U(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function bd(a){var b;return function(d){b||(b=ad(a));return d?(d=b[F(d)],void 0===d&&(d=null),d):b}}function cd(a,b,d,c){if(z(c))return c(a,b,d);n(c,function(c){a=c(a,b,d)});return a}function cf(){var a=this.defaults={transformResponse:[$b],transformRequest:[function(a){return H(a)&&"[object File]"!==sa.call(a)&&"[object Blob]"!==sa.call(a)&&"[object FormData]"!==sa.call(a)?db(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},
|
||||
post:ia(ac),put:ia(ac),patch:ia(ac)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer"},b=!1;this.useApplyAsync=function(a){return y(a)?(b=!!a,this):b};var d=!0;this.useLegacyPromiseExtensions=function(a){return y(a)?(d=!!a,this):d};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,k,l){function m(b){function c(a){var b=M({},a);b.data=cd(a.data,a.headers,a.status,f.transformResponse);
|
||||
a=a.status;return 200<=a&&300>a?b:k.reject(b)}function e(a,b){var c,d={};n(a,function(a,e){z(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}if(!fa.isObject(b))throw G("$http")("badreq",b);var f=M({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer},b);f.headers=function(b){var c=a.headers,d=M({},b.headers),f,g,h,c=M({},c.common,c[F(b.method)]);a:for(f in c){g=F(f);for(h in d)if(F(h)===g)continue a;d[f]=c[f]}return e(d,ia(b))}(b);
|
||||
f.method=sb(f.method);f.paramSerializer=E(f.paramSerializer)?l.get(f.paramSerializer):f.paramSerializer;var g=[function(b){var d=b.headers,e=cd(b.data,bd(d),u,b.transformRequest);q(e)&&n(d,function(a,b){"content-type"===F(b)&&delete d[b]});q(b.withCredentials)&&!q(a.withCredentials)&&(b.withCredentials=a.withCredentials);return r(b,e).then(c,c)},u],h=k.when(f);for(n(v,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){b=
|
||||
g.shift();var m=g.shift(),h=h.then(b,m)}d?(h.success=function(a){Qa(a,"fn");h.then(function(b){a(b.data,b.status,b.headers,f)});return h},h.error=function(a){Qa(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=dd("success"),h.error=dd("error"));return h}function r(c,d){function g(a,c,d,e){function f(){l(c,a,d,e)}J&&(200<=a&&300>a?J.put(R,[a,c,ad(d),e]):J.remove(R));b?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function l(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?n.resolve:
|
||||
n.reject)({data:a,status:b,headers:bd(d),config:c,statusText:e})}function r(a){l(a.data,a.status,ia(a.headers()),a.statusText)}function v(){var a=m.pendingRequests.indexOf(c);-1!==a&&m.pendingRequests.splice(a,1)}var n=k.defer(),D=n.promise,J,K,O=c.headers,R=t(c.url,c.paramSerializer(c.params));m.pendingRequests.push(c);D.then(v,v);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(J=H(c.cache)?c.cache:H(a.cache)?a.cache:A);J&&(K=J.get(R),y(K)?K&&z(K.then)?K.then(r,r):I(K)?l(K[1],
|
||||
K[0],ia(K[2]),K[3]):l(K,200,{},"OK"):J.put(R,D));q(K)&&((K=ed(c.url)?f()[c.xsrfCookieName||a.xsrfCookieName]:u)&&(O[c.xsrfHeaderName||a.xsrfHeaderName]=K),e(c.method,R,d,g,O,c.timeout,c.withCredentials,c.responseType));return D}function t(a,b){0<b.length&&(a+=(-1==a.indexOf("?")?"?":"&")+b);return a}var A=g("$http");a.paramSerializer=E(a.paramSerializer)?l.get(a.paramSerializer):a.paramSerializer;var v=[];n(c,function(a){v.unshift(E(a)?l.get(a):l.invoke(a))});m.pendingRequests=[];(function(a){n(arguments,
|
||||
function(a){m[a]=function(b,c){return m(M({},c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){n(arguments,function(a){m[a]=function(b,c,d){return m(M({},d||{},{method:a,url:b,data:c}))}})})("post","put","patch");m.defaults=a;return m}]}function gf(){this.$get=function(){return function(){return new S.XMLHttpRequest}}}function ff(){this.$get=["$browser","$window","$document","$xhrFactory",function(a,b,d,c){return Uf(a,c,a.defer,b.angular.callbacks,d[0])}]}function Uf(a,b,d,
|
||||
c,e){function f(a,b,d){var f=e.createElement("script"),m=null;f.type="text/javascript";f.src=a;f.async=!0;m=function(a){f.removeEventListener("load",m,!1);f.removeEventListener("error",m,!1);e.body.removeChild(f);f=null;var g=-1,A="unknown";a&&("load"!==a.type||c[b].called||(a={type:"error"}),A=a.type,g="error"===a.type?404:200);d&&d(g,A)};f.addEventListener("load",m,!1);f.addEventListener("error",m,!1);e.body.appendChild(f);return m}return function(e,h,k,l,m,r,t,A){function v(){C&&C();w&&w.abort()}
|
||||
function T(b,c,e,f,g){y(L)&&d.cancel(L);C=w=null;b(c,e,f,g);a.$$completeOutstandingRequest(x)}a.$$incOutstandingRequestCount();h=h||a.url();if("jsonp"==F(e)){var p="_"+(c.counter++).toString(36);c[p]=function(a){c[p].data=a;c[p].called=!0};var C=f(h.replace("JSON_CALLBACK","angular.callbacks."+p),p,function(a,b){T(l,a,c[p].data,"",b);c[p]=x})}else{var w=b(e,h);w.open(e,h,!0);n(m,function(a,b){y(a)&&w.setRequestHeader(b,a)});w.onload=function(){var a=w.statusText||"",b="response"in w?w.response:w.responseText,
|
||||
c=1223===w.status?204:w.status;0===c&&(c=b?200:"file"==wa(h).protocol?404:0);T(l,c,b,w.getAllResponseHeaders(),a)};e=function(){T(l,-1,null,null,"")};w.onerror=e;w.onabort=e;t&&(w.withCredentials=!0);if(A)try{w.responseType=A}catch(ga){if("json"!==A)throw ga;}w.send(q(k)?null:k)}if(0<r)var L=d(v,r);else r&&z(r.then)&&r.then(v)}}function af(){var a="{{",b="}}";this.startSymbol=function(b){return b?(a=b,this):a};this.endSymbol=function(a){return a?(b=a,this):b};this.$get=["$parse","$exceptionHandler",
|
||||
"$sce",function(d,c,e){function f(a){return"\\\\\\"+a}function g(c){return c.replace(m,a).replace(r,b)}function h(f,h,m,r){function p(a){try{var b=a;a=m?e.getTrusted(m,b):e.valueOf(b);var d;if(r&&!y(a))d=a;else if(null==a)d="";else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=db(a)}d=a}return d}catch(g){c(Ja.interr(f,g))}}r=!!r;for(var C,w,n=0,L=[],s=[],D=f.length,J=[],K=[];n<D;)if(-1!=(C=f.indexOf(a,n))&&-1!=(w=f.indexOf(b,C+k)))n!==C&&J.push(g(f.substring(n,C))),n=f.substring(C+
|
||||
k,w),L.push(n),s.push(d(n,p)),n=w+l,K.push(J.length),J.push("");else{n!==D&&J.push(g(f.substring(n)));break}m&&1<J.length&&Ja.throwNoconcat(f);if(!h||L.length){var O=function(a){for(var b=0,c=L.length;b<c;b++){if(r&&q(a[b]))return;J[K[b]]=a[b]}return J.join("")};return M(function(a){var b=0,d=L.length,e=Array(d);try{for(;b<d;b++)e[b]=s[b](a);return O(e)}catch(g){c(Ja.interr(f,g))}},{exp:f,expressions:L,$$watchDelegate:function(a,b){var c;return a.$watchGroup(s,function(d,e){var f=O(d);z(b)&&b.call(this,
|
||||
f,d!==e?c:f,a);c=f})}})}}var k=a.length,l=b.length,m=new RegExp(a.replace(/./g,f),"g"),r=new RegExp(b.replace(/./g,f),"g");h.startSymbol=function(){return a};h.endSymbol=function(){return b};return h}]}function bf(){this.$get=["$rootScope","$window","$q","$$q",function(a,b,d,c){function e(e,h,k,l){var m=4<arguments.length,r=m?ra.call(arguments,4):[],t=b.setInterval,A=b.clearInterval,v=0,n=y(l)&&!l,p=(n?c:d).defer(),C=p.promise;k=y(k)?k:0;C.then(null,null,m?function(){e.apply(null,r)}:e);C.$$intervalId=
|
||||
t(function(){p.notify(v++);0<k&&v>=k&&(p.resolve(v),A(C.$$intervalId),delete f[C.$$intervalId]);n||a.$apply()},h);f[C.$$intervalId]=p;return C}var f={};e.cancel=function(a){return a&&a.$$intervalId in f?(f[a.$$intervalId].reject("canceled"),b.clearInterval(a.$$intervalId),delete f[a.$$intervalId],!0):!1};return e}]}function bc(a){a=a.split("/");for(var b=a.length;b--;)a[b]=ob(a[b]);return a.join("/")}function fd(a,b){var d=wa(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=ea(d.port)||Vf[d.protocol]||
|
||||
null}function gd(a,b){var d="/"!==a.charAt(0);d&&(a="/"+a);var c=wa(a);b.$$path=decodeURIComponent(d&&"/"===c.pathname.charAt(0)?c.pathname.substring(1):c.pathname);b.$$search=xc(c.search);b.$$hash=decodeURIComponent(c.hash);b.$$path&&"/"!=b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function pa(a,b){if(0===b.indexOf(a))return b.substr(a.length)}function Fa(a){var b=a.indexOf("#");return-1==b?a:a.substr(0,b)}function ib(a){return a.replace(/(#.+)|#$/,"$1")}function cc(a,b,d){this.$$html5=!0;d=d||"";
|
||||
fd(a,this);this.$$parse=function(a){var d=pa(b,a);if(!E(d))throw Db("ipthprfx",a,b);gd(d,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Qb(this.$$search),d=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=bc(this.$$path)+(a?"?"+a:"")+d;this.$$absUrl=b+this.$$url.substr(1)};this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;y(f=pa(a,c))?(g=f,g=y(f=pa(d,f))?b+(pa("/",f)||f):a+g):y(f=pa(b,c))?g=b+f:b==c+"/"&&(g=b);g&&this.$$parse(g);
|
||||
return!!g}}function dc(a,b,d){fd(a,this);this.$$parse=function(c){var e=pa(a,c)||pa(b,c),f;q(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",q(e)&&(a=c,this.replace())):(f=pa(d,e),q(f)&&(f=e));gd(f,this);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?f[1]:c);this.$$path=c;this.$$compose()};this.$$compose=function(){var b=Qb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=bc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+(this.$$url?
|
||||
d+this.$$url:"")};this.$$parseLinkUrl=function(b,d){return Fa(a)==Fa(b)?(this.$$parse(b),!0):!1}}function hd(a,b,d){this.$$html5=!0;dc.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a==Fa(c)?f=c:(g=pa(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$compose=function(){var b=Qb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=bc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+d+this.$$url}}function Eb(a){return function(){return this[a]}}
|
||||
function id(a,b){return function(d){if(q(d))return this[a];this[a]=b(d);this.$$compose();return this}}function hf(){var a="",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return y(b)?(a=b,this):a};this.html5Mode=function(a){return $a(a)?(b.enabled=a,this):H(a)?($a(a.enabled)&&(b.enabled=a.enabled),$a(a.requireBase)&&(b.requireBase=a.requireBase),$a(a.rewriteLinks)&&(b.rewriteLinks=a.rewriteLinks),this):b};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",
|
||||
function(d,c,e,f,g){function h(a,b,d){var e=l.url(),f=l.$$state;try{c.url(a,b,d),l.$$state=c.state()}catch(g){throw l.url(e),l.$$state=f,g;}}function k(a,b){d.$broadcast("$locationChangeSuccess",l.absUrl(),a,l.$$state,b)}var l,m;m=c.baseHref();var r=c.url(),t;if(b.enabled){if(!m&&b.requireBase)throw Db("nobase");t=r.substring(0,r.indexOf("/",r.indexOf("//")+2))+(m||"/");m=e.history?cc:hd}else t=Fa(r),m=dc;var A=t.substr(0,Fa(t).lastIndexOf("/")+1);l=new m(t,A,"#"+a);l.$$parseLinkUrl(r,r);l.$$state=
|
||||
c.state();var v=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(b.rewriteLinks&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!=a.which&&2!=a.button){for(var e=B(a.target);"a"!==ta(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),k=e.attr("href")||e.attr("xlink:href");H(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=wa(h.animVal).href);v.test(h)||!h||e.attr("target")||a.isDefaultPrevented()||!l.$$parseLinkUrl(h,k)||(a.preventDefault(),l.absUrl()!=c.url()&&(d.$apply(),g.angular["ff-684208-preventDefault"]=
|
||||
!0))}});ib(l.absUrl())!=ib(r)&&c.url(l.absUrl(),!0);var n=!0;c.onUrlChange(function(a,b){q(pa(A,a))?g.location.href=a:(d.$evalAsync(function(){var c=l.absUrl(),e=l.$$state,f;a=ib(a);l.$$parse(a);l.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;l.absUrl()===a&&(f?(l.$$parse(c),l.$$state=e,h(c,!1,e)):(n=!1,k(c,e)))}),d.$$phase||d.$digest())});d.$watch(function(){var a=ib(c.url()),b=ib(l.absUrl()),f=c.state(),g=l.$$replace,m=a!==b||l.$$html5&&e.history&&f!==l.$$state;if(n||
|
||||
m)n=!1,d.$evalAsync(function(){var b=l.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,l.$$state,f).defaultPrevented;l.absUrl()===b&&(c?(l.$$parse(a),l.$$state=f):(m&&h(b,g,f===l.$$state?null:l.$$state),k(a,f)))});l.$$replace=!1});return l}]}function jf(){var a=!0,b=this;this.debugEnabled=function(b){return y(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&
|
||||
(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=d.console||{},e=b[a]||b.log||x;a=!1;try{a=!!e.apply}catch(k){}return a?function(){var a=[];n(arguments,function(b){a.push(c(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){a&&c.apply(b,arguments)}}()}}]}function Va(a,b){if("__defineGetter__"===a||"__defineSetter__"===a||"__lookupGetter__"===a||"__lookupSetter__"===
|
||||
a||"__proto__"===a)throw ba("isecfld",b);return a}function jd(a,b){a+="";if(!E(a))throw ba("iseccst",b);return a}function xa(a,b){if(a){if(a.constructor===a)throw ba("isecfn",b);if(a.window===a)throw ba("isecwindow",b);if(a.children&&(a.nodeName||a.prop&&a.attr&&a.find))throw ba("isecdom",b);if(a===Object)throw ba("isecobj",b);}return a}function kd(a,b){if(a){if(a.constructor===a)throw ba("isecfn",b);if(a===Wf||a===Xf||a===Yf)throw ba("isecff",b);}}function ld(a,b){if(a&&(a===(0).constructor||a===
|
||||
(!1).constructor||a==="".constructor||a==={}.constructor||a===[].constructor||a===Function.constructor))throw ba("isecaf",b);}function Zf(a,b){return"undefined"!==typeof a?a:b}function md(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function W(a,b){var d,c;switch(a.type){case s.Program:d=!0;n(a.body,function(a){W(a.expression,b);d=d&&a.expression.constant});a.constant=d;break;case s.Literal:a.constant=!0;a.toWatch=[];break;case s.UnaryExpression:W(a.argument,b);a.constant=a.argument.constant;
|
||||
a.toWatch=a.argument.toWatch;break;case s.BinaryExpression:W(a.left,b);W(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case s.LogicalExpression:W(a.left,b);W(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case s.ConditionalExpression:W(a.test,b);W(a.alternate,b);W(a.consequent,b);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case s.Identifier:a.constant=
|
||||
!1;a.toWatch=[a];break;case s.MemberExpression:W(a.object,b);a.computed&&W(a.property,b);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=[a];break;case s.CallExpression:d=a.filter?!b(a.callee.name).$stateful:!1;c=[];n(a.arguments,function(a){W(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=a.filter&&!b(a.callee.name).$stateful?c:[a];break;case s.AssignmentExpression:W(a.left,b);W(a.right,b);a.constant=a.left.constant&&a.right.constant;
|
||||
a.toWatch=[a];break;case s.ArrayExpression:d=!0;c=[];n(a.elements,function(a){W(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=c;break;case s.ObjectExpression:d=!0;c=[];n(a.properties,function(a){W(a.value,b);d=d&&a.value.constant;a.value.constant||c.push.apply(c,a.value.toWatch)});a.constant=d;a.toWatch=c;break;case s.ThisExpression:a.constant=!1,a.toWatch=[]}}function nd(a){if(1==a.length){a=a[0].expression;var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:u}}
|
||||
function od(a){return a.type===s.Identifier||a.type===s.MemberExpression}function pd(a){if(1===a.body.length&&od(a.body[0].expression))return{type:s.AssignmentExpression,left:a.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function qd(a){return 0===a.body.length||1===a.body.length&&(a.body[0].expression.type===s.Literal||a.body[0].expression.type===s.ArrayExpression||a.body[0].expression.type===s.ObjectExpression)}function rd(a,b){this.astBuilder=a;this.$filter=b}function sd(a,
|
||||
b){this.astBuilder=a;this.$filter=b}function Fb(a){return"constructor"==a}function ec(a){return z(a.valueOf)?a.valueOf():$f.call(a)}function kf(){var a=$(),b=$();this.$get=["$filter",function(d){function c(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=ec(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function e(a,b,d,e,f){var g=e.inputs,h;if(1===g.length){var k=c,g=g[0];return a.$watch(function(a){var b=g(a);c(b,k)||(h=e(a,u,u,[b]),k=b&&ec(b));return h},b,d,f)}for(var l=[],m=[],r=0,n=
|
||||
g.length;r<n;r++)l[r]=c,m[r]=null;return a.$watch(function(a){for(var b=!1,d=0,f=g.length;d<f;d++){var k=g[d](a);if(b||(b=!c(k,l[d])))m[d]=k,l[d]=k&&ec(k)}b&&(h=e(a,u,u,m));return h},b,d,f)}function f(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;z(b)&&b.apply(this,arguments);y(a)&&d.$$postDigest(function(){y(f)&&e()})},c)}function g(a,b,c,d){function e(a){var b=!0;n(a,function(a){y(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},function(a,
|
||||
c,d){g=a;z(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function h(a,b,c,d){var e;return e=a.$watch(function(a){return d(a)},function(a,c,d){z(b)&&b.apply(this,arguments);e()},c)}function k(a,b){if(!b)return a;var c=a.$$watchDelegate,d=!1,c=c!==g&&c!==f?function(c,e,f,g){f=d&&g?g[0]:a(c,e,f,g);return b(f,c,e)}:function(c,d,e,f){e=a(c,d,e,f);c=b(e,c,d);return y(e)?c:e};a.$$watchDelegate&&a.$$watchDelegate!==e?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=
|
||||
e,d=!a.inputs,c.inputs=a.inputs?a.inputs:[a]);return c}var l=Ba().noUnsafeEval,m={csp:l,expensiveChecks:!1},r={csp:l,expensiveChecks:!0};return function(c,l,v){var n,p,q;switch(typeof c){case "string":q=c=c.trim();var w=v?b:a;n=w[q];n||(":"===c.charAt(0)&&":"===c.charAt(1)&&(p=!0,c=c.substring(2)),v=v?r:m,n=new fc(v),n=(new gc(n,d,v)).parse(c),n.constant?n.$$watchDelegate=h:p?n.$$watchDelegate=n.literal?g:f:n.inputs&&(n.$$watchDelegate=e),w[q]=n);return k(n,l);case "function":return k(c,l);default:return x}}}]}
|
||||
function mf(){this.$get=["$rootScope","$exceptionHandler",function(a,b){return td(function(b){a.$evalAsync(b)},b)}]}function nf(){this.$get=["$browser","$exceptionHandler",function(a,b){return td(function(b){a.defer(b)},b)}]}function td(a,b){function d(a,b,c){function d(b){return function(c){e||(e=!0,b.call(a,c))}}var e=!1;return[d(b),d(c)]}function c(){this.$$state={status:0}}function e(a,b){return function(c){b.call(a,c)}}function f(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,a(function(){var a,
|
||||
d,e;e=c.pending;c.processScheduled=!1;c.pending=u;for(var f=0,g=e.length;f<g;++f){d=e[f][0];a=e[f][c.status];try{z(a)?d.resolve(a(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),b(h)}}}))}function g(){this.promise=new c;this.resolve=e(this,this.resolve);this.reject=e(this,this.reject);this.notify=e(this,this.notify)}var h=G("$q",TypeError);M(c.prototype,{then:function(a,b,c){if(q(a)&&q(b)&&q(c))return this;var d=new g;this.$$state.pending=this.$$state.pending||[];
|
||||
this.$$state.pending.push([d,a,b,c]);0<this.$$state.status&&f(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return l(b,!0,a)},function(b){return l(b,!1,a)},b)}});M(g.prototype,{resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(h("qcycle",a)):this.$$resolve(a))},$$resolve:function(a){var c,e;e=d(this,this.$$resolve,this.$$reject);try{if(H(a)||z(a))c=a&&a.then;z(c)?(this.promise.$$state.status=
|
||||
-1,c.call(a,e[0],e[1],this.notify)):(this.promise.$$state.value=a,this.promise.$$state.status=1,f(this.promise.$$state))}catch(g){e[1](g),b(g)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;f(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&a(function(){for(var a,e,f=0,g=d.length;f<g;f++){e=d[f][0];a=d[f][3];try{e.notify(z(a)?
|
||||
a(c):c)}catch(h){b(h)}}})}});var k=function(a,b){var c=new g;b?c.resolve(a):c.reject(a);return c.promise},l=function(a,b,c){var d=null;try{z(c)&&(d=c())}catch(e){return k(e,!1)}return d&&z(d.then)?d.then(function(){return k(a,b)},function(a){return k(a,!1)}):k(a,b)},m=function(a,b,c,d){var e=new g;e.resolve(a);return e.promise.then(b,c,d)},r=function A(a){if(!z(a))throw h("norslvr",a);if(!(this instanceof A))return new A(a);var b=new g;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};
|
||||
r.defer=function(){return new g};r.reject=function(a){var b=new g;b.reject(a);return b.promise};r.when=m;r.resolve=m;r.all=function(a){var b=new g,c=0,d=I(a)?[]:{};n(a,function(a,e){c++;m(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return r}function wf(){this.$get=["$window","$timeout",function(a,b){var d=a.requestAnimationFrame||a.webkitRequestAnimationFrame,c=a.cancelAnimationFrame||a.webkitCancelAnimationFrame||
|
||||
a.webkitCancelRequestAnimationFrame,e=!!d,f=e?function(a){var b=d(a);return function(){c(b)}}:function(a){var c=b(a,16.66,!1);return function(){b.cancel(c)}};f.supported=e;return f}]}function lf(){function a(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++nb;this.$$ChildScope=null}b.prototype=a;return b}var b=10,d=G("$rootScope"),c=null,e=null;this.digestTtl=function(a){arguments.length&&
|
||||
(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(f,g,h,k){function l(a){a.currentScope.$$destroyed=!0}function m(a){9===Ha&&(a.$$childHead&&m(a.$$childHead),a.$$nextSibling&&m(a.$$nextSibling));a.$parent=a.$$nextSibling=a.$$prevSibling=a.$$childHead=a.$$childTail=a.$root=a.$$watchers=null}function r(){this.$id=++nb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destroyed=
|
||||
!1;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$$isolateBindings=null}function t(a){if(w.$$phase)throw d("inprog",w.$$phase);w.$$phase=a}function A(a,b){do a.$$watchersCount+=b;while(a=a.$parent)}function v(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function s(){}function p(){for(;aa.length;)try{aa.shift()()}catch(a){g(a)}e=null}function C(){null===e&&(e=k.defer(function(){w.$apply(p)}))}r.prototype={constructor:r,
|
||||
$new:function(b,c){var d;c=c||this;b?(d=new r,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=a(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(b||c!=this)&&d.$on("$destroy",l);return d},$watch:function(a,b,d,e){var f=h(a);if(f.$$watchDelegate)return f.$$watchDelegate(this,b,d,f,a);var g=this,k=g.$$watchers,l={fn:b,last:s,get:f,exp:e||a,eq:!!d};c=null;z(b)||(l.fn=x);k||
|
||||
(k=g.$$watchers=[]);k.unshift(l);A(this,1);return function(){0<=ab(k,l)&&A(g,-1);c=null}},$watchGroup:function(a,b){function c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=!1}}if(1===a.length)return this.$watch(a[0],function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});n(a,function(a,b){var k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},
|
||||
$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!q(e)){if(H(e))if(za(e))for(f!==r&&(f=r,n=f.length=0,l++),a=e.length,n!==a&&(l++,f.length=n=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==t&&(f=t={},n=0,l++);a=0;for(b in e)qa.call(e,b)&&(a++,g=e[b],h=f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(n++,f[b]=g,l++));if(n>a)for(b in l++,f)qa.call(e,b)||(n--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,g,k=1<b.length,l=0,m=
|
||||
h(a,c),r=[],t={},p=!0,n=0;return this.$watch(m,function(){p?(p=!1,b(e,e,d)):b(e,g,d);if(k)if(H(e))if(za(e)){g=Array(e.length);for(var a=0;a<e.length;a++)g[a]=e[a]}else for(a in g={},e)qa.call(e,a)&&(g[a]=e[a]);else g=e})},$digest:function(){var a,f,h,l,m,r,n=b,A,q=[],v,C;t("$digest");k.$$checkUrlChange();this===w&&null!==e&&(k.defer.cancel(e),p());c=null;do{r=!1;for(A=this;u.length;){try{C=u.shift(),C.scope.$eval(C.expression,C.locals)}catch(aa){g(aa)}c=null}a:do{if(l=A.$$watchers)for(m=l.length;m--;)try{if(a=
|
||||
l[m])if((f=a.get(A))!==(h=a.last)&&!(a.eq?ma(f,h):"number"===typeof f&&"number"===typeof h&&isNaN(f)&&isNaN(h)))r=!0,c=a,a.last=a.eq?bb(f,null):f,a.fn(f,h===s?f:h,A),5>n&&(v=4-n,q[v]||(q[v]=[]),q[v].push({msg:z(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:f,oldVal:h}));else if(a===c){r=!1;break a}}catch(y){g(y)}if(!(l=A.$$watchersCount&&A.$$childHead||A!==this&&A.$$nextSibling))for(;A!==this&&!(l=A.$$nextSibling);)A=A.$parent}while(A=l);if((r||u.length)&&!n--)throw w.$$phase=null,d("infdig",
|
||||
b,q);}while(r||u.length);for(w.$$phase=null;L.length;)try{L.shift()()}catch(x){g(x)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===w&&k.$$applicationDestroyed();A(this,-this.$$watchersCount);for(var b in this.$$listenerCount)v(this,this.$$listenerCount[b],b);a&&a.$$childHead==this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=
|
||||
this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=x;this.$on=this.$watch=this.$watchGroup=function(){return x};this.$$listeners={};this.$$nextSibling=null;m(this)}},$eval:function(a,b){return h(a)(this,b)},$evalAsync:function(a,b){w.$$phase||u.length||k.defer(function(){u.length&&w.$digest()});u.push({scope:this,expression:a,locals:b})},$$postDigest:function(a){L.push(a)},$apply:function(a){try{t("$apply");
|
||||
try{return this.$eval(a)}finally{w.$$phase=null}}catch(b){g(b)}finally{try{w.$digest()}catch(c){throw g(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&aa.push(b);C()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,v(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,f=!1,h=
|
||||
{name:a,targetScope:e,stopPropagation:function(){f=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=cb([h],arguments,1),l,m;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(m=d.length;l<m;l++)if(d[l])try{d[l].apply(null,k)}catch(r){g(r)}else d.splice(l,1),l--,m--;if(f)return h.currentScope=null,h;e=e.$parent}while(e);h.currentScope=null;return h},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};
|
||||
if(!this.$$listenerCount[a])return e;for(var f=cb([e],arguments,1),h,k;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,f)}catch(l){g(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var w=new r,u=w.$$asyncQueue=[],L=w.$$postDigestQueue=[],aa=w.$$applyAsyncQueue=[];return w}]}function ge(){var a=/^\s*(https?|ftp|mailto|tel|file):/,
|
||||
b=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(b){return y(b)?(a=b,this):a};this.imgSrcSanitizationWhitelist=function(a){return y(a)?(b=a,this):b};this.$get=function(){return function(d,c){var e=c?b:a,f;f=wa(d).href;return""===f||f.match(e)?d:"unsafe:"+f}}}function ag(a){if("self"===a)return a;if(E(a)){if(-1<a.indexOf("***"))throw ya("iwcard",a);a=ud(a).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+a+"$")}if(Ma(a))return new RegExp("^"+
|
||||
a.source+"$");throw ya("imatcher");}function vd(a){var b=[];y(a)&&n(a,function(a){b.push(ag(a))});return b}function pf(){this.SCE_CONTEXTS=la;var a=["self"],b=[];this.resourceUrlWhitelist=function(b){arguments.length&&(a=vd(b));return a};this.resourceUrlBlacklist=function(a){arguments.length&&(b=vd(a));return b};this.$get=["$injector",function(d){function c(a,b){return"self"===a?ed(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=
|
||||
new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var f=function(a){throw ya("unsafe");};d.has("$sanitize")&&(f=d.get("$sanitize"));var g=e(),h={};h[la.HTML]=e(g);h[la.CSS]=e(g);h[la.URL]=e(g);h[la.JS]=e(g);h[la.RESOURCE_URL]=e(h[la.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw ya("icontext",a,b);if(null===b||q(b)||""===b)return b;if("string"!==typeof b)throw ya("itype",
|
||||
a);return new c(b)},getTrusted:function(d,e){if(null===e||q(e)||""===e)return e;var g=h.hasOwnProperty(d)?h[d]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(d===la.RESOURCE_URL){var g=wa(e.toString()),r,t,n=!1;r=0;for(t=a.length;r<t;r++)if(c(a[r],g)){n=!0;break}if(n)for(r=0,t=b.length;r<t;r++)if(c(b[r],g)){n=!1;break}if(n)return e;throw ya("insecurl",e.toString());}if(d===la.HTML)return f(e);throw ya("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}
|
||||
function of(){var a=!0;this.enabled=function(b){arguments.length&&(a=!!b);return a};this.$get=["$parse","$sceDelegate",function(b,d){if(a&&8>Ha)throw ya("iequirks");var c=ia(la);c.isEnabled=function(){return a};c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},c.valueOf=Ya);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;n(la,function(a,
|
||||
b){var d=F(b);c[fb("parse_as_"+d)]=function(b){return e(a,b)};c[fb("get_trusted_"+d)]=function(b){return f(a,b)};c[fb("trust_as_"+d)]=function(b){return g(a,b)}});return c}]}function qf(){this.$get=["$window","$document",function(a,b){var d={},c=ea((/android (\d+)/.exec(F((a.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((a.navigator||{}).userAgent),f=b[0]||{},g,h=/^(Moz|webkit|ms)(?=[A-Z])/,k=f.body&&f.body.style,l=!1,m=!1;if(k){for(var r in k)if(l=h.exec(r)){g=l[0];g=g.substr(0,1).toUpperCase()+
|
||||
g.substr(1);break}g||(g="WebkitOpacity"in k&&"webkit");l=!!("transition"in k||g+"Transition"in k);m=!!("animation"in k||g+"Animation"in k);!c||l&&m||(l=E(k.webkitTransition),m=E(k.webkitAnimation))}return{history:!(!a.history||!a.history.pushState||4>c||e),hasEvent:function(a){if("input"===a&&11>=Ha)return!1;if(q(d[a])){var b=f.createElement("div");d[a]="on"+a in b}return d[a]},csp:Ba(),vendorPrefix:g,transitions:l,animations:m,android:c}}]}function sf(){this.$get=["$templateCache","$http","$q","$sce",
|
||||
function(a,b,d,c){function e(f,g){e.totalPendingRequests++;E(f)&&a.get(f)||(f=c.getTrustedResourceUrl(f));var h=b.defaults&&b.defaults.transformResponse;I(h)?h=h.filter(function(a){return a!==$b}):h===$b&&(h=null);return b.get(f,{cache:a,transformResponse:h})["finally"](function(){e.totalPendingRequests--}).then(function(b){a.put(f,b.data);return b.data},function(a){if(!g)throw ha("tpload",f,a.status,a.statusText);return d.reject(a)})}e.totalPendingRequests=0;return e}]}function tf(){this.$get=["$rootScope",
|
||||
"$browser","$location",function(a,b,d){return{findBindings:function(a,b,d){a=a.getElementsByClassName("ng-binding");var g=[];n(a,function(a){var c=fa.element(a).data("$binding");c&&n(c,function(c){d?(new RegExp("(^|\\s)"+ud(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!=c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],h=0;h<g.length;++h){var k=a.querySelectorAll("["+g[h]+"model"+(d?"=":"*=")+'"'+b+'"]');if(k.length)return k}},getLocation:function(){return d.url()},
|
||||
setLocation:function(b){b!==d.url()&&(d.url(b),a.$digest())},whenStable:function(a){b.notifyWhenNoOutstandingRequests(a)}}}]}function uf(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(a,b,d,c,e){function f(f,k,l){z(f)||(l=k,k=f,f=x);var m=ra.call(arguments,3),r=y(l)&&!l,t=(r?c:d).defer(),n=t.promise,q;q=b.defer(function(){try{t.resolve(f.apply(null,m))}catch(b){t.reject(b),e(b)}finally{delete g[n.$$timeoutId]}r||a.$apply()},k);n.$$timeoutId=q;g[q]=t;return n}var g={};
|
||||
f.cancel=function(a){return a&&a.$$timeoutId in g?(g[a.$$timeoutId].reject("canceled"),delete g[a.$$timeoutId],b.defer.cancel(a.$$timeoutId)):!1};return f}]}function wa(a){Ha&&(Y.setAttribute("href",a),a=Y.href);Y.setAttribute("href",a);return{href:Y.href,protocol:Y.protocol?Y.protocol.replace(/:$/,""):"",host:Y.host,search:Y.search?Y.search.replace(/^\?/,""):"",hash:Y.hash?Y.hash.replace(/^#/,""):"",hostname:Y.hostname,port:Y.port,pathname:"/"===Y.pathname.charAt(0)?Y.pathname:"/"+Y.pathname}}function ed(a){a=
|
||||
E(a)?wa(a):a;return a.protocol===wd.protocol&&a.host===wd.host}function vf(){this.$get=na(S)}function xd(a){function b(a){try{return decodeURIComponent(a)}catch(b){return a}}var d=a[0]||{},c={},e="";return function(){var a,g,h,k,l;a=d.cookie||"";if(a!==e)for(e=a,a=e.split("; "),c={},h=0;h<a.length;h++)g=a[h],k=g.indexOf("="),0<k&&(l=b(g.substring(0,k)),q(c[l])&&(c[l]=b(g.substring(k+1))));return c}}function zf(){this.$get=xd}function Jc(a){function b(d,c){if(H(d)){var e={};n(d,function(a,c){e[c]=
|
||||
b(c,a)});return e}return a.factory(d+"Filter",c)}this.register=b;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];b("currency",yd);b("date",zd);b("filter",bg);b("json",cg);b("limitTo",dg);b("lowercase",eg);b("number",Ad);b("orderBy",Bd);b("uppercase",fg)}function bg(){return function(a,b,d){if(!za(a)){if(null==a)return a;throw G("filter")("notarray",a);}var c;switch(hc(b)){case "function":break;case "boolean":case "null":case "number":case "string":c=!0;case "object":b=
|
||||
gg(b,d,c);break;default:return a}return Array.prototype.filter.call(a,b)}}function gg(a,b,d){var c=H(a)&&"$"in a;!0===b?b=ma:z(b)||(b=function(a,b){if(q(a))return!1;if(null===a||null===b)return a===b;if(H(b)||H(a)&&!qc(a))return!1;a=F(""+a);b=F(""+b);return-1!==a.indexOf(b)});return function(e){return c&&!H(e)?Ka(e,a.$,b,!1):Ka(e,a,b,d)}}function Ka(a,b,d,c,e){var f=hc(a),g=hc(b);if("string"===g&&"!"===b.charAt(0))return!Ka(a,b.substring(1),d,c);if(I(a))return a.some(function(a){return Ka(a,b,d,c)});
|
||||
switch(f){case "object":var h;if(c){for(h in a)if("$"!==h.charAt(0)&&Ka(a[h],b,d,!0))return!0;return e?!1:Ka(a,b,d,!1)}if("object"===g){for(h in b)if(e=b[h],!z(e)&&!q(e)&&(f="$"===h,!Ka(f?a:a[h],e,d,f,f)))return!1;return!0}return d(a,b);case "function":return!1;default:return d(a,b)}}function hc(a){return null===a?"null":typeof a}function yd(a){var b=a.NUMBER_FORMATS;return function(a,c,e){q(c)&&(c=b.CURRENCY_SYM);q(e)&&(e=b.PATTERNS[1].maxFrac);return null==a?a:Cd(a,b.PATTERNS[1],b.GROUP_SEP,b.DECIMAL_SEP,
|
||||
e).replace(/\u00A4/g,c)}}function Ad(a){var b=a.NUMBER_FORMATS;return function(a,c){return null==a?a:Cd(a,b.PATTERNS[0],b.GROUP_SEP,b.DECIMAL_SEP,c)}}function Cd(a,b,d,c,e){if(H(a))return"";var f=0>a;a=Math.abs(a);var g=Infinity===a;if(!g&&!isFinite(a))return"";var h=a+"",k="",l=!1,m=[];g&&(k="\u221e");if(!g&&-1!==h.indexOf("e")){var r=h.match(/([\d\.]+)e(-?)(\d+)/);r&&"-"==r[2]&&r[3]>e+1?a=0:(k=h,l=!0)}if(g||l)0<e&&1>a&&(k=a.toFixed(e),a=parseFloat(k),k=k.replace(ic,c));else{g=(h.split(ic)[1]||"").length;
|
||||
q(e)&&(e=Math.min(Math.max(b.minFrac,g),b.maxFrac));a=+(Math.round(+(a.toString()+"e"+e)).toString()+"e"+-e);var g=(""+a).split(ic),h=g[0],g=g[1]||"",r=0,t=b.lgSize,n=b.gSize;if(h.length>=t+n)for(r=h.length-t,l=0;l<r;l++)0===(r-l)%n&&0!==l&&(k+=d),k+=h.charAt(l);for(l=r;l<h.length;l++)0===(h.length-l)%t&&0!==l&&(k+=d),k+=h.charAt(l);for(;g.length<e;)g+="0";e&&"0"!==e&&(k+=c+g.substr(0,e))}0===a&&(f=!1);m.push(f?b.negPre:b.posPre,k,f?b.negSuf:b.posSuf);return m.join("")}function Gb(a,b,d){var c="";
|
||||
0>a&&(c="-",a=-a);for(a=""+a;a.length<b;)a="0"+a;d&&(a=a.substr(a.length-b));return c+a}function ca(a,b,d,c){d=d||0;return function(e){e=e["get"+a]();if(0<d||e>-d)e+=d;0===e&&-12==d&&(e=12);return Gb(e,b,c)}}function Hb(a,b){return function(d,c){var e=d["get"+a](),f=sb(b?"SHORT"+a:a);return c[f][e]}}function Dd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Ed(a){return function(b){var d=Dd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-
|
||||
+d;b=1+Math.round(b/6048E5);return Gb(b,a)}}function jc(a,b){return 0>=a.getFullYear()?b.ERAS[0]:b.ERAS[1]}function zd(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=ea(b[9]+b[10]),g=ea(b[9]+b[11]));h.call(a,ea(b[1]),ea(b[2])-1,ea(b[3]));f=ea(b[4]||0)-f;g=ea(b[5]||0)-g;h=ea(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));k.call(a,f,g,h,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
|
||||
return function(c,d,f){var g="",h=[],k,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;E(c)&&(c=hg.test(c)?ea(c):b(c));Q(c)&&(c=new Date(c));if(!da(c)||!isFinite(c.getTime()))return c;for(;d;)(l=ig.exec(d))?(h=cb(h,l,1),d=h.pop()):(h.push(d),d=null);var m=c.getTimezoneOffset();f&&(m=vc(f,c.getTimezoneOffset()),c=Pb(c,f,!0));n(h,function(b){k=jg[b];g+=k?k(c,a.DATETIME_FORMATS,m):b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function cg(){return function(a,b){q(b)&&(b=2);return db(a,b)}}function dg(){return function(a,
|
||||
b,d){b=Infinity===Math.abs(Number(b))?Number(b):ea(b);if(isNaN(b))return a;Q(a)&&(a=a.toString());if(!I(a)&&!E(a))return a;d=!d||isNaN(d)?0:ea(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?a.slice(d,d+b):0===d?a.slice(b,a.length):a.slice(Math.max(0,d+b),d)}}function Bd(a){function b(b,d){d=d?-1:1;return b.map(function(b){var c=1,h=Ya;if(z(b))h=b;else if(E(b)){if("+"==b.charAt(0)||"-"==b.charAt(0))c="-"==b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(h=a(b),h.constant))var k=h(),h=function(a){return a[k]}}return{get:h,
|
||||
descending:c*d}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(a,e,f){if(!za(a))return a;I(e)||(e=[e]);0===e.length&&(e=["+"]);var g=b(e,f);g.push({get:function(){return{}},descending:f?-1:1});a=Array.prototype.map.call(a,function(a,b){return{value:a,predicateValues:g.map(function(c){var e=c.get(a);c=typeof e;if(null===e)c="string",e="null";else if("string"===c)e=e.toLowerCase();else if("object"===c)a:{if("function"===typeof e.valueOf&&
|
||||
(e=e.valueOf(),d(e)))break a;if(qc(e)&&(e=e.toString(),d(e)))break a;e=b}return{value:e,type:c}})}});a.sort(function(a,b){for(var c=0,d=0,e=g.length;d<e;++d){var c=a.predicateValues[d],f=b.predicateValues[d],n=0;c.type===f.type?c.value!==f.value&&(n=c.value<f.value?-1:1):n=c.type<f.type?-1:1;if(c=n*g[d].descending)break}return c});return a=a.map(function(a){return a.value})}}function La(a){z(a)&&(a={link:a});a.restrict=a.restrict||"AC";return na(a)}function Fd(a,b,d,c,e){var f=this,g=[];f.$error=
|
||||
{};f.$$success={};f.$pending=u;f.$name=e(b.name||b.ngForm||"")(d);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;f.$$parentForm=Ib;f.$rollbackViewValue=function(){n(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){n(g,function(a){a.$commitViewValue()})};f.$addControl=function(a){Ra(a.$name,"input");g.push(a);a.$name&&(f[a.$name]=a);a.$$parentForm=f};f.$$renameControl=function(a,b){var c=a.$name;f[c]===a&&delete f[c];f[b]=a;a.$name=b};f.$removeControl=function(a){a.$name&&
|
||||
f[a.$name]===a&&delete f[a.$name];n(f.$pending,function(b,c){f.$setValidity(c,null,a)});n(f.$error,function(b,c){f.$setValidity(c,null,a)});n(f.$$success,function(b,c){f.$setValidity(c,null,a)});ab(g,a);a.$$parentForm=Ib};Gd({ctrl:this,$element:a,set:function(a,b,c){var d=a[b];d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(ab(d,c),0===d.length&&delete a[b])},$animate:c});f.$setDirty=function(){c.removeClass(a,Wa);c.addClass(a,Jb);f.$dirty=!0;f.$pristine=!1;f.$$parentForm.$setDirty()};
|
||||
f.$setPristine=function(){c.setClass(a,Wa,Jb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;n(g,function(a){a.$setPristine()})};f.$setUntouched=function(){n(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){c.addClass(a,"ng-submitted");f.$submitted=!0;f.$$parentForm.$setSubmitted()}}function kc(a){a.$formatters.push(function(b){return a.$isEmpty(b)?b:b.toString()})}function jb(a,b,d,c,e,f){var g=F(b[0].type);if(!e.android){var h=!1;b.on("compositionstart",function(a){h=!0});
|
||||
b.on("compositionend",function(){h=!1;k()})}var k=function(a){l&&(f.defer.cancel(l),l=null);if(!h){var e=b.val();a=a&&a.type;"password"===g||d.ngTrim&&"false"===d.ngTrim||(e=U(e));(c.$viewValue!==e||""===e&&c.$$hasNativeValidators)&&c.$setViewValue(e,a)}};if(e.hasEvent("input"))b.on("input",k);else{var l,m=function(a,b,c){l||(l=f.defer(function(){l=null;b&&b.value===c||k(a)}))};b.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||m(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut",
|
||||
m)}b.on("change",k);c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Kb(a,b){return function(d,c){var e,f;if(da(d))return d;if(E(d)){'"'==d.charAt(0)&&'"'==d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(kg.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,
|
||||
mm:0,ss:0,sss:0},n(e,function(a,c){c<b.length&&(f[b[c]]=+a)}),new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function kb(a,b,d,c){return function(e,f,g,h,k,l,m){function r(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function n(a){return y(a)&&!da(a)?d(a)||u:a}Hd(e,f,g,h);jb(e,f,g,h,k,l);var A=h&&h.$options&&h.$options.timezone,v;h.$$parserName=a;h.$parsers.push(function(a){return h.$isEmpty(a)?null:b.test(a)?(a=d(a,v),A&&(a=Pb(a,A)),a):u});h.$formatters.push(function(a){if(a&&
|
||||
!da(a))throw lb("datefmt",a);if(r(a))return(v=a)&&A&&(v=Pb(v,A,!0)),m("date")(a,c,A);v=null;return""});if(y(g.min)||g.ngMin){var s;h.$validators.min=function(a){return!r(a)||q(s)||d(a)>=s};g.$observe("min",function(a){s=n(a);h.$validate()})}if(y(g.max)||g.ngMax){var p;h.$validators.max=function(a){return!r(a)||q(p)||d(a)<=p};g.$observe("max",function(a){p=n(a);h.$validate()})}}}function Hd(a,b,d,c){(c.$$hasNativeValidators=H(b[0].validity))&&c.$parsers.push(function(a){var c=b.prop("validity")||{};
|
||||
return c.badInput&&!c.typeMismatch?u:a})}function Id(a,b,d,c,e){if(y(c)){a=a(c);if(!a.constant)throw lb("constexpr",d,c);return a(b)}return e}function lc(a,b){a="ngClass"+a;return["$animate",function(d){function c(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],m=0;m<b.length;m++)if(e==b[m])continue a;c.push(e)}return c}function e(a){var b=[];return I(a)?(n(a,function(a){b=b.concat(e(a))}),b):E(a)?a.split(" "):H(a)?(n(a,function(a,c){a&&(b=b.concat(c.split(" ")))}),b):a}return{restrict:"AC",
|
||||
link:function(f,g,h){function k(a,b){var c=g.data("$classCounts")||$(),d=[];n(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",c);return d.join(" ")}function l(a){if(!0===b||f.$index%2===b){var l=e(a||[]);if(!m){var n=k(l,1);h.$addClass(n)}else if(!ma(a,m)){var q=e(m),n=c(l,q),l=c(q,l),n=k(n,1),l=k(l,-1);n&&n.length&&d.addClass(g,n);l&&l.length&&d.removeClass(g,l)}}m=ia(a)}var m;f.$watch(h[a],l,!0);h.$observe("class",function(b){l(f.$eval(h[a]))});"ngClass"!==
|
||||
a&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var l=e(f.$eval(h[a]));g===b?(g=k(l,1),h.$addClass(g)):(g=k(l,-1),h.$removeClass(g))}})}}}]}function Gd(a){function b(a,b){b&&!f[a]?(k.addClass(e,a),f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}function d(a,c){a=a?"-"+zc(a,"-"):"";b(mb+a,!0===c);b(Jd+a,!1===c)}var c=a.ctrl,e=a.$element,f={},g=a.set,h=a.unset,k=a.$animate;f[Jd]=!(f[mb]=e.hasClass(mb));c.$setValidity=function(a,e,f){q(e)?(c.$pending||(c.$pending={}),g(c.$pending,a,f)):(c.$pending&&
|
||||
h(c.$pending,a,f),Kd(c.$pending)&&(c.$pending=u));$a(e)?e?(h(c.$error,a,f),g(c.$$success,a,f)):(g(c.$error,a,f),h(c.$$success,a,f)):(h(c.$error,a,f),h(c.$$success,a,f));c.$pending?(b(Ld,!0),c.$valid=c.$invalid=u,d("",null)):(b(Ld,!1),c.$valid=Kd(c.$error),c.$invalid=!c.$valid,d("",c.$valid));e=c.$pending&&c.$pending[a]?u:c.$error[a]?!1:c.$$success[a]?!0:null;d(a,e);c.$$parentForm.$setValidity(a,e,c)}}function Kd(a){if(a)for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}var lg=/^\/(.+)\/([a-z]*)$/,
|
||||
F=function(a){return E(a)?a.toLowerCase():a},qa=Object.prototype.hasOwnProperty,sb=function(a){return E(a)?a.toUpperCase():a},Ha,B,oa,ra=[].slice,Pf=[].splice,mg=[].push,sa=Object.prototype.toString,rc=Object.getPrototypeOf,Aa=G("ng"),fa=S.angular||(S.angular={}),Sb,nb=0;Ha=X.documentMode;x.$inject=[];Ya.$inject=[];var I=Array.isArray,Vd=/^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/,U=function(a){return E(a)?a.trim():a},ud=function(a){return a.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,
|
||||
"\\$1").replace(/\x08/g,"\\x08")},Ba=function(){if(!y(Ba.rules)){var a=X.querySelector("[ng-csp]")||X.querySelector("[data-ng-csp]");if(a){var b=a.getAttribute("ng-csp")||a.getAttribute("data-ng-csp");Ba.rules={noUnsafeEval:!b||-1!==b.indexOf("no-unsafe-eval"),noInlineStyle:!b||-1!==b.indexOf("no-inline-style")}}else{a=Ba;try{new Function(""),b=!1}catch(d){b=!0}a.rules={noUnsafeEval:b,noInlineStyle:!1}}}return Ba.rules},pb=function(){if(y(pb.name_))return pb.name_;var a,b,d=Oa.length,c,e;for(b=0;b<
|
||||
d;++b)if(c=Oa[b],a=X.querySelector("["+c.replace(":","\\:")+"jq]")){e=a.getAttribute(c+"jq");break}return pb.name_=e},Oa=["ng-","data-ng-","ng:","x-ng-"],be=/[A-Z]/g,Ac=!1,Rb,Na=3,fe={full:"1.4.8",major:1,minor:4,dot:8,codeName:"ice-manipulation"};N.expando="ng339";var gb=N.cache={},Ff=1;N._data=function(a){return this.cache[a[this.expando]]||{}};var Af=/([\:\-\_]+(.))/g,Bf=/^moz([A-Z])/,xb={mouseleave:"mouseout",mouseenter:"mouseover"},Ub=G("jqLite"),Ef=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Tb=/<|&#?\w+;/,
|
||||
Cf=/<([\w:-]+)/,Df=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,ka={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ka.optgroup=ka.option;ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead;ka.th=ka.td;var Kf=Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&
|
||||
16)},Pa=N.prototype={ready:function(a){function b(){d||(d=!0,a())}var d=!1;"complete"===X.readyState?setTimeout(b):(this.on("DOMContentLoaded",b),N(S).on("load",b))},toString:function(){var a=[];n(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?B(this[a]):B(this[this.length+a])},length:0,push:mg,sort:[].sort,splice:[].splice},Cb={};n("multiple selected checked disabled readOnly required open".split(" "),function(a){Cb[F(a)]=a});var Rc={};n("input select option textarea button form details".split(" "),
|
||||
function(a){Rc[a]=!0});var Zc={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};n({data:Wb,removeData:vb,hasData:function(a){for(var b in gb[a.ng339])return!0;return!1}},function(a,b){N[b]=a});n({data:Wb,inheritedData:Bb,scope:function(a){return B.data(a,"$scope")||Bb(a.parentNode||a,["$isolateScope","$scope"])},isolateScope:function(a){return B.data(a,"$isolateScope")||B.data(a,"$isolateScopeNoTemplate")},controller:Oc,injector:function(a){return Bb(a,
|
||||
"$injector")},removeAttr:function(a,b){a.removeAttribute(b)},hasClass:yb,css:function(a,b,d){b=fb(b);if(y(d))a.style[b]=d;else return a.style[b]},attr:function(a,b,d){var c=a.nodeType;if(c!==Na&&2!==c&&8!==c)if(c=F(b),Cb[c])if(y(d))d?(a[b]=!0,a.setAttribute(b,c)):(a[b]=!1,a.removeAttribute(c));else return a[b]||(a.attributes.getNamedItem(b)||x).specified?c:u;else if(y(d))a.setAttribute(b,d);else if(a.getAttribute)return a=a.getAttribute(b,2),null===a?u:a},prop:function(a,b,d){if(y(d))a[b]=d;else return a[b]},
|
||||
text:function(){function a(a,d){if(q(d)){var c=a.nodeType;return 1===c||c===Na?a.textContent:""}a.textContent=d}a.$dv="";return a}(),val:function(a,b){if(q(b)){if(a.multiple&&"select"===ta(a)){var d=[];n(a.options,function(a){a.selected&&d.push(a.value||a.text)});return 0===d.length?null:d}return a.value}a.value=b},html:function(a,b){if(q(b))return a.innerHTML;ub(a,!0);a.innerHTML=b},empty:Pc},function(a,b){N.prototype[b]=function(b,c){var e,f,g=this.length;if(a!==Pc&&q(2==a.length&&a!==yb&&a!==Oc?
|
||||
b:c)){if(H(b)){for(e=0;e<g;e++)if(a===Wb)a(this[e],b);else for(f in b)a(this[e],f,b[f]);return this}e=a.$dv;g=q(e)?Math.min(g,1):g;for(f=0;f<g;f++){var h=a(this[f],b,c);e=e?e+h:h}return e}for(e=0;e<g;e++)a(this[e],b,c);return this}});n({removeData:vb,on:function(a,b,d,c){if(y(c))throw Ub("onargs");if(Kc(a)){c=wb(a,!0);var e=c.events,f=c.handle;f||(f=c.handle=Hf(a,e));c=0<=b.indexOf(" ")?b.split(" "):[b];for(var g=c.length,h=function(b,c,g){var h=e[b];h||(h=e[b]=[],h.specialHandlerWrapper=c,"$destroy"===
|
||||
b||g||a.addEventListener(b,f,!1));h.push(d)};g--;)b=c[g],xb[b]?(h(xb[b],Jf),h(b,u,!0)):h(b)}},off:Nc,one:function(a,b,d){a=B(a);a.on(b,function e(){a.off(b,d);a.off(b,e)});a.on(b,d)},replaceWith:function(a,b){var d,c=a.parentNode;ub(a);n(new N(b),function(b){d?c.insertBefore(b,d.nextSibling):c.replaceChild(b,a);d=b})},children:function(a){var b=[];n(a.childNodes,function(a){1===a.nodeType&&b.push(a)});return b},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,b){var d=
|
||||
a.nodeType;if(1===d||11===d){b=new N(b);for(var d=0,c=b.length;d<c;d++)a.appendChild(b[d])}},prepend:function(a,b){if(1===a.nodeType){var d=a.firstChild;n(new N(b),function(b){a.insertBefore(b,d)})}},wrap:function(a,b){b=B(b).eq(0).clone()[0];var d=a.parentNode;d&&d.replaceChild(b,a);b.appendChild(a)},remove:Xb,detach:function(a){Xb(a,!0)},after:function(a,b){var d=a,c=a.parentNode;b=new N(b);for(var e=0,f=b.length;e<f;e++){var g=b[e];c.insertBefore(g,d.nextSibling);d=g}},addClass:Ab,removeClass:zb,
|
||||
toggleClass:function(a,b,d){b&&n(b.split(" "),function(b){var e=d;q(e)&&(e=!yb(a,b));(e?Ab:zb)(a,b)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,b){return a.getElementsByTagName?a.getElementsByTagName(b):[]},clone:Vb,triggerHandler:function(a,b,d){var c,e,f=b.type||b,g=wb(a);if(g=(g=g&&g.events)&&g[f])c={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},
|
||||
stopImmediatePropagation:function(){this.immediatePropagationStopped=!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:x,type:f,target:a},b.type&&(c=M(c,b)),b=ia(g),e=d?[c].concat(d):[c],n(b,function(b){c.isImmediatePropagationStopped()||b.apply(a,e)})}},function(a,b){N.prototype[b]=function(b,c,e){for(var f,g=0,h=this.length;g<h;g++)q(f)?(f=a(this[g],b,c,e),y(f)&&(f=B(f))):Mc(f,a(this[g],b,c,e));return y(f)?f:this};N.prototype.bind=N.prototype.on;
|
||||
N.prototype.unbind=N.prototype.off});Sa.prototype={put:function(a,b){this[Ca(a,this.nextUid)]=b},get:function(a){return this[Ca(a,this.nextUid)]},remove:function(a){var b=this[a=Ca(a,this.nextUid)];delete this[a];return b}};var yf=[function(){this.$get=[function(){return Sa}]}],Tc=/^[^\(]*\(\s*([^\)]*)\)/m,ng=/,/,og=/^\s*(_?)(\S+?)\1\s*$/,Sc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Da=G("$injector");eb.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw E(d)&&
|
||||
d||(d=a.name||Lf(a)),Da("strictdi",d);b=a.toString().replace(Sc,"");b=b.match(Tc);n(b[1].split(ng),function(a){a.replace(og,function(a,b,d){c.push(d)})})}a.$inject=c}}else I(a)?(b=a.length-1,Qa(a[b],"fn"),c=a.slice(0,b)):Qa(a,"fn",!0);return c};var Md=G("$animate"),Ue=function(){this.$get=["$q","$$rAF",function(a,b){function d(){}d.all=x;d.chain=x;d.prototype={end:x,cancel:x,resume:x,pause:x,complete:x,then:function(c,d){return a(function(a){b(function(){a()})}).then(c,d)}};return d}]},Te=function(){var a=
|
||||
new Sa,b=[];this.$get=["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=E(b)?b.split(" "):I(b)?b:[],n(b,function(b){b&&(d=!0,a[b]=c)}));return d}function f(){n(b,function(b){var c=a.get(b);if(c){var d=Mf(b.attr("class")),e="",f="";n(c,function(a,b){a!==!!d[b]&&(a?e+=(e.length?" ":"")+b:f+=(f.length?" ":"")+b)});n(b,function(a){e&&Ab(a,e);f&&zb(a,f)});a.remove(b)}});b.length=0}return{enabled:x,on:x,off:x,pin:x,push:function(g,h,k,l){l&&l();k=k||{};k.from&&g.css(k.from);
|
||||
k.to&&g.css(k.to);if(k.addClass||k.removeClass)if(h=k.addClass,l=k.removeClass,k=a.get(g)||{},h=e(k,h,!0),l=e(k,l,!1),h||l)a.put(g,k),b.push(g),1===b.length&&c.$$postDigest(f);return new d}}}]},Re=["$provide",function(a){var b=this;this.$$registeredAnimations=Object.create(null);this.register=function(d,c){if(d&&"."!==d.charAt(0))throw Md("notcsel",d);var e=d+"-animation";b.$$registeredAnimations[d.substr(1)]=e;a.factory(e,c)};this.classNameFilter=function(a){if(1===arguments.length&&(this.$$classNameFilter=
|
||||
a instanceof RegExp?a:null)&&/(\s+|\/)ng-animate(\s+|\/)/.test(this.$$classNameFilter.toString()))throw Md("nongcls","ng-animate");return this.$$classNameFilter};this.$get=["$$animateQueue",function(a){function b(a,c,d){if(d){var h;a:{for(h=0;h<d.length;h++){var k=d[h];if(1===k.nodeType){h=k;break a}}h=void 0}!h||h.parentNode||h.previousElementSibling||(d=null)}d?d.after(a):c.prepend(a)}return{on:a.on,off:a.off,pin:a.pin,enabled:a.enabled,cancel:function(a){a.end&&a.end()},enter:function(e,f,g,h){f=
|
||||
f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"enter",Ea(h))},move:function(e,f,g,h){f=f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"move",Ea(h))},leave:function(b,c){return a.push(b,"leave",Ea(c),function(){b.remove()})},addClass:function(b,c,g){g=Ea(g);g.addClass=hb(g.addclass,c);return a.push(b,"addClass",g)},removeClass:function(b,c,g){g=Ea(g);g.removeClass=hb(g.removeClass,c);return a.push(b,"removeClass",g)},setClass:function(b,c,g,h){h=Ea(h);h.addClass=hb(h.addClass,
|
||||
c);h.removeClass=hb(h.removeClass,g);return a.push(b,"setClass",h)},animate:function(b,c,g,h,k){k=Ea(k);k.from=k.from?M(k.from,c):c;k.to=k.to?M(k.to,g):g;k.tempClasses=hb(k.tempClasses,h||"ng-inline-animate");return a.push(b,"animate",k)}}}]}],Se=function(){this.$get=["$$rAF","$q",function(a,b){var d=function(){};d.prototype={done:function(a){this.defer&&this.defer[!0===a?"reject":"resolve"]()},end:function(){this.done()},cancel:function(){this.done(!0)},getPromise:function(){this.defer||(this.defer=
|
||||
b.defer());return this.defer.promise},then:function(a,b){return this.getPromise().then(a,b)},"catch":function(a){return this.getPromise()["catch"](a)},"finally":function(a){return this.getPromise()["finally"](a)}};return function(b,e){function f(){a(function(){e.addClass&&(b.addClass(e.addClass),e.addClass=null);e.removeClass&&(b.removeClass(e.removeClass),e.removeClass=null);e.to&&(b.css(e.to),e.to=null);g||h.done();g=!0});return h}e.cleanupStyles&&(e.from=e.to=null);e.from&&(b.css(e.from),e.from=
|
||||
null);var g,h=new d;return{start:f,end:f}}}]},ha=G("$compile");Cc.$inject=["$provide","$$sanitizeUriProvider"];var Vc=/^((?:x|data)[\:\-_])/i,Qf=G("$controller"),Uc=/^(\S+)(\s+as\s+(\w+))?$/,$e=function(){this.$get=["$document",function(a){return function(b){b?!b.nodeType&&b instanceof B&&(b=b[0]):b=a[0].body;return b.offsetWidth+1}}]},$c="application/json",ac={"Content-Type":$c+";charset=utf-8"},Sf=/^\[|^\{(?!\{)/,Tf={"[":/]$/,"{":/}$/},Rf=/^\)\]\}',?\n/,pg=G("$http"),dd=function(a){return function(){throw pg("legacy",
|
||||
a);}},Ja=fa.$interpolateMinErr=G("$interpolate");Ja.throwNoconcat=function(a){throw Ja("noconcat",a);};Ja.interr=function(a,b){return Ja("interr",a,b.toString())};var qg=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,Vf={http:80,https:443,ftp:21},Db=G("$location"),rg={$$html5:!1,$$replace:!1,absUrl:Eb("$$absUrl"),url:function(a){if(q(a))return this.$$url;var b=qg.exec(a);(b[1]||""===a)&&this.path(decodeURIComponent(b[1]));(b[2]||b[1]||""===a)&&this.search(b[3]||"");this.hash(b[5]||"");return this},protocol:Eb("$$protocol"),
|
||||
host:Eb("$$host"),port:Eb("$$port"),path:id("$$path",function(a){a=null!==a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,b){switch(arguments.length){case 0:return this.$$search;case 1:if(E(a)||Q(a))a=a.toString(),this.$$search=xc(a);else if(H(a))a=bb(a,{}),n(a,function(b,c){null==b&&delete a[c]}),this.$$search=a;else throw Db("isrcharg");break;default:q(b)||null===b?delete this.$$search[a]:this.$$search[a]=b}this.$$compose();return this},hash:id("$$hash",function(a){return null!==
|
||||
a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};n([hd,dc,cc],function(a){a.prototype=Object.create(rg);a.prototype.state=function(b){if(!arguments.length)return this.$$state;if(a!==cc||!this.$$html5)throw Db("nostate");this.$$state=q(b)?null:b;return this}});var ba=G("$parse"),Wf=Function.prototype.call,Xf=Function.prototype.apply,Yf=Function.prototype.bind,Lb=$();n("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Lb[a]=!0});var sg={n:"\n",f:"\f",r:"\r",
|
||||
t:"\t",v:"\v","'":"'",'"':'"'},fc=function(a){this.options=a};fc.prototype={constructor:fc,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(a))this.readIdent();else if(this.is(a,"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++;
|
||||
else{var b=a+this.peek(),d=b+this.peek(2),c=Lb[b],e=Lb[d];Lb[a]||c||e?(a=e?d:c?b:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+=a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,b){return-1!==b.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||
|
||||
"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,b,d){d=d||this.index;b=y(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw ba("lexerr",a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index<this.text.length;){var d=F(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var c=this.peek();
|
||||
if("e"==d&&this.isExpOperator(c))a+=d;else if(this.isExpOperator(d)&&c&&this.isNumber(c)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||c&&this.isNumber(c)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:b,text:a,constant:!0,value:Number(a)})},readIdent:function(){for(var a=this.index;this.index<this.text.length;){var b=this.text.charAt(this.index);if(!this.isIdent(b)&&!this.isNumber(b))break;this.index++}this.tokens.push({index:a,
|
||||
text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var b=this.index;this.index++;for(var d="",c=a,e=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),c=c+f;if(e)"u"===f?(e=this.text.substring(this.index+1,this.index+5),e.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+e+"]"),this.index+=4,d+=String.fromCharCode(parseInt(e,16))):d+=sg[f]||f,e=!1;else if("\\"===f)e=!0;else{if(f===a){this.index++;this.tokens.push({index:b,text:c,constant:!0,
|
||||
value:d});return}d+=f}this.index++}this.throwError("Unterminated quote",b)}};var s=function(a,b){this.lexer=a;this.options=b};s.Program="Program";s.ExpressionStatement="ExpressionStatement";s.AssignmentExpression="AssignmentExpression";s.ConditionalExpression="ConditionalExpression";s.LogicalExpression="LogicalExpression";s.BinaryExpression="BinaryExpression";s.UnaryExpression="UnaryExpression";s.CallExpression="CallExpression";s.MemberExpression="MemberExpression";s.Identifier="Identifier";s.Literal=
|
||||
"Literal";s.ArrayExpression="ArrayExpression";s.Property="Property";s.ObjectExpression="ObjectExpression";s.ThisExpression="ThisExpression";s.NGValueParameter="NGValueParameter";s.prototype={ast:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.program();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);return a},program:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.expressionStatement()),!this.expect(";"))return{type:s.Program,
|
||||
body:a}},expressionStatement:function(){return{type:s.ExpressionStatement,expression:this.filterChain()}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary();this.expect("=")&&(a={type:s.AssignmentExpression,left:a,right:this.assignment(),operator:"="});return a},ternary:function(){var a=this.logicalOR(),b,d;return this.expect("?")&&(b=this.expression(),this.consume(":"))?
|
||||
(d=this.expression(),{type:s.ConditionalExpression,test:a,alternate:b,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:s.LogicalExpression,operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a=this.equality();this.expect("&&");)a={type:s.LogicalExpression,operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),b;b=this.expect("==","!=","===","!==");)a={type:s.BinaryExpression,
|
||||
operator:b.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),b;b=this.expect("<",">","<=",">=");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),b;b=this.expect("+","-");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:s.BinaryExpression,operator:b.text,
|
||||
left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:s.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.constants.hasOwnProperty(this.peek().text)?a=bb(this.constants[this.consume().text]):this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():
|
||||
this.throwError("not a primary expression",this.peek());for(var b;b=this.expect("(","[",".");)"("===b.text?(a={type:s.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):"["===b.text?(a={type:s.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:s.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:s.CallExpression,callee:this.identifier(),
|
||||
arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b},parseArguments:function(){var a=[];if(")"!==this.peekToken().text){do a.push(this.expression());while(this.expect(","))}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:s.Identifier,name:a.text}},constant:function(){return{type:s.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;
|
||||
a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:s.ArrayExpression,elements:a}},object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;b={type:s.Property,kind:"init"};this.peek().constant?b.key=this.constant():this.peek().identifier?b.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");b.value=this.expression();a.push(b)}while(this.expect(","))}this.consume("}");return{type:s.ObjectExpression,properties:a}},
|
||||
throwError:function(a,b){throw ba("syntax",b.text,a,b.index+1,this.text,this.text.substring(b.index));},consume:function(a){if(0===this.tokens.length)throw ba("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw ba("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a,b,d,c,e){if(this.tokens.length>a){a=this.tokens[a];
|
||||
var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1},expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))?(this.tokens.shift(),a):!1},constants:{"true":{type:s.Literal,value:!0},"false":{type:s.Literal,value:!1},"null":{type:s.Literal,value:null},undefined:{type:s.Literal,value:u},"this":{type:s.ThisExpression}}};rd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:b,fn:{vars:[],body:[],own:{}},assign:{vars:[],
|
||||
body:[],own:{}},inputs:[]};W(c,d.$filter);var e="",f;this.stage="assign";if(f=pd(c))this.state.computing="assign",e=this.nextId(),this.recurse(f,e),this.return_(e),e="fn.assign="+this.generateFunction("assign","s,v,l");f=nd(c.body);d.stage="inputs";n(f,function(a,b){var c="fn"+b;d.state[c]={vars:[],body:[],own:{}};d.state.computing=c;var e=d.nextId();d.recurse(a,e);d.return_(e);d.state.inputs.push(c);a.watchId=b});this.state.computing="fn";this.stage="main";this.recurse(c);e='"'+this.USE+" "+this.STRICT+
|
||||
'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+e+this.watchFns()+"return fn;";e=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","getStringValue","ensureSafeAssignContext","ifDefined","plus","text",e))(this.$filter,Va,xa,kd,jd,ld,Zf,md,a);this.state=this.stage=u;e.literal=qd(c);e.constant=c.constant;return e},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;n(b,function(b){a.push("var "+b+"="+d.generateFunction(b,
|
||||
"s"))});b.length&&a.push("fn.inputs=["+b.join(",")+"];");return a.join("")},generateFunction:function(a,b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;n(this.state.filters,function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,b,
|
||||
d,c,e,f){var g,h,k=this,l,m;c=c||x;if(!f&&y(a.watchId))b=b||this.nextId(),this.if_("i",this.lazyAssign(b,this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case s.Program:n(a.body,function(b,c){k.recurse(b.expression,u,u,function(a){h=a});c!==a.body.length-1?k.current().body.push(h,";"):k.return_(h)});break;case s.Literal:m=this.escape(a.value);this.assign(b,m);c(m);break;case s.UnaryExpression:this.recurse(a.argument,u,u,function(a){h=a});m=a.operator+"("+this.ifDefined(h,
|
||||
0)+")";this.assign(b,m);c(m);break;case s.BinaryExpression:this.recurse(a.left,u,u,function(a){g=a});this.recurse(a.right,u,u,function(a){h=a});m="+"===a.operator?this.plus(g,h):"-"===a.operator?this.ifDefined(g,0)+a.operator+this.ifDefined(h,0):"("+g+")"+a.operator+"("+h+")";this.assign(b,m);c(m);break;case s.LogicalExpression:b=b||this.nextId();k.recurse(a.left,b);k.if_("&&"===a.operator?b:k.not(b),k.lazyRecurse(a.right,b));c(b);break;case s.ConditionalExpression:b=b||this.nextId();k.recurse(a.test,
|
||||
b);k.if_(b,k.lazyRecurse(a.alternate,b),k.lazyRecurse(a.consequent,b));c(b);break;case s.Identifier:b=b||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",a.name)+"?l:s"),d.computed=!1,d.name=a.name);Va(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){e&&1!==e&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(b,k.nonComputedMember("s",
|
||||
a.name))})},b&&k.lazyAssign(b,k.nonComputedMember("l",a.name)));(k.state.expensiveChecks||Fb(a.name))&&k.addEnsureSafeObject(b);c(b);break;case s.MemberExpression:g=d&&(d.context=this.nextId())||this.nextId();b=b||this.nextId();k.recurse(a.object,g,u,function(){k.if_(k.notNull(g),function(){if(a.computed)h=k.nextId(),k.recurse(a.property,h),k.getStringValue(h),k.addEnsureSafeMemberName(h),e&&1!==e&&k.if_(k.not(k.computedMember(g,h)),k.lazyAssign(k.computedMember(g,h),"{}")),m=k.ensureSafeObject(k.computedMember(g,
|
||||
h)),k.assign(b,m),d&&(d.computed=!0,d.name=h);else{Va(a.property.name);e&&1!==e&&k.if_(k.not(k.nonComputedMember(g,a.property.name)),k.lazyAssign(k.nonComputedMember(g,a.property.name),"{}"));m=k.nonComputedMember(g,a.property.name);if(k.state.expensiveChecks||Fb(a.property.name))m=k.ensureSafeObject(m);k.assign(b,m);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(b,"undefined")});c(b)},!!e);break;case s.CallExpression:b=b||this.nextId();a.filter?(h=k.filter(a.callee.name),l=[],n(a.arguments,
|
||||
function(a){var b=k.nextId();k.recurse(a,b);l.push(b)}),m=h+"("+l.join(",")+")",k.assign(b,m),c(b)):(h=k.nextId(),g={},l=[],k.recurse(a.callee,h,g,function(){k.if_(k.notNull(h),function(){k.addEnsureSafeFunction(h);n(a.arguments,function(a){k.recurse(a,k.nextId(),u,function(a){l.push(k.ensureSafeObject(a))})});g.name?(k.state.expensiveChecks||k.addEnsureSafeObject(g.context),m=k.member(g.context,g.name,g.computed)+"("+l.join(",")+")"):m=h+"("+l.join(",")+")";m=k.ensureSafeObject(m);k.assign(b,m)},
|
||||
function(){k.assign(b,"undefined")});c(b)}));break;case s.AssignmentExpression:h=this.nextId();g={};if(!od(a.left))throw ba("lval");this.recurse(a.left,u,g,function(){k.if_(k.notNull(g.context),function(){k.recurse(a.right,h);k.addEnsureSafeObject(k.member(g.context,g.name,g.computed));k.addEnsureSafeAssignContext(g.context);m=k.member(g.context,g.name,g.computed)+a.operator+h;k.assign(b,m);c(b||m)})},1);break;case s.ArrayExpression:l=[];n(a.elements,function(a){k.recurse(a,k.nextId(),u,function(a){l.push(a)})});
|
||||
m="["+l.join(",")+"]";this.assign(b,m);c(m);break;case s.ObjectExpression:l=[];n(a.properties,function(a){k.recurse(a.value,k.nextId(),u,function(b){l.push(k.escape(a.key.type===s.Identifier?a.key.name:""+a.key.value)+":"+b)})});m="{"+l.join(",")+"}";this.assign(b,m);c(m);break;case s.ThisExpression:this.assign(b,"s");c("s");break;case s.NGValueParameter:this.assign(b,"v"),c("v")}},getHasOwnProperty:function(a,b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+
|
||||
this.escape(b)+" in "+a+")"));return c[d]},assign:function(a,b){if(a)return this.current().body.push(a,"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a,
|
||||
"){");b();c.push("}");d&&(c.push("else{"),d(),c.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,b){return a+"."+b},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a),";")},
|
||||
addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a),";")},addEnsureSafeAssignContext:function(a){this.current().body.push(this.ensureSafeAssignContext(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},getStringValue:function(a){this.assign(a,"getStringValue("+a+",text)")},ensureSafeAssignContext:function(a){return"ensureSafeAssignContext("+
|
||||
a+",text)"},lazyRecurse:function(a,b,d,c,e,f){var g=this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(E(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(Q(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===
|
||||
typeof a)return"undefined";throw ba("esc");},nextId:function(a,b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};sd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=b;W(c,d.$filter);var e,f;if(e=pd(c))f=this.recurse(e);e=nd(c.body);var g;e&&(g=[],n(e,function(a,b){var c=d.recurse(a);a.input=c;g.push(c);a.watchId=b}));var h=[];n(c.body,function(a){h.push(d.recurse(a.expression))});
|
||||
e=0===c.body.length?function(){}:1===c.body.length?h[0]:function(a,b){var c;n(h,function(d){c=d(a,b)});return c};f&&(e.assign=function(a,b,c){return f(a,c,b)});g&&(e.inputs=g);e.literal=qd(c);e.constant=c.constant;return e},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case s.Literal:return this.value(a.value,b);case s.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case s.BinaryExpression:return c=this.recurse(a.left),
|
||||
e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case s.Identifier:return Va(a.name,f.expression),f.identifier(a.name,f.expensiveChecks||Fb(a.name),b,d,f.expression);case s.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(Va(a.property.name,
|
||||
f.expression),e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d,f.expression):this.nonComputedMember(c,e,f.expensiveChecks,b,d,f.expression);case s.CallExpression:return g=[],n(a.arguments,function(a){g.push(f.recurse(a))}),a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var r=[],n=0;n<g.length;++n)r.push(g[n](a,c,d,f));a=e.apply(u,r,f);return b?{context:u,name:u,value:a}:a}:function(a,
|
||||
c,d,m){var r=e(a,c,d,m),n;if(null!=r.value){xa(r.context,f.expression);kd(r.value,f.expression);n=[];for(var q=0;q<g.length;++q)n.push(xa(g[q](a,c,d,m),f.expression));n=xa(r.value.apply(r.context,n),f.expression)}return b?{value:n}:n};case s.AssignmentExpression:return c=this.recurse(a.left,!0,1),e=this.recurse(a.right),function(a,d,g,m){var n=c(a,d,g,m);a=e(a,d,g,m);xa(n.value,f.expression);ld(n.context);n.context[n.name]=a;return b?{value:a}:a};case s.ArrayExpression:return g=[],n(a.elements,function(a){g.push(f.recurse(a))}),
|
||||
function(a,c,d,e){for(var f=[],n=0;n<g.length;++n)f.push(g[n](a,c,d,e));return b?{value:f}:f};case s.ObjectExpression:return g=[],n(a.properties,function(a){g.push({key:a.key.type===s.Identifier?a.key.name:""+a.key.value,value:f.recurse(a.value)})}),function(a,c,d,e){for(var f={},n=0;n<g.length;++n)f[g[n].key]=g[n].value(a,c,d,e);return b?{value:f}:f};case s.ThisExpression:return function(a){return b?{value:a}:a};case s.NGValueParameter:return function(a,c,d,e){return b?{value:d}:d}}},"unary+":function(a,
|
||||
b){return function(d,c,e,f){d=a(d,c,e,f);d=y(d)?+d:0;return b?{value:d}:d}},"unary-":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=y(d)?-d:0;return b?{value:d}:d}},"unary!":function(a,b){return function(d,c,e,f){d=!a(d,c,e,f);return b?{value:d}:d}},"binary+":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);h=md(h,c);return d?{value:h}:h}},"binary-":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);h=(y(h)?h:0)-(y(c)?c:0);return d?{value:h}:h}},"binary*":function(a,
|
||||
b,d){return function(c,e,f,g){c=a(c,e,f,g)*b(c,e,f,g);return d?{value:c}:c}},"binary/":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)/b(c,e,f,g);return d?{value:c}:c}},"binary%":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)%b(c,e,f,g);return d?{value:c}:c}},"binary===":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)===b(c,e,f,g);return d?{value:c}:c}},"binary!==":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!==b(c,e,f,g);return d?{value:c}:c}},"binary==":function(a,b,
|
||||
d){return function(c,e,f,g){c=a(c,e,f,g)==b(c,e,f,g);return d?{value:c}:c}},"binary!=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!=b(c,e,f,g);return d?{value:c}:c}},"binary<":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<b(c,e,f,g);return d?{value:c}:c}},"binary>":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c,
|
||||
e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,h){e=a(e,f,g,h)?b(e,f,g,h):d(e,f,g,h);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:u,name:u,value:a}:a}},identifier:function(a,b,d,c,e){return function(f,g,h,k){f=
|
||||
g&&a in g?g:f;c&&1!==c&&f&&!f[a]&&(f[a]={});g=f?f[a]:u;b&&xa(g,e);return d?{context:f,name:a,value:g}:g}},computedMember:function(a,b,d,c,e){return function(f,g,h,k){var l=a(f,g,h,k),m,n;null!=l&&(m=b(f,g,h,k),m=jd(m),Va(m,e),c&&1!==c&&l&&!l[m]&&(l[m]={}),n=l[m],xa(n,e));return d?{context:l,name:m,value:n}:n}},nonComputedMember:function(a,b,d,c,e,f){return function(g,h,k,l){g=a(g,h,k,l);e&&1!==e&&g&&!g[b]&&(g[b]={});h=null!=g?g[b]:u;(d||Fb(b))&&xa(h,f);return c?{context:g,name:b,value:h}:h}},inputs:function(a,
|
||||
b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};var gc=function(a,b,d){this.lexer=a;this.$filter=b;this.options=d;this.ast=new s(this.lexer);this.astCompiler=d.csp?new sd(this.ast,b):new rd(this.ast,b)};gc.prototype={constructor:gc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};$();$();var $f=Object.prototype.valueOf,ya=G("$sce"),la={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},ha=G("$compile"),Y=X.createElement("a"),wd=wa(S.location.href);
|
||||
xd.$inject=["$document"];Jc.$inject=["$provide"];yd.$inject=["$locale"];Ad.$inject=["$locale"];var ic=".",jg={yyyy:ca("FullYear",4),yy:ca("FullYear",2,0,!0),y:ca("FullYear",1),MMMM:Hb("Month"),MMM:Hb("Month",!0),MM:ca("Month",2,1),M:ca("Month",1,1),dd:ca("Date",2),d:ca("Date",1),HH:ca("Hours",2),H:ca("Hours",1),hh:ca("Hours",2,-12),h:ca("Hours",1,-12),mm:ca("Minutes",2),m:ca("Minutes",1),ss:ca("Seconds",2),s:ca("Seconds",1),sss:ca("Milliseconds",3),EEEE:Hb("Day"),EEE:Hb("Day",!0),a:function(a,b){return 12>
|
||||
a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Gb(Math[0<a?"floor":"ceil"](a/60),2)+Gb(Math.abs(a%60),2))},ww:Ed(2),w:Ed(1),G:jc,GG:jc,GGG:jc,GGGG:function(a,b){return 0>=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}},ig=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,hg=/^\-?\d+$/;zd.$inject=["$locale"];var eg=na(F),fg=na(sb);Bd.$inject=["$parse"];var he=na({restrict:"E",compile:function(a,b){if(!b.href&&!b.xlinkHref)return function(a,
|
||||
b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===sa.call(b.prop("href"))?"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),tb={};n(Cb,function(a,b){function d(a,d,e){a.$watch(e[c],function(a){e.$set(b,!!a)})}if("multiple"!=a){var c=va("ng-"+b),e=d;"checked"===a&&(e=function(a,b,e){e.ngModel!==e[c]&&d(a,b,e)});tb[c]=function(){return{restrict:"A",priority:100,link:e}}}});n(Zc,function(a,b){tb[b]=function(){return{priority:100,link:function(a,
|
||||
c,e){if("ngPattern"===b&&"/"==e.ngPattern.charAt(0)&&(c=e.ngPattern.match(lg))){e.$set("ngPattern",new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});n(["src","srcset","href"],function(a){var b=va("ng-"+a);tb[b]=function(){return{priority:99,link:function(d,c,e){var f=a,g=a;"href"===a&&"[object SVGAnimatedString]"===sa.call(c.prop("href"))&&(g="xlinkHref",e.$attr[g]="xlink:href",f=null);e.$observe(b,function(b){b?(e.$set(g,b),Ha&&f&&c.prop(f,e[g])):"href"===a&&e.$set(g,null)})}}}});
|
||||
var Ib={$addControl:x,$$renameControl:function(a,b){a.$name=b},$removeControl:x,$setValidity:x,$setDirty:x,$setPristine:x,$setSubmitted:x};Fd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Nd=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||x}return{name:"form",restrict:a?"EAC":"E",require:["form","^^?form"],controller:Fd,compile:function(d,f){d.addClass(Wa).addClass(mb);var g=f.name?"name":a&&f.ngForm?"ngForm":
|
||||
!1;return{pre:function(a,d,e,f){var n=f[0];if(!("action"in e)){var q=function(b){a.$apply(function(){n.$commitViewValue();n.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",q,!1);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",q,!1)},0,!1)})}(f[1]||n.$$parentForm).$addControl(n);var s=g?c(n.$name):x;g&&(s(a,n),e.$observe(g,function(b){n.$name!==b&&(s(a,u),n.$$parentForm.$$renameControl(n,b),s=c(n.$name),s(a,n))}));d.on("$destroy",function(){n.$$parentForm.$removeControl(n);
|
||||
s(a,u);M(n,Ib)})}}}}}]},ie=Nd(),ve=Nd(!0),kg=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,tg=/^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/,ug=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,vg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Od=/^(\d{4})-(\d{2})-(\d{2})$/,Pd=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,mc=/^(\d{4})-W(\d\d)$/,Qd=/^(\d{4})-(\d\d)$/,
|
||||
Rd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Sd={text:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);kc(c)},date:kb("date",Od,Kb(Od,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":kb("datetimelocal",Pd,Kb(Pd,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:kb("time",Rd,Kb(Rd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:kb("week",mc,function(a,b){if(da(a))return a;if(E(a)){mc.lastIndex=0;var d=mc.exec(a);if(d){var c=+d[1],e=+d[2],f=d=0,g=0,h=0,k=Dd(c),e=7*(e-1);b&&(d=b.getHours(),f=
|
||||
b.getMinutes(),g=b.getSeconds(),h=b.getMilliseconds());return new Date(c,0,k.getDate()+e,d,f,g,h)}}return NaN},"yyyy-Www"),month:kb("month",Qd,Kb(Qd,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f){Hd(a,b,d,c);jb(a,b,d,c,e,f);c.$$parserName="number";c.$parsers.push(function(a){return c.$isEmpty(a)?null:vg.test(a)?parseFloat(a):u});c.$formatters.push(function(a){if(!c.$isEmpty(a)){if(!Q(a))throw lb("numfmt",a);a=a.toString()}return a});if(y(d.min)||d.ngMin){var g;c.$validators.min=function(a){return c.$isEmpty(a)||
|
||||
q(g)||a>=g};d.$observe("min",function(a){y(a)&&!Q(a)&&(a=parseFloat(a,10));g=Q(a)&&!isNaN(a)?a:u;c.$validate()})}if(y(d.max)||d.ngMax){var h;c.$validators.max=function(a){return c.$isEmpty(a)||q(h)||a<=h};d.$observe("max",function(a){y(a)&&!Q(a)&&(a=parseFloat(a,10));h=Q(a)&&!isNaN(a)?a:u;c.$validate()})}},url:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);kc(c);c.$$parserName="url";c.$validators.url=function(a,b){var d=a||b;return c.$isEmpty(d)||tg.test(d)}},email:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);kc(c);
|
||||
c.$$parserName="email";c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||ug.test(d)}},radio:function(a,b,d,c){q(d.name)&&b.attr("name",++nb);b.on("click",function(a){b[0].checked&&c.$setViewValue(d.value,a&&a.type)});c.$render=function(){b[0].checked=d.value==c.$viewValue};d.$observe("value",c.$render)},checkbox:function(a,b,d,c,e,f,g,h){var k=Id(h,a,"ngTrueValue",d.ngTrueValue,!0),l=Id(h,a,"ngFalseValue",d.ngFalseValue,!1);b.on("click",function(a){c.$setViewValue(b[0].checked,a&&
|
||||
a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return ma(a,k)});c.$parsers.push(function(a){return a?k:l})},hidden:x,button:x,submit:x,reset:x,file:x},Dc=["$browser","$sniffer","$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,h){h[0]&&(Sd[F(g.type)]||Sd.text)(e,f,g,h[0],b,a,d,c)}}}}],wg=/^(true|false|\d+)$/,Ne=function(){return{restrict:"A",priority:100,compile:function(a,
|
||||
b){return wg.test(b.ngValue)?function(a,b,e){e.$set("value",a.$eval(e.ngValue))}:function(a,b,e){a.$watch(e.ngValue,function(a){e.$set("value",a)})}}}},ne=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b);return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0];b.$watch(e.ngBind,function(a){c.textContent=q(a)?"":a})}}}}],pe=["$interpolate","$compile",function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));
|
||||
b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=q(a)?"":a})}}}}],oe=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g=b(e.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(c);return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){c.html(a.getTrustedHtml(f(b))||"")})}}}}],Me=na({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),
|
||||
qe=lc("",!0),se=lc("Odd",0),re=lc("Even",1),te=La({compile:function(a,b){b.$set("ngCloak",u);a.removeClass("ng-cloak")}}),ue=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Ic={},xg={blur:!0,focus:!0};n("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var b=va("ng-"+a);Ic[b]=["$parse","$rootScope",function(d,c){return{restrict:"A",compile:function(e,f){var g=
|
||||
d(f[b],null,!0);return function(b,d){d.on(a,function(d){var e=function(){g(b,{$event:d})};xg[a]&&c.$$phase?b.$evalAsync(e):b.$apply(e)})}}}}]});var xe=["$animate",function(a){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(b,d,c,e,f){var g,h,k;b.$watch(c.ngIf,function(b){b?h||f(function(b,e){h=e;b[b.length++]=X.createComment(" end ngIf: "+c.ngIf+" ");g={clone:b};a.enter(b,d.parent(),d)}):(k&&(k.remove(),k=null),h&&(h.$destroy(),h=null),g&&(k=
|
||||
rb(g.clone),a.leave(k).then(function(){k=null}),g=null))})}}}],ye=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:fa.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",h=e.autoscroll;return function(c,e,m,n,q){var s=0,v,u,p,C=function(){u&&(u.remove(),u=null);v&&(v.$destroy(),v=null);p&&(d.leave(p).then(function(){u=null}),u=p,p=null)};c.$watch(f,function(f){var m=function(){!y(h)||h&&!c.$eval(h)||
|
||||
b()},u=++s;f?(a(f,!0).then(function(a){if(u===s){var b=c.$new();n.template=a;a=q(b,function(a){C();d.enter(a,null,e).then(m)});v=b;p=a;v.$emit("$includeContentLoaded",f);c.$eval(g)}},function(){u===s&&(C(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(C(),n.template=null)})}}}}],Pe=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(b,d,c,e){/SVG/.test(d[0].toString())?(d.empty(),a(Lc(e.template,X).childNodes)(b,function(a){d.append(a)},
|
||||
{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],ze=La({priority:450,compile:function(){return{pre:function(a,b,d){a.$eval(d.ngInit)}}}}),Le=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=b.attr(d.$attr.ngList)||", ",f="false"!==d.ngTrim,g=f?U(e):e;c.$parsers.push(function(a){if(!q(a)){var b=[];a&&n(a.split(g),function(a){a&&b.push(f?U(a):a)});return b}});c.$formatters.push(function(a){return I(a)?a.join(e):u});c.$isEmpty=function(a){return!a||
|
||||
!a.length}}}},mb="ng-valid",Jd="ng-invalid",Wa="ng-pristine",Jb="ng-dirty",Ld="ng-pending",lb=G("ngModel"),yg=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,b,d,c,e,f,g,h,k,l){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=u;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;
|
||||
this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=u;this.$name=l(d.name||"",!1)(a);this.$$parentForm=Ib;var m=e(d.ngModel),r=m.assign,t=m,s=r,v=null,B,p=this;this.$$setOptions=function(a){if((p.$options=a)&&a.getterSetter){var b=e(d.ngModel+"()"),f=e(d.ngModel+"($$$p)");t=function(a){var c=m(a);z(c)&&(c=b(a));return c};s=function(a,b){z(m(a))?f(a,{$$$p:p.$modelValue}):r(a,p.$modelValue)}}else if(!m.assign)throw lb("nonassign",d.ngModel,ua(c));};this.$render=x;this.$isEmpty=
|
||||
function(a){return q(a)||""===a||null===a||a!==a};var C=0;Gd({ctrl:this,$element:c,set:function(a,b){a[b]=!0},unset:function(a,b){delete a[b]},$animate:f});this.$setPristine=function(){p.$dirty=!1;p.$pristine=!0;f.removeClass(c,Jb);f.addClass(c,Wa)};this.$setDirty=function(){p.$dirty=!0;p.$pristine=!1;f.removeClass(c,Wa);f.addClass(c,Jb);p.$$parentForm.$setDirty()};this.$setUntouched=function(){p.$touched=!1;p.$untouched=!0;f.setClass(c,"ng-untouched","ng-touched")};this.$setTouched=function(){p.$touched=
|
||||
!0;p.$untouched=!1;f.setClass(c,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){g.cancel(v);p.$viewValue=p.$$lastCommittedViewValue;p.$render()};this.$validate=function(){if(!Q(p.$modelValue)||!isNaN(p.$modelValue)){var a=p.$$rawModelValue,b=p.$valid,c=p.$modelValue,d=p.$options&&p.$options.allowInvalid;p.$$runValidators(a,p.$$lastCommittedViewValue,function(e){d||b===e||(p.$modelValue=e?a:u,p.$modelValue!==c&&p.$$writeModelToScope())})}};this.$$runValidators=function(a,b,c){function d(){var c=
|
||||
!0;n(p.$validators,function(d,e){var g=d(a,b);c=c&&g;f(e,g)});return c?!0:(n(p.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;n(p.$asyncValidators,function(e,g){var h=e(a,b);if(!h||!z(h.then))throw lb("$asyncValidators",h);f(g,u);c.push(h.then(function(){f(g,!0)},function(a){d=!1;f(g,!1)}))});c.length?k.all(c).then(function(){g(d)},x):g(!0)}function f(a,b){h===C&&p.$setValidity(a,b)}function g(a){h===C&&c(a)}C++;var h=C;(function(){var a=p.$$parserName||"parse";if(q(B))f(a,
|
||||
null);else return B||(n(p.$validators,function(a,b){f(b,null)}),n(p.$asyncValidators,function(a,b){f(b,null)})),f(a,B),B;return!0})()?d()?e():g(!1):g(!1)};this.$commitViewValue=function(){var a=p.$viewValue;g.cancel(v);if(p.$$lastCommittedViewValue!==a||""===a&&p.$$hasNativeValidators)p.$$lastCommittedViewValue=a,p.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var b=p.$$lastCommittedViewValue;if(B=q(b)?u:!0)for(var c=0;c<p.$parsers.length;c++)if(b=p.$parsers[c](b),
|
||||
q(b)){B=!1;break}Q(p.$modelValue)&&isNaN(p.$modelValue)&&(p.$modelValue=t(a));var d=p.$modelValue,e=p.$options&&p.$options.allowInvalid;p.$$rawModelValue=b;e&&(p.$modelValue=b,p.$modelValue!==d&&p.$$writeModelToScope());p.$$runValidators(b,p.$$lastCommittedViewValue,function(a){e||(p.$modelValue=a?b:u,p.$modelValue!==d&&p.$$writeModelToScope())})};this.$$writeModelToScope=function(){s(a,p.$modelValue);n(p.$viewChangeListeners,function(a){try{a()}catch(c){b(c)}})};this.$setViewValue=function(a,b){p.$viewValue=
|
||||
a;p.$options&&!p.$options.updateOnDefault||p.$$debounceViewValueCommit(b)};this.$$debounceViewValueCommit=function(b){var c=0,d=p.$options;d&&y(d.debounce)&&(d=d.debounce,Q(d)?c=d:Q(d[b])?c=d[b]:Q(d["default"])&&(c=d["default"]));g.cancel(v);c?v=g(function(){p.$commitViewValue()},c):h.$$phase?p.$commitViewValue():a.$apply(function(){p.$commitViewValue()})};a.$watch(function(){var b=t(a);if(b!==p.$modelValue&&(p.$modelValue===p.$modelValue||b===b)){p.$modelValue=p.$$rawModelValue=b;B=u;for(var c=p.$formatters,
|
||||
d=c.length,e=b;d--;)e=c[d](e);p.$viewValue!==e&&(p.$viewValue=p.$$lastCommittedViewValue=e,p.$render(),p.$$runValidators(b,e,x))}return b})}],Ke=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:yg,priority:1,compile:function(b){b.addClass(Wa).addClass("ng-untouched").addClass(mb);return{pre:function(a,b,e,f){var g=f[0];b=f[1]||g.$$parentForm;g.$$setOptions(f[2]&&f[2].$options);b.$addControl(g);e.$observe("name",function(a){g.$name!==a&&g.$$parentForm.$$renameControl(g,
|
||||
a)});a.$on("$destroy",function(){g.$$parentForm.$removeControl(g)})},post:function(b,c,e,f){var g=f[0];if(g.$options&&g.$options.updateOn)c.on(g.$options.updateOn,function(a){g.$$debounceViewValueCommit(a&&a.type)});c.on("blur",function(c){g.$touched||(a.$$phase?b.$evalAsync(g.$setTouched):b.$apply(g.$setTouched))})}}}}}],zg=/(\s+|^)default(\s+|$)/,Oe=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,b){var d=this;this.$options=bb(a.$eval(b.ngModelOptions));y(this.$options.updateOn)?
|
||||
(this.$options.updateOnDefault=!1,this.$options.updateOn=U(this.$options.updateOn.replace(zg,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},Ae=La({terminal:!0,priority:1E3}),Ag=G("ngOptions"),Bg=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,Ie=["$compile","$parse",function(a,
|
||||
b){function d(a,c,d){function e(a,b,c,d,f){this.selectValue=a;this.viewValue=b;this.label=c;this.group=d;this.disabled=f}function l(a){var b;if(!q&&za(a))b=a;else{b=[];for(var c in a)a.hasOwnProperty(c)&&"$"!==c.charAt(0)&&b.push(c)}return b}var m=a.match(Bg);if(!m)throw Ag("iexp",a,ua(c));var n=m[5]||m[7],q=m[6];a=/ as /.test(m[0])&&m[1];var s=m[9];c=b(m[2]?m[1]:n);var v=a&&b(a)||c,u=s&&b(s),p=s?function(a,b){return u(d,b)}:function(a){return Ca(a)},C=function(a,b){return p(a,z(a,b))},w=b(m[2]||
|
||||
m[1]),y=b(m[3]||""),B=b(m[4]||""),x=b(m[8]),D={},z=q?function(a,b){D[q]=b;D[n]=a;return D}:function(a){D[n]=a;return D};return{trackBy:s,getTrackByValue:C,getWatchables:b(x,function(a){var b=[];a=a||[];for(var c=l(a),e=c.length,f=0;f<e;f++){var g=a===c?f:c[f],k=z(a[g],g),g=p(a[g],k);b.push(g);if(m[2]||m[1])g=w(d,k),b.push(g);m[4]&&(k=B(d,k),b.push(k))}return b}),getOptions:function(){for(var a=[],b={},c=x(d)||[],f=l(c),g=f.length,m=0;m<g;m++){var n=c===f?m:f[m],r=z(c[n],n),q=v(d,r),n=p(q,r),t=w(d,
|
||||
r),u=y(d,r),r=B(d,r),q=new e(n,q,t,u,r);a.push(q);b[n]=q}return{items:a,selectValueMap:b,getOptionFromViewValue:function(a){return b[C(a)]},getViewValueFromOption:function(a){return s?fa.copy(a.viewValue):a.viewValue}}}}}var c=X.createElement("option"),e=X.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","?ngModel"],link:{pre:function(a,b,c,d){d[0].registerOption=x},post:function(b,g,h,k){function l(a,b){a.element=b;b.disabled=a.disabled;a.label!==b.label&&(b.label=a.label,
|
||||
b.textContent=a.label);a.value!==b.value&&(b.value=a.selectValue)}function m(a,b,c,d){b&&F(b.nodeName)===c?c=b:(c=d.cloneNode(!1),b?a.insertBefore(c,b):a.appendChild(c));return c}function r(a){for(var b;a;)b=a.nextSibling,Xb(a),a=b}function q(a){var b=p&&p[0],c=z&&z[0];if(b||c)for(;a&&(a===b||a===c||8===a.nodeType||""===a.value);)a=a.nextSibling;return a}function s(){var a=D&&u.readValue();D=E.getOptions();var b={},d=g[0].firstChild;x&&g.prepend(p);d=q(d);D.items.forEach(function(a){var f,h;a.group?
|
||||
(f=b[a.group],f||(f=m(g[0],d,"optgroup",e),d=f.nextSibling,f.label=a.group,f=b[a.group]={groupElement:f,currentOptionElement:f.firstChild}),h=m(f.groupElement,f.currentOptionElement,"option",c),l(a,h),f.currentOptionElement=h.nextSibling):(h=m(g[0],d,"option",c),l(a,h),d=h.nextSibling)});Object.keys(b).forEach(function(a){r(b[a].currentOptionElement)});r(d);v.$render();if(!v.$isEmpty(a)){var f=u.readValue();(E.trackBy?ma(a,f):a===f)||(v.$setViewValue(f),v.$render())}}var v=k[1];if(v){var u=k[0];k=
|
||||
h.multiple;for(var p,C=0,w=g.children(),y=w.length;C<y;C++)if(""===w[C].value){p=w.eq(C);break}var x=!!p,z=B(c.cloneNode(!1));z.val("?");var D,E=d(h.ngOptions,g,b);k?(v.$isEmpty=function(a){return!a||0===a.length},u.writeValue=function(a){D.items.forEach(function(a){a.element.selected=!1});a&&a.forEach(function(a){(a=D.getOptionFromViewValue(a))&&!a.disabled&&(a.element.selected=!0)})},u.readValue=function(){var a=g.val()||[],b=[];n(a,function(a){(a=D.selectValueMap[a])&&!a.disabled&&b.push(D.getViewValueFromOption(a))});
|
||||
return b},E.trackBy&&b.$watchCollection(function(){if(I(v.$viewValue))return v.$viewValue.map(function(a){return E.getTrackByValue(a)})},function(){v.$render()})):(u.writeValue=function(a){var b=D.getOptionFromViewValue(a);b&&!b.disabled?g[0].value!==b.selectValue&&(z.remove(),x||p.remove(),g[0].value=b.selectValue,b.element.selected=!0,b.element.setAttribute("selected","selected")):null===a||x?(z.remove(),x||g.prepend(p),g.val(""),p.prop("selected",!0),p.attr("selected",!0)):(x||p.remove(),g.prepend(z),
|
||||
g.val("?"),z.prop("selected",!0),z.attr("selected",!0))},u.readValue=function(){var a=D.selectValueMap[g.val()];return a&&!a.disabled?(x||p.remove(),z.remove(),D.getViewValueFromOption(a)):null},E.trackBy&&b.$watch(function(){return E.getTrackByValue(v.$viewValue)},function(){v.$render()}));x?(p.remove(),a(p)(b),p.removeClass("ng-scope")):p=B(c.cloneNode(!1));s();b.$watchCollection(E.getWatchables,s)}}}}}],Be=["$locale","$interpolate","$log",function(a,b,d){var c=/{}/g,e=/^when(Minus)?(.+)$/;return{link:function(f,
|
||||
g,h){function k(a){g.text(a||"")}var l=h.count,m=h.$attr.when&&g.attr(h.$attr.when),r=h.offset||0,s=f.$eval(m)||{},u={},v=b.startSymbol(),y=b.endSymbol(),p=v+l+"-"+r+y,C=fa.noop,w;n(h,function(a,b){var c=e.exec(b);c&&(c=(c[1]?"-":"")+F(c[2]),s[c]=g.attr(h.$attr[b]))});n(s,function(a,d){u[d]=b(a.replace(c,p))});f.$watch(l,function(b){var c=parseFloat(b),e=isNaN(c);e||c in s||(c=a.pluralCat(c-r));c===w||e&&Q(w)&&isNaN(w)||(C(),e=u[c],q(e)?(null!=b&&d.debug("ngPluralize: no rule defined for '"+c+"' in "+
|
||||
m),C=x,k()):C=f.$watch(e,k),w=c)})}}}],Ce=["$parse","$animate",function(a,b){var d=G("ngRepeat"),c=function(a,b,c,d,k,l,m){a[c]=d;k&&(a[k]=l);a.$index=b;a.$first=0===b;a.$last=b===m-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(b&1))};return{restrict:"A",multiElement:!0,transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,compile:function(e,f){var g=f.ngRepeat,h=X.createComment(" end ngRepeat: "+g+" "),k=g.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
|
||||
if(!k)throw d("iexp",g);var l=k[1],m=k[2],r=k[3],q=k[4],k=l.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!k)throw d("iidexp",l);var s=k[3]||k[1],v=k[2];if(r&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(r)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(r)))throw d("badident",r);var x,p,y,w,z={$id:Ca};q?x=a(q):(y=function(a,b){return Ca(b)},w=function(a){return a});return function(a,e,f,k,l){x&&(p=function(b,c,d){v&&(z[v]=b);z[s]=c;z.$index=
|
||||
d;return x(a,z)});var q=$();a.$watchCollection(m,function(f){var k,m,t=e[0],x,z=$(),D,E,H,F,I,G,J;r&&(a[r]=f);if(za(f))I=f,m=p||y;else for(J in m=p||w,I=[],f)qa.call(f,J)&&"$"!==J.charAt(0)&&I.push(J);D=I.length;J=Array(D);for(k=0;k<D;k++)if(E=f===I?k:I[k],H=f[E],F=m(E,H,k),q[F])G=q[F],delete q[F],z[F]=G,J[k]=G;else{if(z[F])throw n(J,function(a){a&&a.scope&&(q[a.id]=a)}),d("dupes",g,F,H);J[k]={id:F,scope:u,clone:u};z[F]=!0}for(x in q){G=q[x];F=rb(G.clone);b.leave(F);if(F[0].parentNode)for(k=0,m=F.length;k<
|
||||
m;k++)F[k].$$NG_REMOVED=!0;G.scope.$destroy()}for(k=0;k<D;k++)if(E=f===I?k:I[k],H=f[E],G=J[k],G.scope){x=t;do x=x.nextSibling;while(x&&x.$$NG_REMOVED);G.clone[0]!=x&&b.move(rb(G.clone),null,B(t));t=G.clone[G.clone.length-1];c(G.scope,k,s,H,v,E,D)}else l(function(a,d){G.scope=d;var e=h.cloneNode(!1);a[a.length++]=e;b.enter(a,null,B(t));t=e;G.clone=a;z[G.id]=G;c(G.scope,k,s,H,v,E,D)});q=z})}}}}],De=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngShow,function(b){a[b?
|
||||
"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],we=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngHide,function(b){a[b?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Ee=La(function(a,b,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&n(d,function(a,c){b.css(c,"")});a&&b.css(a)},!0)}),Fe=["$animate",function(a){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(b,
|
||||
d,c,e){var f=[],g=[],h=[],k=[],l=function(a,b){return function(){a.splice(b,1)}};b.$watch(c.ngSwitch||c.on,function(b){var c,d;c=0;for(d=h.length;c<d;++c)a.cancel(h[c]);c=h.length=0;for(d=k.length;c<d;++c){var q=rb(g[c].clone);k[c].$destroy();(h[c]=a.leave(q)).then(l(h,c))}g.length=0;k.length=0;(f=e.cases["!"+b]||e.cases["?"])&&n(f,function(b){b.transclude(function(c,d){k.push(d);var e=b.element;c[c.length++]=X.createComment(" end ngSwitchWhen: ");g.push({clone:c});a.enter(c,e.parent(),e)})})})}}}],
|
||||
Ge=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["!"+d.ngSwitchWhen]=c.cases["!"+d.ngSwitchWhen]||[];c.cases["!"+d.ngSwitchWhen].push({transclude:e,element:b})}}),He=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["?"]=c.cases["?"]||[];c.cases["?"].push({transclude:e,element:b})}}),Je=La({restrict:"EAC",link:function(a,b,d,c,e){if(!e)throw G("ngTransclude")("orphan",ua(b));e(function(a){b.empty();
|
||||
b.append(a)})}}),je=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(b,d){"text/ng-template"==d.type&&a.put(d.id,b[0].text)}}}],Cg={$setViewValue:x,$render:x},Dg=["$element","$scope","$attrs",function(a,b,d){var c=this,e=new Sa;c.ngModelCtrl=Cg;c.unknownOption=B(X.createElement("option"));c.renderUnknownOption=function(b){b="? "+Ca(b)+" ?";c.unknownOption.val(b);a.prepend(c.unknownOption);a.val(b)};b.$on("$destroy",function(){c.renderUnknownOption=x});c.removeUnknownOption=
|
||||
function(){c.unknownOption.parent()&&c.unknownOption.remove()};c.readValue=function(){c.removeUnknownOption();return a.val()};c.writeValue=function(b){c.hasOption(b)?(c.removeUnknownOption(),a.val(b),""===b&&c.emptyOption.prop("selected",!0)):null==b&&c.emptyOption?(c.removeUnknownOption(),a.val("")):c.renderUnknownOption(b)};c.addOption=function(a,b){Ra(a,'"option value"');""===a&&(c.emptyOption=b);var d=e.get(a)||0;e.put(a,d+1);c.ngModelCtrl.$render();b[0].hasAttribute("selected")&&(b[0].selected=
|
||||
!0)};c.removeOption=function(a){var b=e.get(a);b&&(1===b?(e.remove(a),""===a&&(c.emptyOption=u)):e.put(a,b-1))};c.hasOption=function(a){return!!e.get(a)};c.registerOption=function(a,b,d,e,l){if(e){var m;d.$observe("value",function(a){y(m)&&c.removeOption(m);m=a;c.addOption(a,b)})}else l?a.$watch(l,function(a,e){d.$set("value",a);e!==a&&c.removeOption(e);c.addOption(a,b)}):c.addOption(d.value,b);b.on("$destroy",function(){c.removeOption(d.value);c.ngModelCtrl.$render()})}}],ke=function(){return{restrict:"E",
|
||||
require:["select","?ngModel"],controller:Dg,priority:1,link:{pre:function(a,b,d,c){var e=c[1];if(e){var f=c[0];f.ngModelCtrl=e;e.$render=function(){f.writeValue(e.$viewValue)};b.on("change",function(){a.$apply(function(){e.$setViewValue(f.readValue())})});if(d.multiple){f.readValue=function(){var a=[];n(b.find("option"),function(b){b.selected&&a.push(b.value)});return a};f.writeValue=function(a){var c=new Sa(a);n(b.find("option"),function(a){a.selected=y(c.get(a.value))})};var g,h=NaN;a.$watch(function(){h!==
|
||||
e.$viewValue||ma(g,e.$viewValue)||(g=ia(e.$viewValue),e.$render());h=e.$viewValue});e.$isEmpty=function(a){return!a||0===a.length}}}}}}},me=["$interpolate",function(a){return{restrict:"E",priority:100,compile:function(b,d){if(y(d.value))var c=a(d.value,!0);else{var e=a(b.text(),!0);e||d.$set("value",b.text())}return function(a,b,d){var k=b.parent();(k=k.data("$selectController")||k.parent().data("$selectController"))&&k.registerOption(a,b,d,c,e)}}}}],le=na({restrict:"E",terminal:!1}),Fc=function(){return{restrict:"A",
|
||||
require:"?ngModel",link:function(a,b,d,c){c&&(d.required=!0,c.$validators.required=function(a,b){return!d.required||!c.$isEmpty(b)},d.$observe("required",function(){c.$validate()}))}}},Ec=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e,f=d.ngPattern||d.pattern;d.$observe("pattern",function(a){E(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw G("ngPattern")("noregexp",f,a,ua(b));e=a||u;c.$validate()});c.$validators.pattern=function(a,b){return c.$isEmpty(b)||
|
||||
q(e)||e.test(b)}}}}},Hc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=-1;d.$observe("maxlength",function(a){a=ea(a);e=isNaN(a)?-1:a;c.$validate()});c.$validators.maxlength=function(a,b){return 0>e||c.$isEmpty(b)||b.length<=e}}}}},Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=0;d.$observe("minlength",function(a){e=ea(a)||0;c.$validate()});c.$validators.minlength=function(a,b){return c.$isEmpty(b)||b.length>=e}}}}};S.angular.bootstrap?
|
||||
console.log("WARNING: Tried to load angular more than once."):(ce(),ee(fa),fa.module("ngLocale",[],["$provide",function(a){function b(a){a+="";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),
|
||||
SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",
|
||||
negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",pluralCat:function(a,c){var e=a|0,f=c;u===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),B(X).ready(function(){Zd(X,yc)}))})(window,document);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
|
||||
//# sourceMappingURL=angular.min.js.map
|
1
admin/static/js/angular.min.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../frontend/static/js/angular.min.js
|
|
@ -17,34 +17,89 @@ angular.module("FICApp", ["ngRoute", "ngResource"])
|
|||
controller: "TeamsListController",
|
||||
templateUrl: "views/team-list.html"
|
||||
})
|
||||
.when("/teams/:teamId", {
|
||||
controller: "TeamController",
|
||||
templateUrl: "views/team.html"
|
||||
})
|
||||
.when("/teams/new", {
|
||||
controller: "TeamNewController",
|
||||
templateUrl: "views/team-new.html"
|
||||
})
|
||||
.when("/", {
|
||||
templateUrl: "views/home.html"
|
||||
});
|
||||
$locationProvider.html5Mode(true);
|
||||
});
|
||||
|
||||
angular.module("FICApp")
|
||||
.factory("Version", function($resource) {
|
||||
return $resource("/api/version")
|
||||
})
|
||||
.factory("Team", function($resource) {
|
||||
return $resource("/api/teams/:teamId", { teamId: '@id' }, {
|
||||
'save': {method: 'PATCH'},
|
||||
})
|
||||
});
|
||||
angular.module("FICApp")
|
||||
})
|
||||
.factory("TeamMember", function($resource) {
|
||||
return $resource("/api/teams/:teamId/members", { teamId: '@id' })
|
||||
})
|
||||
.factory("TeamMy", function($resource) {
|
||||
return $resource("/api/teams/:teamId/my.json", { teamId: '@id' })
|
||||
})
|
||||
.factory("Teams", function($resource) {
|
||||
return $resource("/api/teams/teams.json")
|
||||
})
|
||||
.factory("TeamStats", function($resource) {
|
||||
return $resource("/api/teams/:teamId/stats.json", { teamId: '@id' })
|
||||
})
|
||||
.factory("TeamPresence", function($resource) {
|
||||
return $resource("/api/teams/:teamId/tries", { teamId: '@id' })
|
||||
})
|
||||
.factory("Theme", function($resource) {
|
||||
return $resource("/api/themes/:themeId", null, {
|
||||
'save': {method: 'PATCH'},
|
||||
})
|
||||
});
|
||||
angular.module("FICApp")
|
||||
.factory("Exercice", function($resource) {
|
||||
return $resource("/api/themes/:themeId/:exerciceId", null, {
|
||||
'query': {method: "GET", url: "/api/themes/:themeId/exercices", isArray: true},
|
||||
'save': {method: 'PATCH'},
|
||||
})
|
||||
.factory("Themes", function($resource) {
|
||||
return $resource("/api/themes/themes.json", null, {
|
||||
'get': {method: 'GET'},
|
||||
})
|
||||
})
|
||||
.factory("Exercice", function($resource) {
|
||||
return $resource("/api/exercices/:exerciceId")
|
||||
});
|
||||
|
||||
String.prototype.capitalize = function() {
|
||||
return this
|
||||
.toLowerCase()
|
||||
.replace(
|
||||
/(^|\s)([a-z])/g,
|
||||
function(m,p1,p2) { return p1+p2.toUpperCase(); }
|
||||
);
|
||||
}
|
||||
|
||||
angular.module("FICApp")
|
||||
.filter("capitalize", function() {
|
||||
return function(input) {
|
||||
return input.capitalize();
|
||||
}
|
||||
})
|
||||
.filter("time", function() {
|
||||
return function(input) {
|
||||
if (input == undefined) {
|
||||
return "--";
|
||||
} else if (input >= 10) {
|
||||
return input;
|
||||
} else {
|
||||
return "0" + input;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.controller("VersionController", function($scope, Version) {
|
||||
$scope.v = Version.get();
|
||||
})
|
||||
|
||||
.controller("ThemesListController", function($scope, Theme, $location) {
|
||||
$scope.themes = Theme.query();
|
||||
$scope.fields = ["id", "name"];
|
||||
|
@ -81,14 +136,278 @@ angular.module("FICApp")
|
|||
|
||||
.controller("TeamsListController", function($scope, Team, $location) {
|
||||
$scope.teams = Team.query();
|
||||
$scope.fields = ["id", "name"];
|
||||
$scope.fields = ["id", "name", "initialName"];
|
||||
|
||||
$scope.show = function(id) {
|
||||
$location.url("/teams/" + id);
|
||||
};
|
||||
})
|
||||
.controller("TeamController", function($scope, Team, TeamMember, $routeParams) {
|
||||
$scope.team = Team.get({ teamId: $routeParams.teamId });
|
||||
$scope.members = TeamMember.query({ teamId: $routeParams.teamId });
|
||||
})
|
||||
.controller("TeamStatsController", function($scope, TeamStats, $routeParams) {
|
||||
$scope.teamstats = TeamStats.get({ teamId: $routeParams.teamId });
|
||||
$scope.teamstats.$promise.then(function(res) {
|
||||
solvedByLevelPie("#pieLevels", res.levels);
|
||||
solvedByThemesPie("#pieThemes", res.themes);
|
||||
});
|
||||
})
|
||||
.controller("TeamExercicesController", function($scope, Teams, Themes, TeamMy, Exercice, $routeParams) {
|
||||
$scope.teams = Teams.get();
|
||||
$scope.themes = Themes.get();
|
||||
$scope.exercices = Exercice.query();
|
||||
$scope.my = TeamMy.get({ teamId: $routeParams.teamId });
|
||||
|
||||
$scope.teams.$promise.then(function(res){
|
||||
$scope.nb_teams = 0;
|
||||
$scope.nb_reg_teams = Object.keys(res).length;
|
||||
angular.forEach(res, function(team, tid) {
|
||||
if (team.rank)
|
||||
$scope.nb_teams += 1;
|
||||
}, 0);
|
||||
});
|
||||
|
||||
$scope.my.$promise.then(function(res){
|
||||
$scope.solved_exercices = 0;
|
||||
angular.forEach(res.exercices, function(exercice, eid) {
|
||||
if (exercice.solved) {
|
||||
$scope.solved_exercices += 1;
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
|
||||
})
|
||||
.controller("TeamNewController", function($scope, Team, $location) {
|
||||
$scope.contact = new Team({
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
.controller("PresenceController", function($scope, TeamPresence, $routeParams) {
|
||||
$scope.presence = TeamPresence.query({ teamId: $routeParams.teamId });
|
||||
$scope.presence.$promise.then(function(res) {
|
||||
presenceCal("#presenceCal", res);
|
||||
});
|
||||
})
|
||||
.controller("CountdownController", function($scope, $http, $timeout) {
|
||||
$scope.time = {};
|
||||
function updTime() {
|
||||
$timeout.cancel($scope.cbm);
|
||||
$scope.cbm = $timeout(updTime, 1000);
|
||||
if (sessionStorage.userService) {
|
||||
var time = angular.fromJson(sessionStorage.userService);
|
||||
var srv_cur = (Date.now() + (time.cu * 1000 - time.he)) / 1000;
|
||||
var remain = time.du;
|
||||
if (time.st == Math.floor(srv_cur)) {
|
||||
$scope.refresh(true);
|
||||
}
|
||||
if (time.st > 0 && time.st <= srv_cur) {
|
||||
remain = time.st + time.du - srv_cur;
|
||||
}
|
||||
if (remain < 0) {
|
||||
remain = 0;
|
||||
$scope.time.end = true;
|
||||
$scope.time.expired = true;
|
||||
} else if (remain < 60) {
|
||||
$scope.time.end = false;
|
||||
$scope.time.expired = true;
|
||||
} else {
|
||||
$scope.time.end = false;
|
||||
$scope.time.expired = false;
|
||||
}
|
||||
$scope.time.start = time.st * 1000;
|
||||
$scope.time.duration = time.du;
|
||||
$scope.time.remaining = remain;
|
||||
$scope.time.hours = Math.floor(remain / 3600);
|
||||
$scope.time.minutes = Math.floor((remain % 3600) / 60);
|
||||
$scope.time.seconds = Math.floor(remain % 60);
|
||||
}
|
||||
}
|
||||
|
||||
$http.get("/time.json").success(function(time) {
|
||||
time.he = (new Date()).getTime();
|
||||
sessionStorage.userService = angular.toJson(time);
|
||||
updTime();
|
||||
});
|
||||
});
|
||||
|
||||
function solvedByLevelPie(location, data) {
|
||||
var width = d3.select(location).node().getBoundingClientRect().width - 10,
|
||||
height = d3.select(location).node().getBoundingClientRect().width - 10,
|
||||
radius = Math.min(width, height) / 2,
|
||||
innerRadius = 0.1 * radius;
|
||||
|
||||
var color = d3.scale.ordinal()
|
||||
.range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]);
|
||||
|
||||
var pie = d3.layout.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.width; });
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(function (d) {
|
||||
return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius;
|
||||
});
|
||||
|
||||
var outlineArc = d3.svg.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(radius);
|
||||
|
||||
var svg = d3.select(location).append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
data.forEach(function(d) {
|
||||
d.score = d.solved * 100 / d.total;
|
||||
d.width = d.tries + 1;
|
||||
});
|
||||
|
||||
var path = svg.selectAll(".solidArc")
|
||||
.data(pie(data))
|
||||
.enter().append("path")
|
||||
.attr("fill", function(d) { return color(d.data.tip); })
|
||||
.attr("class", "solidArc")
|
||||
.attr("stroke", "gray")
|
||||
.attr("d", arc);
|
||||
|
||||
var outerPath = svg.selectAll(".outlineArc")
|
||||
.data(pie(data))
|
||||
.enter().append("path")
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "gray")
|
||||
.attr("class", "outlineArc")
|
||||
.attr("d", outlineArc);
|
||||
|
||||
var labelArc = d3.svg.arc()
|
||||
.outerRadius(0.8 * radius)
|
||||
.innerRadius(0.8 * radius);
|
||||
|
||||
svg.selectAll(".labelArc")
|
||||
.data(pie(data))
|
||||
.enter().append("text")
|
||||
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(function(d) { return d.data.tip + ": " + d.data.solved + "/" + d.data.total; });
|
||||
|
||||
svg.selectAll(".label2Arc")
|
||||
.data(pie(data))
|
||||
.enter().append("text")
|
||||
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(function(d) { return d.data.tries; });
|
||||
}
|
||||
|
||||
function solvedByThemesPie(location, data) {
|
||||
var width = d3.select(location).node().getBoundingClientRect().width,
|
||||
height = d3.select(location).node().getBoundingClientRect().width,
|
||||
radius = Math.min(width, height) / 2,
|
||||
innerRadius = 0.1 * radius;
|
||||
|
||||
var color = d3.scale.ordinal()
|
||||
.range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]);
|
||||
|
||||
var pie = d3.layout.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.width; });
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(function (d) {
|
||||
return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius;
|
||||
});
|
||||
|
||||
var outlineArc = d3.svg.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(radius);
|
||||
|
||||
var svg = d3.select(location).append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
data.forEach(function(d) {
|
||||
d.score = d.solved * 100 / d.total;
|
||||
d.width = d.tries + 0.5;
|
||||
});
|
||||
|
||||
var path = svg.selectAll(".solidArc")
|
||||
.data(pie(data))
|
||||
.enter().append("path")
|
||||
.attr("fill", function(d) { return color(d.data.tip); })
|
||||
.attr("class", "solidArc")
|
||||
.attr("stroke", "gray")
|
||||
.attr("d", arc);
|
||||
|
||||
var outerPath = svg.selectAll(".outlineArc")
|
||||
.data(pie(data))
|
||||
.enter().append("path")
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "gray")
|
||||
.attr("class", "outlineArc")
|
||||
.attr("d", outlineArc);
|
||||
|
||||
svg.selectAll(".label2Arc")
|
||||
.data(pie(data))
|
||||
.enter().append("text")
|
||||
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(function(d) { return d.data.solved; });
|
||||
|
||||
var labelArc = d3.svg.arc()
|
||||
.outerRadius(0.8 * radius)
|
||||
.innerRadius(0.8 * radius);
|
||||
|
||||
svg.selectAll(".labelArc")
|
||||
.data(pie(data))
|
||||
.enter().append("text")
|
||||
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(function(d) { return d.data.tip + ": " + d.data.tries; });
|
||||
}
|
||||
|
||||
function presenceCal(location, data) {
|
||||
var width = d3.select(location).node().getBoundingClientRect().width,
|
||||
height = 80,
|
||||
cellSize = 17; // cell size
|
||||
|
||||
var percent = d3.format(".1%"),
|
||||
format = d3.time.format("%H:%M");
|
||||
|
||||
var color = d3.scale.quantize()
|
||||
.domain([0, 16])
|
||||
.range(d3.range(8).map(function(d) { return "q" + d + "-8"; }));
|
||||
|
||||
var svg = d3.select(location).selectAll("svg")
|
||||
.data(d3.range(26, 29))
|
||||
.enter().append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.attr("class", "RdYlGn")
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + ((width - cellSize * 24) / 2) + "," + (height - cellSize * 4 - 1) + ")");
|
||||
|
||||
svg.append("text")
|
||||
.attr("transform", "translate(-6," + cellSize * 2.6 + ")rotate(-90)")
|
||||
.style("text-anchor", "middle")
|
||||
.text(function(d) { return d + "-02"; });
|
||||
|
||||
var rect = svg.selectAll(".quarter")
|
||||
.data(function(d) { return d3.time.minutes(new Date(2016, 1, d, 0), new Date(2016, 1, d, 24), 15); })
|
||||
.enter().append("rect")
|
||||
.attr("width", cellSize)
|
||||
.attr("height", cellSize)
|
||||
.attr("class", function(d) { return color(data.reduce(function(prev, cur){
|
||||
cur = new Date(cur).getTime();
|
||||
dv = d.getTime();
|
||||
return prev + ((dv <= cur && cur < dv+15*60000)?1:0);
|
||||
}, 0)); });
|
||||
}
|
||||
|
|
7
admin/static/js/bootstrap.min.js
vendored
1
admin/static/js/bootstrap.min.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../frontend/static/js/bootstrap.min.js
|
1
admin/static/js/d3.v3.min.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../frontend/static/js/d3.v3.min.js
|
1
admin/static/js/i18n
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../frontend/static/js/i18n/
|
5
admin/static/js/jquery.min.js
vendored
1
admin/static/js/jquery.min.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../frontend/static/js/jquery.min.js
|
9
admin/static/views/home.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<div class="well well-lg">
|
||||
<h3>Interface d'administration du challenge</h3>
|
||||
<p>
|
||||
Sélectionnez une action dans le menu ci-dessus.
|
||||
</p>
|
||||
<p ng-controller="VersionController">
|
||||
Version de l'API : {{ v.version }}
|
||||
</p>
|
||||
</div>
|
54
admin/static/views/team.html
Normal file
|
@ -0,0 +1,54 @@
|
|||
<style>
|
||||
.RdYlGn .q0-8{fill:rgb(245,250,250)}
|
||||
.RdYlGn .q1-8{fill:rgb(190,200,200)}
|
||||
.RdYlGn .q2-8{fill:rgb(170,180,180)}
|
||||
.RdYlGn .q3-8{fill:rgb(150,160,160)}
|
||||
.RdYlGn .q4-8{fill:rgb(130,140,140)}
|
||||
.RdYlGn .q5-8{fill:rgb(110,120,120)}
|
||||
.RdYlGn .q6-8{fill:rgb(90,100,100)}
|
||||
.RdYlGn .q7-8{fill:rgb(70,80,80)}
|
||||
</style>
|
||||
|
||||
<h1>{{ team.name }}<span ng-show="team.name != team.initialName"> ({{ team.initialName}})</span> <small><span ng-repeat="member in members"><span ng-show="$last && !$first"> et </span><span ng-show="$middle">, </span>{{ member.firstname | capitalize }} <em ng-show="member.nickname">{{ member.nickname }}</em> {{ member.lastname | capitalize }}</span></small></h1>
|
||||
|
||||
<div ng-controller="TeamExercicesController">
|
||||
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Points</dt>
|
||||
<dd>{{ my.score }}</dd>
|
||||
<dt>Classement</dt>
|
||||
<dd>{{ teams[my.team_id].rank }}/{{ nb_teams }} ({{ nb_reg_teams }} registered teams)</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Présence</h2>
|
||||
|
||||
<div id="presenceCal" ng-controller="PresenceController">
|
||||
</div>
|
||||
|
||||
<h2>Exercices résolus : {{ solved_exercices }}/{{ exercices.length }} {{ solved_exercices * 100 / exercices.length | number:0 }}%</h2>
|
||||
|
||||
<dl>
|
||||
<div style="float: left;padding: 0 5px; margin: 5px; border: 1px solid #ccc; border-radius: 3px; min-width: 5vw" ng-repeat="(tid,theme) in themes" class="text-center">
|
||||
<dt>{{ theme.name }}</dt>
|
||||
<dd>
|
||||
<ul class="list-unstyled">
|
||||
<li ng-repeat="(eid,exercice) in theme.exercices" ng-show="my.exercices[eid] && my.exercices[eid].solved"><a href="https://fic.srs.epita.fr/{{ my.exercices[eid].theme_id }}/{{ eid }}" target="_blank"><abbr title="{{ my.exercices[eid].statement }}">{{ exercice.title }}</abbr></a> (<abbr title="{{ my.exercices[eid].solved_time | date:'mediumDate' }} à {{ my.exercices[eid].solved_time | date:'mediumTime' }}">{{ my.exercices[eid].solved_number }}<sup>e</sup></abbr>)</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="container" ng-controller="TeamStatsController">
|
||||
<div class="row">
|
||||
<div class="col-sm-6" id="pieLevels">
|
||||
<h4 class="text-center">Tentatives par niveaux</h4>
|
||||
</div>
|
||||
<div class="col-sm-6" id="pieThemes">
|
||||
<h4 class="text-center">Tentatives par thèmes</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
36
backend/hint.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
type askOpenHint struct {
|
||||
HintId int64 `json:"id"`
|
||||
}
|
||||
|
||||
func treatOpeningHint(pathname string, team fic.Team) {
|
||||
var ask askOpenHint
|
||||
|
||||
if cnt_raw, err := ioutil.ReadFile(pathname); err != nil {
|
||||
log.Println("[ERR]", err)
|
||||
} else if err := json.Unmarshal(cnt_raw, &ask); err != nil {
|
||||
log.Println("[ERR]", err)
|
||||
} else if ask.HintId == 0 {
|
||||
log.Println("[WRN] Invalid content in hint file: ", pathname)
|
||||
os.Remove(pathname)
|
||||
} else if hint, err := fic.GetHint(ask.HintId); err != nil {
|
||||
log.Println("[ERR]", err)
|
||||
} else if err := team.OpenHint(hint); err != nil {
|
||||
log.Println("[ERR]", err)
|
||||
} else {
|
||||
genTeamMyFile(team)
|
||||
if err := os.Remove(pathname); err != nil {
|
||||
log.Println("[ERR]", err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,16 +11,16 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/inotify"
|
||||
"gopkg.in/fsnotify.v1"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var TeamsDir string
|
||||
var SubmissionDir string
|
||||
|
||||
func watchsubdir(watcher *inotify.Watcher, pathname string) error {
|
||||
func watchsubdir(watcher *fsnotify.Watcher, pathname string) error {
|
||||
log.Println("Watch new directory:", pathname)
|
||||
if err := watcher.AddWatch(pathname, inotify.IN_CLOSE_WRITE|inotify.IN_CREATE); err != nil {
|
||||
if err := watcher.Add(pathname); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -45,9 +45,14 @@ func main() {
|
|||
var dsn = flag.String("dsn", "fic:fic@/fic", "DSN to connect to the MySQL server")
|
||||
flag.StringVar(&SubmissionDir, "submission", "./submissions", "Base directory where save submissions")
|
||||
flag.StringVar(&TeamsDir, "teams", "../TEAMS", "Base directory where save teams JSON files")
|
||||
flag.StringVar(&fic.FilesDir, "files", "/files", "Request path prefix to reach files")
|
||||
var skipFullGeneration = flag.Bool("skipFullGeneration", false, "Skip initial full generation (safe to skip after start)")
|
||||
flag.BoolVar(&fic.PartialValidation, "partialValidation", false, "Validates flags which are corrects, don't be binary")
|
||||
flag.BoolVar(&fic.UnlockedChallenges, "unlockedChallenges", false, "Make all challenges accessible without having to validate previous level")
|
||||
flag.Parse()
|
||||
|
||||
log.SetPrefix("[backend] ")
|
||||
|
||||
SubmissionDir = path.Clean(SubmissionDir)
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
@ -66,10 +71,11 @@ func main() {
|
|||
defer fic.DBClose()
|
||||
|
||||
log.Println("Registering directory events...")
|
||||
watcher, err := inotify.NewWatcher()
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
if err := watchsubdir(watcher, SubmissionDir); err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -82,18 +88,18 @@ func main() {
|
|||
|
||||
for {
|
||||
select {
|
||||
case ev := <-watcher.Event:
|
||||
if ev.Mask&inotify.IN_CREATE == inotify.IN_CREATE {
|
||||
case ev := <-watcher.Events:
|
||||
if ev.Op & fsnotify.Create == fsnotify.Create {
|
||||
// Register new subdirectory
|
||||
if d, err := os.Stat(ev.Name); err == nil && d.IsDir() {
|
||||
if err := watchsubdir(watcher, ev.Name); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
} else if ev.Mask&inotify.IN_CLOSE_WRITE == inotify.IN_CLOSE_WRITE {
|
||||
} else if ev.Op & fsnotify.Write == fsnotify.Write {
|
||||
go treat(ev.Name)
|
||||
}
|
||||
case err := <-watcher.Error:
|
||||
case err := <-watcher.Errors:
|
||||
log.Println("error:", err)
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +116,8 @@ func treat(raw_path string) {
|
|||
log.Println("[ERR]", err)
|
||||
} else if spath[2] == "name" {
|
||||
treatRename(raw_path, team)
|
||||
} else if spath[2] == "hint" {
|
||||
treatOpeningHint(raw_path, team)
|
||||
} else {
|
||||
treatSubmission(raw_path, team, spath[2])
|
||||
}
|
||||
|
|
39
frontend/chname.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ChNameHandler struct {}
|
||||
|
||||
func (n ChNameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Handling %s name change request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Check request type and size
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else if r.ContentLength < 0 || r.ContentLength > 1023 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête trop longue ou de taille inconnue\"}", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract URL arguments
|
||||
var sURL = strings.Split(r.URL.Path, "/")
|
||||
|
||||
if len(sURL) != 1 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
team := sURL[0]
|
||||
|
||||
// Enqueue file for backend treatment
|
||||
if saveTeamFile(SubmissionDir, team, "name", w, r) {
|
||||
http.Error(w, "{\"errmsg\":\"Demande de changement de nom acceptée\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
39
frontend/hint.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HintHandler struct {}
|
||||
|
||||
func (h HintHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Handling %s opening hint request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Check request type and size
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else if r.ContentLength <= 0 || r.ContentLength > 1023 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête trop longue ou de taille inconnue\"}", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract URL arguments
|
||||
var sURL = strings.Split(r.URL.Path, "/")
|
||||
|
||||
if len(sURL) != 1 && len(sURL) != 2 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
team := sURL[0]
|
||||
|
||||
// Enqueue file for backend treatment
|
||||
if saveTeamFile(SubmissionDir, team, "hint", w, r) {
|
||||
http.Error(w, "{\"errmsg\":\"Demande d'astuce acceptée...\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
|
@ -22,21 +22,24 @@ func touchStartedFile(startSub time.Duration) {
|
|||
log.Println("Started! Go, Go, Go!!")
|
||||
fd.Close()
|
||||
} else {
|
||||
log.Println("Unable to start challenge:", err)
|
||||
log.Fatal("Unable to start challenge:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var bind = flag.String("bind", "0.0.0.0:8080", "Bind port/socket")
|
||||
var bind = flag.String("bind", "127.0.0.1:8080", "Bind port/socket")
|
||||
var prefix = flag.String("prefix", "", "Request path prefix to strip (from proxy)")
|
||||
var start = flag.Int64("start", 0, fmt.Sprintf("Challenge start timestamp (in 2 minutes: %d)", time.Now().Unix()/60*60+120))
|
||||
var duration = flag.Duration("duration", 180*time.Minute, "Challenge duration")
|
||||
var denyChName = flag.Bool("denyChName", false, "Deny team to change their name")
|
||||
var allowRegistration = flag.Bool("allowRegistration", false, "New team can add itself")
|
||||
flag.StringVar(&TeamsDir, "teams", "../TEAMS", "Base directory where save teams JSON files")
|
||||
var resolutionRoute = flag.Bool("resolutionRoute", false, "Enable resolution route")
|
||||
flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files")
|
||||
flag.StringVar(&SubmissionDir, "submission", "./submissions/", "Base directory where save submissions")
|
||||
flag.Parse()
|
||||
|
||||
log.SetPrefix("[frontend] ")
|
||||
|
||||
log.Println("Creating submission directory...")
|
||||
if _, err := os.Stat(SubmissionDir); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(SubmissionDir, 0777); err != nil {
|
||||
|
@ -67,7 +70,17 @@ func main() {
|
|||
|
||||
log.Println("Registering handlers...")
|
||||
http.Handle(fmt.Sprintf("%s/time.json", *prefix), http.StripPrefix(*prefix, TimeHandler{startTime, *duration}))
|
||||
http.Handle(fmt.Sprintf("%s/", *prefix), http.StripPrefix(*prefix, SubmissionHandler{end, *denyChName, *allowRegistration}))
|
||||
if *resolutionRoute {
|
||||
http.Handle(fmt.Sprintf("%s/resolution/", *prefix), http.StripPrefix(fmt.Sprintf("%s/resolution/", *prefix), ResolutionHandler{}))
|
||||
}
|
||||
if *allowRegistration {
|
||||
http.Handle(fmt.Sprintf("%s/registration", *prefix), http.StripPrefix(fmt.Sprintf("%s/registration", *prefix), RegistrationHandler{}))
|
||||
}
|
||||
if !*denyChName {
|
||||
http.Handle(fmt.Sprintf("%s/chname/", *prefix), http.StripPrefix(fmt.Sprintf("%s/chname/", *prefix), ChNameHandler{}))
|
||||
}
|
||||
http.Handle(fmt.Sprintf("%s/openhint/", *prefix), http.StripPrefix(fmt.Sprintf("%s/openhint/", *prefix), HintHandler{}))
|
||||
http.Handle(fmt.Sprintf("%s/submission/", *prefix), http.StripPrefix(fmt.Sprintf("%s/submission/", *prefix), SubmissionHandler{end}))
|
||||
|
||||
log.Println(fmt.Sprintf("Ready, listening on %s", *bind))
|
||||
if err := http.ListenAndServe(*bind, nil); err != nil {
|
||||
|
|
34
frontend/nginx-fic-static.conf
Normal file
|
@ -0,0 +1,34 @@
|
|||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
|
||||
server_name fic.srs.epita.fr;
|
||||
|
||||
access_log /var/log/nginx/fic2016.access_log main;
|
||||
error_log /var/log/nginx/fic2016.error_log info;
|
||||
|
||||
root /srv/www/fic2016-static/;
|
||||
|
||||
error_page 403 404 /e404.html;
|
||||
error_page 413 404 /e413.html;
|
||||
error_page 500 502 504 /e500.html;
|
||||
|
||||
location /.htaccess {
|
||||
return 404;
|
||||
}
|
||||
location /chbase.sh {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location ~ ^/[0-9] {
|
||||
rewrite ^/.*$ /index.html;
|
||||
}
|
||||
|
||||
location /edit {
|
||||
rewrite ^/.*$ /index.html;
|
||||
}
|
||||
|
||||
location /rank {
|
||||
rewrite ^/.*$ /index.html;
|
||||
}
|
||||
}
|
33
frontend/register.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
type RegistrationHandler struct {}
|
||||
|
||||
func (e RegistrationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Handling %s registration request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Check request type and size
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else if r.ContentLength < 0 || r.ContentLength > 1023 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête trop longue ou de taille inconnue\"}", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
// Enqueue file for backend treatment
|
||||
if err := saveFile(path.Join(SubmissionDir, "_registration"), "", r); err != nil {
|
||||
log.Println("Unable to open registration file:", err)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "{\"errmsg\":\"Demande d'enregistrement acceptée\"}", http.StatusAccepted)
|
||||
}
|
34
frontend/resolution.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type ResolutionHandler struct {}
|
||||
|
||||
const resolutiontpl = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge Forensic - Résolution</title>
|
||||
</head>
|
||||
<body style="margin: 0">
|
||||
<video src="{{.}}" controls width="100%" height="100%"></video>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
func (s ResolutionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Handling %s request from %s: /resolution%s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
|
||||
if resolutionTmpl, err := template.New("resolution").Parse(resolutiontpl); err != nil {
|
||||
log.Println("Cannot create template: ", err)
|
||||
} else if err := resolutionTmpl.Execute(w, path.Join("/vids/", r.URL.Path)); err != nil {
|
||||
log.Println("An error occurs during template execution: ", err)
|
||||
}
|
||||
}
|
81
frontend/save.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
func saveTeamFile(dirname string, team string, filename string, w http.ResponseWriter, r *http.Request) bool {
|
||||
if _, err := os.Stat(path.Join(dirname, team)); os.IsNotExist(err) || len(team) < 1 || team[0] == '_' {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return false
|
||||
} else {
|
||||
if len(filename) <= 0 {
|
||||
log.Println("EMPTY $EXERCICE RECEIVED:", filename)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
return false
|
||||
}
|
||||
|
||||
// Previous submission not treated
|
||||
if _, err := os.Stat(path.Join(SubmissionDir, team, filename)); !os.IsNotExist(err) {
|
||||
http.Error(w, "{\"errmsg\":\"Du calme ! une requête est déjà en cours de traitement.\"}", http.StatusPaymentRequired)
|
||||
return false
|
||||
}
|
||||
|
||||
if err := saveFile(path.Join(SubmissionDir, team), filename, r); err != nil {
|
||||
log.Println("Unable to handle submission file:", err)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func saveFile(dirname string, filename string, r *http.Request) error {
|
||||
if _, err := os.Stat(dirname); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(dirname, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var f *os.File
|
||||
|
||||
if filename == "" {
|
||||
if fd, err := ioutil.TempFile(dirname, ""); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer f.Close()
|
||||
f = fd
|
||||
}
|
||||
} else {
|
||||
if fd, err := os.Create(path.Join(dirname, filename)); err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer f.Close()
|
||||
f = fd
|
||||
}
|
||||
}
|
||||
|
||||
// Read request body
|
||||
var body []byte
|
||||
if r.ContentLength > 0 {
|
||||
tmp := make([]byte, 1024)
|
||||
for {
|
||||
n, err := r.Body.Read(tmp)
|
||||
for j := 0; j < n; j++ {
|
||||
body = append(body, tmp[j])
|
||||
}
|
||||
if err != nil || n <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f.Write(body)
|
||||
|
||||
return nil
|
||||
}
|
13
frontend/static/css/bootstrap.min.css
vendored
|
@ -69,11 +69,6 @@ h1 small.authors {
|
|||
}
|
||||
|
||||
.teamname {
|
||||
padding: 2px 7px;
|
||||
border-radius: 2px;
|
||||
box-shadow: #444 0 0 3px;
|
||||
}
|
||||
.teamname span {
|
||||
-webkit-filter: invert(100%);
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
|
11
frontend/static/css/slate.min.css
vendored
|
@ -2,11 +2,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge FIC2016</title>
|
||||
<title>Challenge Forensic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link href="/css/bootstrap.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/slate.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/fic.css" type="text/css" rel="stylesheet" media="screen">
|
||||
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge FIC2016</title>
|
||||
<title>Challenge Forensic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link href="/css/bootstrap.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/slate.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/fic.css" type="text/css" rel="stylesheet" media="screen">
|
||||
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge FIC2016</title>
|
||||
<title>Challenge Forensic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link href="/css/bootstrap.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/slate.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/fic.css" type="text/css" rel="stylesheet" media="screen">
|
||||
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
|
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 17 KiB |
|
@ -2,11 +2,10 @@
|
|||
<html ng-app="FICApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge FIC2016</title>
|
||||
<title>Challenge Forensic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link href="/css/bootstrap.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/slate.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/fic.css" type="text/css" rel="stylesheet" media="screen">
|
||||
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
|
@ -32,7 +31,12 @@
|
|||
<div class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="navbar-header col-sm-3">
|
||||
<div class="navbar-left col-sm-3" ng-show="!(time.start || my.team_id)">
|
||||
<a href="https://www.forum-fic.com/">
|
||||
<img src="/img/fic.png" alt="Forum International de la Cybersécurité" class="center-block">
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-left col-sm-3" ng-show="(time.start || my.team_id)">
|
||||
<a href="/">
|
||||
<img src="/img/fic.png" alt="Forum International de la Cybersécurité" class="center-block">
|
||||
</a>
|
||||
|
@ -49,8 +53,18 @@
|
|||
<span class="point">:</span>
|
||||
<span id="sec">{{ time.seconds | time }}</span>
|
||||
</div>
|
||||
<div id="clock" class="col-sm-7" ng-show="!(!time.start || my.team_id)">
|
||||
{{ time.start | date:"shortDate" }}
|
||||
<div id="clock" class="col-sm-7" ng-show="!(time.start || my.team_id)" style="padding: 25px">
|
||||
<div class="btn-group btn-group-justified btn-group-lg">
|
||||
<a class="btn btn-default" href="/">
|
||||
<span class="glyphicon glyphicon-home"></span> Accueil
|
||||
</a>
|
||||
<a class="btn btn-default" href="/rank">
|
||||
<span class="glyphicon glyphicon-list"></span> Classement
|
||||
</a>
|
||||
<a class="btn btn-default" href="https://www.youtube.com/playlist?list=PLSJ8QLhKMtQv7jRhdAn9wXSMYTsvqfieX">
|
||||
<span class="glyphicon glyphicon-blackboard"></span> Vidéos
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -59,31 +73,41 @@
|
|||
<div class="container" ng-controller="DataController">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-9 col-sm-offset-3">
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="panel panel-default" ng-show="(my.team_id)" style="margin-top: 10px; margin-bottom: 0px;">
|
||||
<div class="panel-heading" style="background-color: {{ teams[my.team_id].color }}; color: {{ teams[my.team_id].color }};">
|
||||
<a style="margin: -8px -13px; color: {{ teams[my.team_id].color }};" class="pull-right btn btn-default" href="/edit"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></a>
|
||||
<div class="panel-title">
|
||||
<strong class="teamname">{{ my.name }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<span ng-show="teams[my.team_id].rank">{{ teams[my.team_id].rank }}<sup>e</sup> sur {{ teams_count }} –</span>
|
||||
{{ my.score }} points
|
||||
<div style="margin: -8px -10px;" class="pull-right btn-group">
|
||||
<a class="btn btn-default" href="/rules"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></a>
|
||||
<a class="btn btn-default" href="/rank"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-9">
|
||||
<div class="page-header">
|
||||
<h1 ng-show="(current_theme)">{{ themes[current_theme].name }} <small class="authors" ng-show="themes[current_theme].authors">{{ themes[current_theme].authors }}</small></h1>
|
||||
<h1 ng-show="(!current_theme && title)">{{ title }} <small class="authors" ng-show="authors">{{ authors }}</small></h1>
|
||||
<h1 ng-show="(!current_theme && !title)">Challenge forensic 2016 <small class="authors">Laboratoire SRS, Epita</small></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="panel panel-default" ng-show="(my.team_id)">
|
||||
<div class="panel-body">
|
||||
<strong class="teamname" style="background-color: {{ teams[my.team_id].color }}; color: {{ teams[my.team_id].color }};"><span>{{ my.name }}</span></strong>
|
||||
<a style="float: right;" class="btn btn-default btn-xs" href="/edit">voir</a><br><br>
|
||||
|
||||
<span ng-show="teams[my.team_id].rank">{{ teams[my.team_id].rank }}<sup>e</sup> sur {{ teams_count }} –</span>
|
||||
{{ my.score }} points
|
||||
<a style="float: right;" class="btn btn-default btn-xs btn-primary" href="/rank">classement</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-group">
|
||||
<a ng-repeat="(k,theme) in themes" ng-class="{active: k == current_theme}" class="list-group-item" href="/{{ k }}"><span class="badge"><span ng-show="(my.team_id)">{{ theme.exercice_solved }}/</span>{{ theme.exercice_count }}</span>{{ theme.name }}</a>
|
||||
<a ng-repeat="(k,theme) in themes" ng-class="{active: k == current_theme}" class="list-group-item" href="/{{ k }}"><span class="badge"><span class="glyphicon glyphicon-gift" aria-hidden="true"></span> <span ng-show="(my.team_id)">{{ theme.exercice_solved }}/</span>{{ theme.exercice_count }}</span>{{ theme.name }} <span class="glyphicon glyphicon-fire" aria-hidden="true"></span></a>
|
||||
</div>
|
||||
|
||||
<a href="https://srs.epita.fr/">
|
||||
|
@ -101,6 +125,7 @@
|
|||
<script src="/js/bootstrap.min.js"></script>
|
||||
<script src="/js/angular-route.min.js"></script>
|
||||
<script src="/js/angular-sanitize.min.js"></script>
|
||||
<script src="/js/i18n/angular-locale_fr-fr.js"></script>
|
||||
<script src="/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
angular.module("FICApp", ["ngRoute", "ngSanitize"])
|
||||
.config(function($routeProvider, $locationProvider) {
|
||||
$routeProvider
|
||||
.when("/rules", {
|
||||
templateUrl: "views/rules.html"
|
||||
})
|
||||
.when("/edit", {
|
||||
controller: "MyTeamController",
|
||||
templateUrl: "views/team-edit.html"
|
||||
|
@ -92,7 +95,29 @@ angular.module("FICApp")
|
|||
}
|
||||
}
|
||||
})
|
||||
.controller("DataController", function($scope, $http, $rootScope, $timeout) {
|
||||
.filter("size", function() {
|
||||
var units = [
|
||||
"o",
|
||||
"kio",
|
||||
"Mio",
|
||||
"Gio",
|
||||
"Tio",
|
||||
"Pio",
|
||||
"Eio",
|
||||
"Zio",
|
||||
"Yio",
|
||||
]
|
||||
return function(input) {
|
||||
var res = input;
|
||||
var unit = 0;
|
||||
while (res > 1024) {
|
||||
unit += 1;
|
||||
res = res / 1024;
|
||||
}
|
||||
return (Math.round(res * 100) / 100) + " " + units[unit];
|
||||
}
|
||||
})
|
||||
.controller("DataController", function($sce, $scope, $http, $rootScope, $timeout) {
|
||||
var actMenu = function() {
|
||||
if ($scope.my && $scope.themes) {
|
||||
angular.forEach($scope.themes, function(theme, key) {
|
||||
|
@ -139,13 +164,18 @@ angular.module("FICApp")
|
|||
}
|
||||
$http.get("/my.json").success(function(my) {
|
||||
$scope.my = my;
|
||||
angular.forEach($scope.my.exercices, function(exercice, eid) {
|
||||
if (exercice.video_uri) {
|
||||
exercice.video_uri = $sce.trustAsResourceUrl(exercice.video_uri);
|
||||
}
|
||||
});
|
||||
actMenu();
|
||||
});
|
||||
console.log("refresh!");
|
||||
}
|
||||
$rootScope.refresh();
|
||||
})
|
||||
.controller("ExerciceController", function($scope, $routeParams, $http, $rootScope) {
|
||||
.controller("ExerciceController", function($scope, $routeParams, $http, $rootScope, $timeout) {
|
||||
$rootScope.current_theme = $routeParams.theme;
|
||||
|
||||
if ($routeParams.exercice) {
|
||||
|
@ -168,9 +198,38 @@ angular.module("FICApp")
|
|||
}
|
||||
}
|
||||
|
||||
$scope.hsubmit = function(hint) {
|
||||
hint.submitted = true;
|
||||
$http({
|
||||
url: "/openhint/" + $rootScope.current_exercice,
|
||||
method: "POST",
|
||||
data: {
|
||||
id: hint.id
|
||||
}
|
||||
}).success(function(data, status, header, config) {
|
||||
var checkDiffHint = function() {
|
||||
$http.get("/my.json").success(function(my) {
|
||||
angular.forEach(my.exercices[$rootScope.current_exercice].hints, function(h,hid){
|
||||
if (hint.id == h.id) {
|
||||
if (hint.content != h.content) {
|
||||
$rootScope.refresh();
|
||||
} else {
|
||||
$timeout.cancel($scope.cbh);
|
||||
$scope.cbh = $timeout(checkDiffHint, 750);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
checkDiffHint();
|
||||
}).error(function(data, status, header, config) {
|
||||
hint.submitted = false;
|
||||
console.error(data.errmsg);
|
||||
});
|
||||
};
|
||||
})
|
||||
.controller("SubmissionController", function($scope, $http, $rootScope, $timeout) {
|
||||
$scope.flags = []
|
||||
$scope.flags = [];
|
||||
$rootScope.sberr = "";
|
||||
|
||||
var waitMy = function() {
|
||||
|
@ -178,12 +237,16 @@ angular.module("FICApp")
|
|||
$timeout.cancel($scope.cbs);
|
||||
$scope.cbs = $timeout(waitMy, 420);
|
||||
} else {
|
||||
$scope.flags = [];
|
||||
angular.forEach($scope.my.exercices[$rootScope.current_exercice].keys, function(key,kid) {
|
||||
this.push({
|
||||
var o = {
|
||||
id: kid,
|
||||
name: key,
|
||||
value: ""
|
||||
});
|
||||
};
|
||||
if ($scope.my.exercices[$rootScope.current_exercice].solved_matrix != null)
|
||||
o.found = $scope.my.exercices[$rootScope.current_exercice].solved_matrix[kid];
|
||||
this.push(o);
|
||||
}, $scope.flags);
|
||||
}
|
||||
}
|
||||
|
@ -192,23 +255,10 @@ angular.module("FICApp")
|
|||
$scope.ssubmit = function() {
|
||||
var flgs = {}
|
||||
|
||||
var filled = true;
|
||||
angular.forEach($scope.flags, function(flag,kid) {
|
||||
flgs[flag.name] = flag.value;
|
||||
filled = filled && flag.value.length > 0;
|
||||
});
|
||||
|
||||
if (!filled) {
|
||||
$rootScope.messageClass = {"text-danger": true};
|
||||
$rootScope.sberr = "Tous les champs sont obligatoires.";
|
||||
$timeout(function() {
|
||||
if ($rootScope.sberr == "Tous les champs sont obligatoires.") {
|
||||
$rootScope.sberr = "";
|
||||
}
|
||||
}, 2345);
|
||||
return;
|
||||
}
|
||||
|
||||
$http({
|
||||
url: "/submit/" + $rootScope.current_exercice,
|
||||
method: "POST",
|
||||
|
@ -226,6 +276,7 @@ angular.module("FICApp")
|
|||
$http.get("/my.json").success(function(my) {
|
||||
if ($scope.my.exercices[$rootScope.current_exercice].solved_time != my.exercices[$rootScope.current_exercice].solved_time) {
|
||||
$rootScope.refresh();
|
||||
waitMy();
|
||||
} else {
|
||||
$timeout.cancel($scope.cbd);
|
||||
$scope.cbd = $timeout(checkDiff, 750);
|
||||
|
|
5
frontend/static/js/d3.v3.min.js
vendored
Normal file
124
frontend/static/js/i18n/angular-locale_fr-fr.js
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
'use strict';
|
||||
angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": [
|
||||
"AM",
|
||||
"PM"
|
||||
],
|
||||
"DAY": [
|
||||
"dimanche",
|
||||
"lundi",
|
||||
"mardi",
|
||||
"mercredi",
|
||||
"jeudi",
|
||||
"vendredi",
|
||||
"samedi"
|
||||
],
|
||||
"ERANAMES": [
|
||||
"avant J\u00e9sus-Christ",
|
||||
"apr\u00e8s J\u00e9sus-Christ"
|
||||
],
|
||||
"ERAS": [
|
||||
"av. J.-C.",
|
||||
"ap. J.-C."
|
||||
],
|
||||
"FIRSTDAYOFWEEK": 0,
|
||||
"MONTH": [
|
||||
"janvier",
|
||||
"f\u00e9vrier",
|
||||
"mars",
|
||||
"avril",
|
||||
"mai",
|
||||
"juin",
|
||||
"juillet",
|
||||
"ao\u00fbt",
|
||||
"septembre",
|
||||
"octobre",
|
||||
"novembre",
|
||||
"d\u00e9cembre"
|
||||
],
|
||||
"SHORTDAY": [
|
||||
"dim.",
|
||||
"lun.",
|
||||
"mar.",
|
||||
"mer.",
|
||||
"jeu.",
|
||||
"ven.",
|
||||
"sam."
|
||||
],
|
||||
"SHORTMONTH": [
|
||||
"janv.",
|
||||
"f\u00e9vr.",
|
||||
"mars",
|
||||
"avr.",
|
||||
"mai",
|
||||
"juin",
|
||||
"juil.",
|
||||
"ao\u00fbt",
|
||||
"sept.",
|
||||
"oct.",
|
||||
"nov.",
|
||||
"d\u00e9c."
|
||||
],
|
||||
"STANDALONEMONTH": [
|
||||
"Janvier",
|
||||
"F\u00e9vrier",
|
||||
"Mars",
|
||||
"Avril",
|
||||
"Mai",
|
||||
"Juin",
|
||||
"Juillet",
|
||||
"Ao\u00fbt",
|
||||
"Septembre",
|
||||
"Octobre",
|
||||
"Novembre",
|
||||
"D\u00e9cembre"
|
||||
],
|
||||
"WEEKENDRANGE": [
|
||||
5,
|
||||
6
|
||||
],
|
||||
"fullDate": "EEEE d MMMM y",
|
||||
"longDate": "d MMMM y",
|
||||
"medium": "d MMM y HH:mm:ss",
|
||||
"mediumDate": "d MMM y",
|
||||
"mediumTime": "HH:mm:ss",
|
||||
"short": "dd/MM/y HH:mm",
|
||||
"shortDate": "dd/MM/y",
|
||||
"shortTime": "HH:mm"
|
||||
},
|
||||
"NUMBER_FORMATS": {
|
||||
"CURRENCY_SYM": "\u20ac",
|
||||
"DECIMAL_SEP": ",",
|
||||
"GROUP_SEP": "\u00a0",
|
||||
"PATTERNS": [
|
||||
{
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 3,
|
||||
"minFrac": 0,
|
||||
"minInt": 1,
|
||||
"negPre": "-",
|
||||
"negSuf": "",
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
{
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"maxFrac": 2,
|
||||
"minFrac": 2,
|
||||
"minInt": 1,
|
||||
"negPre": "-",
|
||||
"negSuf": "\u00a0\u00a4",
|
||||
"posPre": "",
|
||||
"posSuf": "\u00a0\u00a4"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "fr-fr",
|
||||
"pluralCat": function(n, opt_precision) { var i = n | 0; if (i == 0 || i == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
});
|
||||
}]);
|
|
@ -2,11 +2,10 @@
|
|||
<html ng-app="FICApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge FIC2016</title>
|
||||
<title>Challenge Forensic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link href="/css/bootstrap.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/slate.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
<link href="/css/fic.css" type="text/css" rel="stylesheet" media="screen">
|
||||
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
|
@ -199,6 +198,7 @@
|
|||
<script src="/js/angular-animate.min.js"></script>
|
||||
<script src="/js/angular-route.min.js"></script>
|
||||
<script src="/js/angular-sanitize.min.js"></script>
|
||||
<script src="/js/i18n/angular-locale_fr-fr.js"></script>>
|
||||
<script src="/js/public.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
2
frontend/static/robots.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow: /
|
23
frontend/static/views/rules.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<div class="well well-lg">
|
||||
<h3>Calcul des points</h3>
|
||||
<p>
|
||||
Compromissions, défauts de configuration, utilisations malveillantes,
|
||||
contournements des règles de sécurité, … tous les jours nous mettons
|
||||
en danger nos données.
|
||||
</p>
|
||||
<p>
|
||||
Saurez-vous identifier les différents vecteurs de fuites de données avec
|
||||
lesquels nos systèmes d'informations et nos utilisateurs font faces ?
|
||||
</p>
|
||||
|
||||
<h3>Les étudiants</h3>
|
||||
<p>
|
||||
<strong>Attention :</strong> puisqu'il s'agit de captures effectuées dans
|
||||
le but de découvrir si des actes malveillants ont été commis sur différents
|
||||
systèmes d'information, les contenus qui sont
|
||||
téléchargeables <em>peuvent</em> contenir du contenu malveillant !
|
||||
</p>
|
||||
<p>
|
||||
Bon courage !
|
||||
</p>
|
||||
</div>
|
|
@ -1,5 +1,5 @@
|
|||
<ul class="nav nav-tabs nav-justified">
|
||||
<li ng-repeat="(k,exercice) in themes[current_theme].exercices" ng-class="{active: k == current_exercice, disabled: !my.exercices[k]}"><a ng-show="(!my.exercices[k])">{{ exercice.title }}</a><a href="/{{ current_theme }}/{{ k }}" ng-show="(my.exercices[k])">{{ exercice.title }} <span ng-show="(my.team_id && my.exercices[k].solved)" class="badge">{{ exercice.gain }}</span></a></li>
|
||||
<li ng-repeat="(k,exercice) in themes[current_theme].exercices" ng-class="{active: k == current_exercice, disabled: !my.exercices[k]}"><a ng-show="(!my.exercices[k])">{{ exercice.title }}</a><a href="/{{ current_theme }}/{{ k }}" ng-show="(my.exercices[k])">{{ exercice.title }} <span class="glyphicon glyphicon-ok" aria-hidden="true" ng-show="(my.team_id && my.exercices[k].solved)"></span></a></li>
|
||||
</ul>
|
||||
|
||||
<div class="alert alert-warning" style="margin-top:15px;" ng-show="!(my.exercices[current_exercice])">
|
||||
|
@ -7,54 +7,58 @@
|
|||
</div>
|
||||
<div style="margin-top: 15px" class="well well-lg" ng-show="(my.exercices[current_exercice])">
|
||||
<p ng-bind-html="my.exercices[current_exercice].statement"></p>
|
||||
<blockquote ng-show="(my.exercices[current_exercice].hint)" ng-bind-html="my.exercices[current_exercice].hint"></blockquote>
|
||||
<hr ng-show="!(my.exercices[current_exercice].hint)">
|
||||
<ul>
|
||||
<li><strong>Gain :</strong> {{ themes[current_theme].exercices[current_exercice].gain }} points</li>
|
||||
<li><strong>Gain :</strong> {{ themes[current_theme].exercices[current_exercice].gain }} points <em ng-show="themes[current_theme].happy">+50% happy hour</em> <em ng-show="themes[current_theme].exercices[current_exercice].solved < 1">+50% first blood</em></li>
|
||||
<li><strong>Résolu par :</strong> {{ themes[current_theme].exercices[current_exercice].solved }} équipes</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-info" ng-show="(my.exercices[current_exercice] && my.exercices[current_exercice].files.length)">
|
||||
<div class="panel panel-default" ng-show="(my.exercices[current_exercice] && my.exercices[current_exercice].files.length)">
|
||||
<div class="panel-heading">
|
||||
<div class="panel-title">Téléchargements</div>
|
||||
<div class="panel-title"><span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span> Téléchargements</div>
|
||||
</div>
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Nom</th>
|
||||
<th>Taille</th>
|
||||
<th>SHA-1</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="file in my.exercices[current_exercice].files">
|
||||
<td><a href="{{ file.path }}" target="_self"><span class="glyphicon glyphicon-download" aria-hidden="true"></span></a></td>
|
||||
<td>{{ file.name }}</td>
|
||||
<td>{{ file.size }}</td>
|
||||
<td><code>{{ file.checksum }}</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="list-group">
|
||||
<a href="{{ file.path }}" target="_self" class="list-group-item" ng-repeat="file in my.exercices[current_exercice].files">
|
||||
<h1 class="pull-left" style="margin: 7px 7px 5px -5px"><span class="glyphicon glyphicon-download" aria-hidden="true"></span></h1>
|
||||
<h4 class="list-group-item-heading"><strong><samp>{{ file.name }}</samp></strong></h4>
|
||||
<p class="list-group-item-text">Taille : <span title="{{ file.size }} octets">{{ file.size | size }}</span> – SHA-1 : <samp>{{ file.checksum }}</samp></p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-info" ng-show="(my.exercices[current_exercice] && my.exercices[current_exercice].hints.length)">
|
||||
<div class="panel-heading">
|
||||
<div class="panel-title"><span class="glyphicon glyphicon-lamp" aria-hidden="true"></span> Indices</div>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" ng-repeat="hint in my.exercices[current_exercice].hints">
|
||||
<form class="pull-right" ng-show="!hint.unlocked" ng-submit="hsubmit(hint)">
|
||||
<button type="submit" class="btn btn-info" ng-class="{disabled: hint.submitted}"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span> Débloquer</button>
|
||||
</form>
|
||||
<h4 class="list-group-item-heading">{{ hint.title }}</h4>
|
||||
<p class="list-group-item-text" ng-show="hint.unlocked" ng-bind-html="hint.content"></p>
|
||||
<p class="list-group-item-text" ng-show="!hint.unlocked">Cet indice vous coûtera {{ hint.cost }}% des points de l'exercice</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-danger" ng-show="my.team_id && my.exercices[current_exercice] && !(my.exercices[current_exercice].solved)">
|
||||
<div class="panel-heading">
|
||||
<div class="panel-title">Soumettre une solution</div>
|
||||
<div class="panel-title"><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> Soumettre une solution</div>
|
||||
</div>
|
||||
<ul class="list-group" ng-show="(my.exercices[current_exercice].solved_number || my.exercices[current_exercice].submitted || sberr)">
|
||||
<li class="list-group-item text-warning" ng-show="my.exercices[current_exercice].solved_number">{{ my.exercices[current_exercice].solved_number }} tentative(s) effectuée(s). Dernière solution envoyée à {{ my.exercices[current_exercice].solved_time | date:"fullDate" }}.</li>
|
||||
<li class="list-group-item text-warning" ng-show="my.exercices[current_exercice].solved_number">{{ my.exercices[current_exercice].solved_number }} tentative(s) effectuée(s). Dernière solution envoyée à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}.</li>
|
||||
<li class="list-group-item" ng-class="messageClass" ng-show="my.exercices[current_exercice].submitted || sberr"><strong ng-show="!sberr">Votre solution a bien été envoyée !</strong><strong ng-show="sberr">{{ sberr }}</strong> {{ message }}</li>
|
||||
</ul>
|
||||
<div class="panel-body" ng-show="!my.exercices[current_exercice].submitted || sberr">
|
||||
<form ng-controller="SubmissionController" ng-submit="ssubmit()">
|
||||
<div class="form-group" ng-repeat="key in flags">
|
||||
<div class="form-group" ng-repeat="key in flags" ng-class="{'has-success': key.found, 'has-feedback': key.found}">
|
||||
<label for="sol_{{ key.id }}">{{ key.name }} :</label>
|
||||
<input type="text" class="form-control" id="sol_{{ key.id }}" name="sol_{{ index }}" ng-model="key.value">
|
||||
<input type="text" class="form-control" id="sol_{{ key.id }}" name="sol_{{ index }}" ng-model="key.value" ng-disabled="key.found">
|
||||
<span class="glyphicon glyphicon-ok form-control-feedback" aria-hidden="true" ng-show="key.found"></span>
|
||||
</div>
|
||||
<div class="form-group text-right">
|
||||
<button type="submit" class="btn btn-warning" id="sbmt">Soumettre</button>
|
||||
<button type="submit" class="btn btn-danger" id="sbmt">Soumettre</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -62,24 +66,29 @@
|
|||
|
||||
<div class="panel panel-success" ng-show="(my.team_id && my.exercices[current_exercice].solved)">
|
||||
<div class="panel-heading">
|
||||
<div class="panel-title">Challenge réussi !</div>
|
||||
<div class="panel-title"><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> Challenge réussi !</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
Vous êtes la {{ my.exercices[current_exercice].solved_number }}<sup>e</sup> équipe à avoir résolu ce challenge à {{ my.exercices[current_exercice].solved_time | date:"fullDate" }}. Vous avez marqué {{ themes[current_theme].exercices[current_exercice].gain }} points !
|
||||
Vous êtes la {{ my.exercices[current_exercice].solved_number }}<sup>e</sup> équipe à avoir résolu ce challenge à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous avez marqué {{ themes[current_theme].exercices[current_exercice].gain }} points !
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-success" ng-show="(!my.team_id && my.exercices[current_exercice].keys)">
|
||||
<div class="panel-heading">
|
||||
<div class="panel-title">Clefs du challenge</div>
|
||||
<div class="panel-title"><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> Solution du challenge</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
Vérifiez les clefs que vous trouvez en comparant leur SHA-512 :
|
||||
</p>
|
||||
<dl class="dl-horizontal" ng-repeat="key in my.exercices[current_exercice].keys">
|
||||
<dt>{{ key.slice(128) }}</dt>
|
||||
<dd class="samp"><code>{{ key.slice(0, 128) }}</code></dd>
|
||||
<dt title="{{ key.slice(128) }}">{{ key.slice(128) }}</dt>
|
||||
<dd class="samp"><samp>{{ key.slice(0, 128) }}</samp></dd>
|
||||
</dl>
|
||||
<div class="embed-responsive">
|
||||
<iframe type="text/html" ng-show="my.exercices[current_exercice].video_uri" ng-src="{{ my.exercices[current_exercice].video_uri }}" class="embed-responsive-item">
|
||||
Regardez la vidéo de résolution de cet exercice : <a ng-href="{{ my.exercices[current_exercice].video_uri }}">{{ my.exercices[current_exercice].video_uri }}</a>.
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Challenge FIC2016</title>
|
||||
<title>Challenge Forensic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link href="/css/bootstrap.min.css" type="text/css" rel="stylesheet" media="screen">
|
||||
|
|
|
@ -2,11 +2,8 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -14,8 +11,6 @@ import (
|
|||
|
||||
type SubmissionHandler struct {
|
||||
ChallengeEnd time.Time
|
||||
DenyChName bool
|
||||
AllowRegistration bool
|
||||
}
|
||||
|
||||
func (s SubmissionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -35,113 +30,25 @@ func (s SubmissionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
// Extract URL arguments
|
||||
var sURL = strings.Split(r.URL.Path, "/")
|
||||
|
||||
if len(sURL) == 2 && s.AllowRegistration && sURL[1] == "registration" {
|
||||
if _, err := os.Stat(path.Join(SubmissionDir, "_registration")); os.IsNotExist(err) {
|
||||
log.Println("Creating _registration directory")
|
||||
if err := os.MkdirAll(path.Join(SubmissionDir, "_registration"), 0777); err != nil {
|
||||
log.Println("Unable to create _registration directory: ", err)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if f, err := ioutil.TempFile(path.Join(SubmissionDir, "_registration"), ""); err != nil {
|
||||
log.Println("Unable to open registration file:", err)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds..\"}", http.StatusInternalServerError)
|
||||
} else {
|
||||
// Read request body
|
||||
var body []byte
|
||||
if r.ContentLength > 0 {
|
||||
tmp := make([]byte, 1024)
|
||||
for {
|
||||
n, err := r.Body.Read(tmp)
|
||||
for j := 0; j < n; j++ {
|
||||
body = append(body, tmp[j])
|
||||
}
|
||||
if err != nil || n <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f.Write(body)
|
||||
f.Close()
|
||||
}
|
||||
|
||||
http.Error(w, "{\"errmsg\":\"Demande d'enregistrement acceptée\"}", http.StatusAccepted)
|
||||
return
|
||||
} else if len(sURL) != 3 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
if len(sURL) != 2 {
|
||||
http.Error(w, "{\"errmsg\":\"Arguments manquants.\"}", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
team := sURL[0]
|
||||
|
||||
if time.Now().Sub(s.ChallengeEnd) > 0 {
|
||||
http.Error(w, "{\"errmsg\":\"Vous ne pouvez plus soumettre, le challenge est terminé.\"}", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse arguments
|
||||
if _, err := os.Stat(path.Join(TeamsDir, sURL[1])); os.IsNotExist(err) || len(sURL[1]) < 1 || sURL[1][0] == '_' {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else if pex, err := strconv.Atoi(sURL[2]); (s.DenyChName || sURL[2] != "name") && err != nil {
|
||||
if pex, err := strconv.Atoi(sURL[1]); err != nil {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else {
|
||||
team := sURL[1]
|
||||
exercice := fmt.Sprintf("%d", pex)
|
||||
|
||||
var exercice string
|
||||
if sURL[2] == "name" {
|
||||
exercice = sURL[2]
|
||||
} else {
|
||||
exercice = fmt.Sprintf("%d", pex)
|
||||
if saveTeamFile(TeamsDir, team, exercice, w, r) {
|
||||
http.Error(w, "{\"errmsg\":\"Son traitement est en cours...\"}", http.StatusAccepted)
|
||||
}
|
||||
|
||||
if len(exercice) <= 0 {
|
||||
log.Println("EMPTY $EXERCICE RECEIVED:", exercice)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(SubmissionDir, team)); os.IsNotExist(err) {
|
||||
log.Println("Creating submission directory for", team)
|
||||
if err := os.MkdirAll(path.Join(SubmissionDir, team), 0777); err != nil {
|
||||
log.Println("Unable to create submission directory: ", err)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Previous submission not treated
|
||||
if _, err := os.Stat(path.Join(SubmissionDir, team, exercice)); !os.IsNotExist(err) {
|
||||
http.Error(w, "{\"errmsg\":\"Du calme ! une requête est déjà en cours de traitement.\"}", http.StatusPaymentRequired)
|
||||
return
|
||||
}
|
||||
|
||||
// Read request body
|
||||
var body []byte
|
||||
if r.ContentLength > 0 {
|
||||
tmp := make([]byte, 1024)
|
||||
for {
|
||||
n, err := r.Body.Read(tmp)
|
||||
for j := 0; j < n; j++ {
|
||||
body = append(body, tmp[j])
|
||||
}
|
||||
if err != nil || n <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store content in file
|
||||
if file, err := os.Create(path.Join(SubmissionDir, team, exercice)); err != nil {
|
||||
log.Println("Unable to open exercice file:", err)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
return
|
||||
} else {
|
||||
file.Write(body)
|
||||
file.Close()
|
||||
}
|
||||
http.Error(w, "{\"errmsg\":\"Son traitement est en cours...\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
@ -19,6 +20,8 @@ type timeObject struct {
|
|||
}
|
||||
|
||||
func (t TimeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Handling %s request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if j, err := json.Marshal(timeObject{t.StartTime.Unix(), time.Now().Unix(), int(t.Duration.Seconds())}); err != nil {
|
||||
|
|
36
libfic/db.go
|
@ -64,7 +64,6 @@ CREATE TABLE IF NOT EXISTS exercices(
|
|||
id_theme INTEGER NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
statement TEXT NOT NULL,
|
||||
hint TEXT NOT NULL,
|
||||
depend INTEGER,
|
||||
gain INTEGER NOT NULL,
|
||||
video_uri VARCHAR(255) NOT NULL,
|
||||
|
@ -85,6 +84,18 @@ CREATE TABLE IF NOT EXISTS exercice_files(
|
|||
size INTEGER NOT NULL,
|
||||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS exercice_hints(
|
||||
id_hint INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
id_exercice INTEGER NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
cost INTEGER NOT NULL,
|
||||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -96,6 +107,17 @@ CREATE TABLE IF NOT EXISTS exercice_keys(
|
|||
value BINARY(64) NOT NULL,
|
||||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS key_found(
|
||||
id_key INTEGER NOT NULL,
|
||||
id_team INTEGER NOT NULL,
|
||||
time TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY(id_key) REFERENCES exercice_keys(id_key),
|
||||
FOREIGN KEY(id_team) REFERENCES teams(id_team)
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -104,6 +126,7 @@ CREATE TABLE IF NOT EXISTS exercice_solved(
|
|||
id_exercice INTEGER NOT NULL,
|
||||
id_team INTEGER NOT NULL,
|
||||
time TIMESTAMP NOT NULL,
|
||||
coefficient FLOAT NOT NULL,
|
||||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
|
||||
FOREIGN KEY(id_team) REFERENCES teams(id_team)
|
||||
);
|
||||
|
@ -118,6 +141,17 @@ CREATE TABLE IF NOT EXISTS exercice_tries(
|
|||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
|
||||
FOREIGN KEY(id_team) REFERENCES teams(id_team)
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS team_hints(
|
||||
id_team INTEGER,
|
||||
id_hint INTEGER,
|
||||
time TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY(id_hint) REFERENCES exercice_hints(id_hint),
|
||||
FOREIGN KEY(id_team) REFERENCES teams(id_team)
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -5,11 +5,12 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var PartialValidation bool
|
||||
|
||||
type Exercice struct {
|
||||
Id int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Statement string `json:"statement"`
|
||||
Hint string `json:"hint"`
|
||||
Depend *int64 `json:"depend"`
|
||||
Gain int64 `json:"gain"`
|
||||
VideoURI string `json:"videoURI"`
|
||||
|
@ -17,7 +18,7 @@ type Exercice struct {
|
|||
|
||||
func GetExercice(id int64) (Exercice, error) {
|
||||
var e Exercice
|
||||
if err := DBQueryRow("SELECT id_exercice, title, statement, hint, depend, gain, video_uri FROM exercices WHERE id_exercice = ?", id).Scan(&e.Id, &e.Title, &e.Statement, &e.Hint, &e.Depend, &e.Gain, &e.VideoURI); err != nil {
|
||||
if err := DBQueryRow("SELECT id_exercice, title, statement, depend, gain, video_uri FROM exercices WHERE id_exercice = ?", id).Scan(&e.Id, &e.Title, &e.Statement, &e.Depend, &e.Gain, &e.VideoURI); err != nil {
|
||||
return Exercice{}, err
|
||||
}
|
||||
|
||||
|
@ -26,7 +27,7 @@ func GetExercice(id int64) (Exercice, error) {
|
|||
|
||||
func (t Theme) GetExercice(id int) (Exercice, error) {
|
||||
var e Exercice
|
||||
if err := DBQueryRow("SELECT id_exercice, title, statement, hint, depend, gain, video_uri FROM exercices WHERE id_theme = ? AND id_exercice = ?", t.Id, id).Scan(&e.Id, &e.Title, &e.Statement, &e.Hint, &e.Depend, &e.Gain, &e.VideoURI); err != nil {
|
||||
if err := DBQueryRow("SELECT id_exercice, title, statement, depend, gain, video_uri FROM exercices WHERE id_theme = ? AND id_exercice = ?", t.Id, id).Scan(&e.Id, &e.Title, &e.Statement, &e.Depend, &e.Gain, &e.VideoURI); err != nil {
|
||||
return Exercice{}, err
|
||||
}
|
||||
|
||||
|
@ -34,7 +35,7 @@ func (t Theme) GetExercice(id int) (Exercice, error) {
|
|||
}
|
||||
|
||||
func GetExercices() ([]Exercice, error) {
|
||||
if rows, err := DBQuery("SELECT id_exercice, title, statement, hint, depend, gain, video_uri FROM exercices"); err != nil {
|
||||
if rows, err := DBQuery("SELECT id_exercice, title, statement, depend, gain, video_uri FROM exercices"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
@ -42,7 +43,7 @@ func GetExercices() ([]Exercice, error) {
|
|||
var exos = make([]Exercice, 0)
|
||||
for rows.Next() {
|
||||
var e Exercice
|
||||
if err := rows.Scan(&e.Id, &e.Title, &e.Statement, &e.Hint, &e.Depend, &e.Gain, &e.VideoURI); err != nil {
|
||||
if err := rows.Scan(&e.Id, &e.Title, &e.Statement, &e.Depend, &e.Gain, &e.VideoURI); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exos = append(exos, e)
|
||||
|
@ -56,7 +57,7 @@ func GetExercices() ([]Exercice, error) {
|
|||
}
|
||||
|
||||
func (t Theme) GetExercices() ([]Exercice, error) {
|
||||
if rows, err := DBQuery("SELECT id_exercice, title, statement, hint, depend, gain, video_uri FROM exercices WHERE id_theme = ?", t.Id); err != nil {
|
||||
if rows, err := DBQuery("SELECT id_exercice, title, statement, depend, gain, video_uri FROM exercices WHERE id_theme = ?", t.Id); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
@ -64,7 +65,7 @@ func (t Theme) GetExercices() ([]Exercice, error) {
|
|||
var exos = make([]Exercice, 0)
|
||||
for rows.Next() {
|
||||
var e Exercice
|
||||
if err := rows.Scan(&e.Id, &e.Title, &e.Statement, &e.Hint, &e.Depend, &e.Gain, &e.VideoURI); err != nil {
|
||||
if err := rows.Scan(&e.Id, &e.Title, &e.Statement, &e.Depend, &e.Gain, &e.VideoURI); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exos = append(exos, e)
|
||||
|
@ -77,28 +78,28 @@ func (t Theme) GetExercices() ([]Exercice, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (t Theme) AddExercice(title string, statement string, hint string, depend *Exercice, gain int, videoURI string) (Exercice, error) {
|
||||
func (t Theme) AddExercice(title string, statement string, depend *Exercice, gain int, videoURI string) (Exercice, error) {
|
||||
var dpd interface{}
|
||||
if depend == nil {
|
||||
dpd = nil
|
||||
} else {
|
||||
dpd = depend.Id
|
||||
}
|
||||
if res, err := DBExec("INSERT INTO exercices (id_theme, title, statement, hint, depend, gain, video_uri) VALUES (?, ?, ?, ?, ?, ?, ?)", t.Id, title, statement, hint, dpd, gain, videoURI); err != nil {
|
||||
if res, err := DBExec("INSERT INTO exercices (id_theme, title, statement, depend, gain, video_uri) VALUES (?, ?, ?, ?, ?, ?)", t.Id, title, statement, dpd, gain, videoURI); err != nil {
|
||||
return Exercice{}, err
|
||||
} else if eid, err := res.LastInsertId(); err != nil {
|
||||
return Exercice{}, err
|
||||
} else {
|
||||
if depend == nil {
|
||||
return Exercice{eid, title, statement, hint, nil, int64(gain), videoURI}, nil
|
||||
return Exercice{eid, title, statement, nil, int64(gain), videoURI}, nil
|
||||
} else {
|
||||
return Exercice{eid, title, statement, hint, &depend.Id, int64(gain), videoURI}, nil
|
||||
return Exercice{eid, title, statement, &depend.Id, int64(gain), videoURI}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e Exercice) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE exercices SET title = ?, statement = ?, hint = ?, depend = ?, gain = ?, video_uri = ? WHERE id_exercice = ?", e.Title, e.Statement, e.Hint, e.Depend, e.Gain, e.VideoURI, e.Id); err != nil {
|
||||
if res, err := DBExec("UPDATE exercices SET title = ?, statement = ?, depend = ?, gain = ?, video_uri = ? WHERE id_exercice = ?", e.Title, e.Statement, e.Depend, e.Gain, e.VideoURI, e.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
|
@ -202,13 +203,14 @@ func (e Exercice) CheckResponse(resps map[string]string, t Team) (bool, error) {
|
|||
|
||||
valid := true
|
||||
for _, key := range keys {
|
||||
if _, ok := resps[key.Type]; !ok {
|
||||
if res, ok := resps[key.Type]; !ok {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
if !key.Check(resps[key.Type]) {
|
||||
valid = false
|
||||
break
|
||||
} else if !key.Check(res) {
|
||||
if !PartialValidation || t.HasPartiallySolved(key) == nil {
|
||||
valid = false
|
||||
}
|
||||
} else {
|
||||
key.FoundBy(t)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
74
libfic/hint.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package fic
|
||||
|
||||
import (
|
||||
)
|
||||
|
||||
type EHint struct {
|
||||
Id int64 `json:"id"`
|
||||
IdExercice int64 `json:"idExercice"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Cost int64 `json:"cost"`
|
||||
}
|
||||
|
||||
func GetHint(id int64) (EHint, error) {
|
||||
var h EHint
|
||||
if err := DBQueryRow("SELECT id_hint, id_exercice, title, content, cost FROM exercice_hints WHERE id_hint = ?", id).Scan(&h.Id, &h.IdExercice, &h.Title, &h.Content, &h.Cost); err != nil {
|
||||
return h, err
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (e Exercice) GetHints() ([]EHint, error) {
|
||||
if rows, err := DBQuery("SELECT id_hint, title, content, cost FROM exercice_hints WHERE id_exercice = ?", e.Id); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
var hints = make([]EHint, 0)
|
||||
for rows.Next() {
|
||||
var h EHint
|
||||
h.IdExercice = e.Id
|
||||
if err := rows.Scan(&h.Id, &h.Title, &h.Content, &h.Cost); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hints = append(hints, h)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hints, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e Exercice) AddHint(title string, content string, cost int64) (EHint, error) {
|
||||
if res, err := DBExec("INSERT INTO exercice_hints (id_exercice, title, content, cost) VALUES (?, ?, ?, ?)", e.Id, title, content, cost); err != nil {
|
||||
return EHint{}, err
|
||||
} else if hid, err := res.LastInsertId(); err != nil {
|
||||
return EHint{}, err
|
||||
} else {
|
||||
return EHint{hid, e.Id, title, content, cost}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h EHint) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE exercice_hints SET id_exercice = ?, title = ?, content = ?, cost = ? WHERE id_hint = ?", h.IdExercice, h.Title, h.Content, h.Cost, h.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func (h EHint) Delete() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM exercice_hints WHERE id_hint = ?", h.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package fic
|
|||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
|
@ -88,3 +89,7 @@ func (k Key) Check(val string) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func (k Key) FoundBy(t Team) {
|
||||
DBExec("INSERT INTO key_found (id_key, id_team, time) VALUES (?, ?, ?)", k.Id, t.Id, time.Now())
|
||||
}
|
||||
|
|
179
libfic/team.go
|
@ -1,10 +1,13 @@
|
|||
package fic
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var UnlockedChallenges bool
|
||||
|
||||
type Team struct {
|
||||
Id int64 `json:"id"`
|
||||
InitialName string `json:"initialName"`
|
||||
|
@ -142,7 +145,7 @@ func GetTryRank() ([]int64, error) {
|
|||
}
|
||||
|
||||
func (t Team) HasAccess(e Exercice) bool {
|
||||
if e.Depend == nil {
|
||||
if e.Depend == nil || UnlockedChallenges {
|
||||
return true
|
||||
} else {
|
||||
ed := Exercice{}
|
||||
|
@ -152,6 +155,72 @@ func (t Team) HasAccess(e Exercice) bool {
|
|||
}
|
||||
}
|
||||
|
||||
func NbTry(t *Team, e Exercice) int {
|
||||
var cnt *int
|
||||
|
||||
if t != nil {
|
||||
DBQueryRow("SELECT COUNT(*) FROM exercice_tries WHERE id_team = ? AND id_exercice = ?", t.Id, e.Id).Scan(&cnt)
|
||||
} else {
|
||||
DBQueryRow("SELECT COUNT(*) FROM exercice_tries WHERE id_exercice = ?", e.Id).Scan(&cnt)
|
||||
}
|
||||
|
||||
if cnt == nil {
|
||||
return 0
|
||||
} else {
|
||||
return *cnt
|
||||
}
|
||||
}
|
||||
|
||||
func GetTries(t *Team, e *Exercice) ([]time.Time, error) {
|
||||
var rows *sql.Rows
|
||||
var err error
|
||||
|
||||
if t == nil {
|
||||
if e == nil {
|
||||
rows, err = DBQuery("SELECT time FROM exercice_tries ORDER BY time ASC")
|
||||
} else {
|
||||
rows, err = DBQuery("SELECT time FROM exercice_tries WHERE id_exercice = ? ORDER BY time ASC", e.Id)
|
||||
}
|
||||
} else {
|
||||
if e == nil {
|
||||
rows, err = DBQuery("SELECT time FROM exercice_tries WHERE id_team = ? ORDER BY time ASC", t.Id)
|
||||
} else {
|
||||
rows, err = DBQuery("SELECT time FROM exercice_tries WHERE id_team = ? AND id_exercice = ? ORDER BY time ASC", t.Id, e.Id)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
times := make([]time.Time, 0)
|
||||
for rows.Next() {
|
||||
var tm time.Time
|
||||
if err := rows.Scan(&tm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
times = append(times, tm)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return times, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t Team) HasHint(h EHint) (bool) {
|
||||
var tm *time.Time
|
||||
DBQueryRow("SELECT MIN(time) FROM team_hints WHERE id_team = ? AND id_hint = ?", t.Id, h.Id).Scan(&tm)
|
||||
return tm != nil
|
||||
}
|
||||
|
||||
func (t Team) OpenHint(h EHint) (error) {
|
||||
_, err := DBExec("INSERT INTO team_hints (id_team, id_hint, time) VALUES (?, ?, ?)", t.Id, h.Id, time.Now())
|
||||
return err
|
||||
}
|
||||
|
||||
func (t Team) HasSolved(e Exercice) (bool, time.Time, int64) {
|
||||
var nb *int64
|
||||
var tm *time.Time
|
||||
|
@ -170,6 +239,114 @@ func (t Team) HasSolved(e Exercice) (bool, time.Time, int64) {
|
|||
}
|
||||
}
|
||||
|
||||
func IsSolved(e Exercice) (int, time.Time) {
|
||||
var nb *int
|
||||
var tm *time.Time
|
||||
if DBQueryRow("SELECT COUNT(id_exercice), MIN(time) FROM exercice_solved WHERE id_exercice = ?", e.Id).Scan(&nb, &tm); nb == nil || tm == nil {
|
||||
return 0, time.Time{}
|
||||
} else {
|
||||
return *nb, *tm
|
||||
}
|
||||
}
|
||||
|
||||
func (t Team) HasPartiallySolved(k Key) (*time.Time) {
|
||||
var tm *time.Time
|
||||
DBQueryRow("SELECT MIN(time) FROM key_found WHERE id_team = ? AND id_key = ?", t.Id, k.Id).Scan(&tm)
|
||||
return tm
|
||||
}
|
||||
|
||||
type statLine struct {
|
||||
Tip string `json:"tip"`
|
||||
Total int `json:"total"`
|
||||
Solved int `json:"solved"`
|
||||
Tried int `json:"tried"`
|
||||
Tries int `json:"tries"`
|
||||
}
|
||||
|
||||
type teamStats struct {
|
||||
Levels []statLine `json:"levels"`
|
||||
Themes []statLine `json:"themes"`
|
||||
}
|
||||
|
||||
func (s *teamStats) GetLevel(level int) *statLine {
|
||||
level -= 1
|
||||
|
||||
for len(s.Levels) <= level {
|
||||
s.Levels = append(s.Levels, statLine{
|
||||
fmt.Sprintf("Level %d", (len(s.Levels) + 1)),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
})
|
||||
}
|
||||
|
||||
return &s.Levels[level]
|
||||
}
|
||||
|
||||
func (t Team) GetStats() (interface{}, error) {
|
||||
return GetTeamsStats(&t)
|
||||
}
|
||||
|
||||
func GetTeamsStats(t *Team) (interface{}, error) {
|
||||
stat := teamStats{}
|
||||
|
||||
if themes, err := GetThemes(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, theme := range themes {
|
||||
total := 0
|
||||
solved := 0
|
||||
tried := 0
|
||||
tries := 0
|
||||
|
||||
if exercices, err := theme.GetExercices(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, exercice := range exercices {
|
||||
var lvl int
|
||||
if lvl, err = exercice.GetLevel(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sLvl := stat.GetLevel(lvl)
|
||||
|
||||
total += 1
|
||||
sLvl.Total += 1
|
||||
|
||||
if t != nil {
|
||||
if b, _, _ := t.HasSolved(exercice); b {
|
||||
solved += 1
|
||||
sLvl.Solved += 1
|
||||
}
|
||||
} else {
|
||||
if n, _ := IsSolved(exercice); n > 0 {
|
||||
solved += 1
|
||||
sLvl.Solved += 1
|
||||
}
|
||||
}
|
||||
|
||||
try := NbTry(t, exercice)
|
||||
if try > 0 {
|
||||
tried += 1
|
||||
tries += try
|
||||
sLvl.Tried += 1
|
||||
sLvl.Tries += try
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stat.Themes = append(stat.Themes, statLine{
|
||||
theme.Name,
|
||||
total,
|
||||
solved,
|
||||
tried,
|
||||
tries,
|
||||
})
|
||||
}
|
||||
|
||||
return stat, nil
|
||||
}
|
||||
}
|
||||
|
||||
type exportedTeam struct {
|
||||
Name string `json:"name"`
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
"path"
|
||||
)
|
||||
|
||||
type myTeamFile struct {
|
||||
|
@ -12,14 +13,22 @@ type myTeamFile struct {
|
|||
Checksum string `json:"checksum"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
type myTeamHint struct {
|
||||
HintId int64 `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Cost int64 `json:"cost"`
|
||||
Unlocked bool `json:"unlocked"`
|
||||
}
|
||||
type myTeamExercice struct {
|
||||
ThemeId int `json:"theme_id"`
|
||||
Statement string `json:"statement"`
|
||||
Hint string `json:"hint"`
|
||||
Hints []myTeamHint `json:"hints"`
|
||||
Gain int64 `json:"gain"`
|
||||
Files []myTeamFile `json:"files"`
|
||||
Keys []string `json:"keys"`
|
||||
Solved bool `json:"solved"`
|
||||
SolvedMat []bool `json:"solved_matrix"`
|
||||
SolvedTime time.Time `json:"solved_time"`
|
||||
SolvedNumber int64 `json:"solved_number"`
|
||||
VideoURI string `json:"video_uri"`
|
||||
|
@ -34,6 +43,8 @@ type myTeam struct {
|
|||
|
||||
func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
||||
ret := myTeam{}
|
||||
|
||||
// Fill information about the team
|
||||
if t == nil {
|
||||
ret.Id = 0
|
||||
} else {
|
||||
|
@ -45,8 +56,9 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
|||
}
|
||||
|
||||
}
|
||||
ret.Exercices = map[string]myTeamExercice{}
|
||||
|
||||
// Fill exercices, only if the challenge is started
|
||||
ret.Exercices = map[string]myTeamExercice{}
|
||||
if exos, err := GetExercices(); err != nil {
|
||||
return ret, err
|
||||
} else if started {
|
||||
|
@ -57,7 +69,6 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
|||
exercice.ThemeId = tid
|
||||
}
|
||||
exercice.Statement = e.Statement
|
||||
exercice.Hint = e.Hint
|
||||
if t == nil {
|
||||
exercice.VideoURI = e.VideoURI
|
||||
exercice.Solved = true
|
||||
|
@ -66,6 +77,36 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
|||
exercice.Solved, exercice.SolvedTime, exercice.SolvedNumber = t.HasSolved(e)
|
||||
}
|
||||
|
||||
// Expose exercice files
|
||||
|
||||
exercice.Files = []myTeamFile{}
|
||||
|
||||
if files, err := e.GetFiles(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, f := range files {
|
||||
exercice.Files = append(exercice.Files, myTeamFile{path.Join(FilesDir, f.Path), f.Name, hex.EncodeToString(f.Checksum), f.Size})
|
||||
}
|
||||
}
|
||||
|
||||
// Expose exercice hints
|
||||
|
||||
exercice.Hints = []myTeamHint{}
|
||||
|
||||
if hints, err := e.GetHints(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, h := range hints {
|
||||
if t == nil || t.HasHint(h) {
|
||||
exercice.Hints = append(exercice.Hints, myTeamHint{h.Id, h.Title, h.Content, h.Cost, true})
|
||||
} else {
|
||||
exercice.Hints = append(exercice.Hints, myTeamHint{h.Id, h.Title, "", h.Cost, false})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expose exercice keys
|
||||
|
||||
exercice.Keys = []string{}
|
||||
|
||||
if keys, err := e.GetKeys(); err != nil {
|
||||
|
@ -76,20 +117,14 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
|||
exercice.Keys = append(exercice.Keys, fmt.Sprintf("%x", k.Value)+k.Type)
|
||||
} else {
|
||||
exercice.Keys = append(exercice.Keys, k.Type)
|
||||
if PartialValidation {
|
||||
exercice.SolvedMat = append(exercice.SolvedMat, t.HasPartiallySolved(k) != nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exercice.Files = []myTeamFile{}
|
||||
|
||||
if files, err := e.GetFiles(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, f := range files {
|
||||
exercice.Files = append(exercice.Files, myTeamFile{f.Path, f.Name, hex.EncodeToString(f.Checksum), f.Size})
|
||||
}
|
||||
}
|
||||
|
||||
// Hash table ordered by exercice Id
|
||||
ret.Exercices[fmt.Sprintf("%d", e.Id)] = exercice
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ func ExportThemes() (interface{}, error) {
|
|||
}
|
||||
ret[fmt.Sprintf("%d", theme.Id)] = exportedTheme{
|
||||
theme.Name,
|
||||
theme.Authors[:len(theme.Authors)-1],
|
||||
theme.Authors,
|
||||
exos,
|
||||
}
|
||||
}
|
||||
|
|
1
playbooks/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
playbook.retry
|
4
playbooks/ansible.cfg
Normal file
|
@ -0,0 +1,4 @@
|
|||
[defaults]
|
||||
hostfile = stage
|
||||
legacy_playbook_variables = no
|
||||
pipelining = False
|
2
playbooks/group_vars/all
Normal file
|
@ -0,0 +1,2 @@
|
|||
nrpe_allowed_hosts:
|
||||
- montou.ra.nemunai.re
|
8
playbooks/playbook.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
|
||||
- name: Custom ansible playbook
|
||||
hosts: all
|
||||
roles:
|
||||
- nrpe
|
||||
- fic-backend
|
||||
- fic-frontend
|
1
playbooks/roles/fic-backend/files/backend
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../../backend/backend
|
12
playbooks/roles/fic-backend/files/backend.service
Normal file
|
@ -0,0 +1,12 @@
|
|||
[Unit]
|
||||
Description=FIC Backend service
|
||||
After=mysql.service
|
||||
|
||||
[Service]
|
||||
User=fic
|
||||
Group=nogroup
|
||||
WorkingDirectory=/home/fic
|
||||
ExecStart=/home/fic/backend -unlockedChallenges -teams ./TEAMS -submission ./submissions
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
1
playbooks/roles/fic-backend/files/static
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../../admin/static/
|
57
playbooks/roles/fic-backend/tasks/main.yml
Normal file
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
- name: install mysql
|
||||
apt: name=mariadb-server
|
||||
|
||||
- name: create fic user
|
||||
user:
|
||||
name=fic
|
||||
home=/home/fic
|
||||
group=nogroup
|
||||
|
||||
- name: copy backend binary
|
||||
copy:
|
||||
src=backend
|
||||
mode=755
|
||||
dest=/home/fic/backend
|
||||
|
||||
- name: copy admin htdocs
|
||||
copy:
|
||||
src=static/
|
||||
dest=/home/fic/admin-htdocs
|
||||
|
||||
- name: copy admin binary
|
||||
copy:
|
||||
src=admin
|
||||
mode=755
|
||||
dest=/home/fic/admin
|
||||
|
||||
|
||||
- name: enable and start mysql daemon
|
||||
service: name=mysql enabled=yes state=started
|
||||
|
||||
- name: create fic database
|
||||
mysql_db:
|
||||
name=fic
|
||||
state=present
|
||||
|
||||
- name: create fic user
|
||||
mysql_user:
|
||||
name=fic
|
||||
password=fic
|
||||
priv=fic.*:ALL
|
||||
state=present
|
||||
|
||||
|
||||
- name: add backend service
|
||||
copy:
|
||||
src=backend.service
|
||||
dest=/lib/systemd/system/fic-backend.service
|
||||
|
||||
- name: reload systemd
|
||||
command: systemctl daemon-reload
|
||||
|
||||
- name: enable and start fic-backend
|
||||
service:
|
||||
name=fic-backend
|
||||
enabled=yes
|
||||
state=started
|
1
playbooks/roles/fic-frontend/files/ficpasswd
Normal file
|
@ -0,0 +1 @@
|
|||
nemunaire:$apr1$GCAyuMBH$BGenYoXt1ZX7x7bt6bPa0.
|
1
playbooks/roles/fic-frontend/files/frontend
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../../frontend/frontend
|
12
playbooks/roles/fic-frontend/files/frontend.service
Normal file
|
@ -0,0 +1,12 @@
|
|||
[Unit]
|
||||
Description=FIC Frontend service
|
||||
After=nginx.service
|
||||
|
||||
[Service]
|
||||
User=fic
|
||||
Group=nogroup
|
||||
WorkingDirectory=/home/fic
|
||||
ExecStart=/home/fic/frontend -teams ./TEAMS -submission ./submissions -start 1477954800 -duration 2065h
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
179
playbooks/roles/fic-frontend/files/nginx-frontend-htpasswd.conf
Normal file
|
@ -0,0 +1,179 @@
|
|||
server_tokens off;
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=24h max_size=1g;
|
||||
proxy_connect_timeout 1s;
|
||||
|
||||
server {
|
||||
listen 80 default;
|
||||
listen [::]:80 default;
|
||||
|
||||
root /home/fic/frontend-htdocs/;
|
||||
|
||||
error_page 401 /welcome.html;
|
||||
error_page 403 404 /e404.html;
|
||||
error_page 413 404 /e413.html;
|
||||
error_page 500 502 504 /e500.html;
|
||||
|
||||
location = / {
|
||||
auth_basic "Secure Zone";
|
||||
auth_basic_user_file ficpasswd;
|
||||
}
|
||||
location = /index.html {
|
||||
auth_basic "Secure Zone";
|
||||
auth_basic_user_file ficpasswd;
|
||||
}
|
||||
|
||||
location ~ ^/[0-9] {
|
||||
rewrite ^/.*$ /index.html;
|
||||
}
|
||||
location /edit {
|
||||
rewrite ^/.*$ /index.html;
|
||||
}
|
||||
|
||||
location /rank {
|
||||
rewrite ^/.*$ /index.html;
|
||||
}
|
||||
|
||||
location /files/ {
|
||||
alias /home/fic/FILES/;
|
||||
tcp_nodelay on;
|
||||
}
|
||||
|
||||
location /wait.json {
|
||||
auth_basic "Secure Zone";
|
||||
auth_basic_user_file ficpasswd;
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
root /home/fic/TEAMS/$team/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
location /public.json {
|
||||
root /home/fic/TEAMS/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
location /stats.json {
|
||||
root /home/fic/TEAMS/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
location /my.json {
|
||||
auth_basic "Secure Zone";
|
||||
auth_basic_user_file ficpasswd;
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
root /home/fic/TEAMS/$team/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
|
||||
if (!-f $document_root/../started) {
|
||||
rewrite ^/ /wait.json;
|
||||
}
|
||||
}
|
||||
location /teams.json {
|
||||
root /home/fic/TEAMS/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
location /themes.json {
|
||||
root /home/fic/TEAMS/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
auth_basic "Secure Zone";
|
||||
auth_basic_user_file ficpasswd;
|
||||
|
||||
if ($remote_user !~ "^nemunaire|bombal_s$") {
|
||||
return 403;
|
||||
}
|
||||
|
||||
proxy_pass http://localhost:8081/admin/api/;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /admin/ {
|
||||
auth_basic "Secure Zone";
|
||||
auth_basic_user_file ficpasswd;
|
||||
|
||||
if ($remote_user !~ "^nemunaire|bombal_s$") {
|
||||
return 403;
|
||||
}
|
||||
|
||||
proxy_pass http://localhost:8081;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /submit/ {
|
||||
auth_basic "Secure Zone";
|
||||
auth_basic_user_file ficpasswd;
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
rewrite ^/submit/(.*)$ /submission/$team/$1 break;
|
||||
|
||||
proxy_pass http://localhost:8080/;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /submit/name {
|
||||
auth_basic "Secure Zone";
|
||||
auth_basic_user_file ficpasswd;
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
rewrite ^/submit/.*$ /chname/$team break;
|
||||
|
||||
proxy_pass http://localhost:8080/;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /openhint/ {
|
||||
auth_basic "Secure Zone";
|
||||
auth_basic_user_file ficpasswd;
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
rewrite ^/openhint/(.*)$ /openhint/$team/$1 break;
|
||||
|
||||
proxy_pass http://localhost:8080/;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location = /time.json {
|
||||
proxy_pass http://localhost:8080/time.json;
|
||||
proxy_method GET;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
proxy_cache STATIC;
|
||||
proxy_cache_valid 1s;
|
||||
}
|
||||
|
||||
location = /events.json {
|
||||
proxy_pass http://localhost:8081/api/events;
|
||||
proxy_method GET;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
proxy_cache STATIC;
|
||||
proxy_cache_valid 3s;
|
||||
}
|
||||
}
|
179
playbooks/roles/fic-frontend/files/nginx-frontend-pam.conf
Normal file
|
@ -0,0 +1,179 @@
|
|||
server_tokens off;
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=24h max_size=1g;
|
||||
proxy_connect_timeout 1s;
|
||||
|
||||
server {
|
||||
listen 80 default;
|
||||
listen [::]:80 default;
|
||||
|
||||
root /home/fic/frontend-htdocs/;
|
||||
|
||||
error_page 401 /welcome.html;
|
||||
error_page 403 404 /e404.html;
|
||||
error_page 413 404 /e413.html;
|
||||
error_page 500 502 504 /e500.html;
|
||||
|
||||
location = / {
|
||||
auth_pam "Secure Zone";
|
||||
auth_pam_service_name "nginx-srs";
|
||||
}
|
||||
location = /index.html {
|
||||
auth_pam "Secure Zone";
|
||||
auth_pam_service_name "nginx-srs";
|
||||
}
|
||||
|
||||
location ~ ^/[0-9] {
|
||||
rewrite ^/.*$ /index.html;
|
||||
}
|
||||
location /edit {
|
||||
rewrite ^/.*$ /index.html;
|
||||
}
|
||||
|
||||
location /rank {
|
||||
rewrite ^/.*$ /index.html;
|
||||
}
|
||||
|
||||
location /files/ {
|
||||
alias /home/fic/FILES/;
|
||||
tcp_nodelay on;
|
||||
}
|
||||
|
||||
location /wait.json {
|
||||
auth_pam "Secure Zone";
|
||||
auth_pam_service_name "nginx-srs";
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
root /home/fic/TEAMS/$team/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
location /public.json {
|
||||
root /home/fic/TEAMS/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
location /stats.json {
|
||||
root /home/fic/TEAMS/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
location /my.json {
|
||||
auth_pam "Secure Zone";
|
||||
auth_pam_service_name "nginx-srs";
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
root /home/fic/TEAMS/$team/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
|
||||
if (!-f $document_root/../started) {
|
||||
rewrite ^/ /wait.json;
|
||||
}
|
||||
}
|
||||
location /teams.json {
|
||||
root /home/fic/TEAMS/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
location /themes.json {
|
||||
root /home/fic/TEAMS/;
|
||||
expires epoch;
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
auth_pam "Secure Zone";
|
||||
auth_pam_service_name "nginx-srs";
|
||||
|
||||
if ($remote_user !~ "^nemunaire|bombal_s$") {
|
||||
return 403;
|
||||
}
|
||||
|
||||
proxy_pass http://localhost:8081/admin/api/;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /admin/ {
|
||||
auth_pam "Secure Zone";
|
||||
auth_pam_service_name "nginx-srs";
|
||||
|
||||
if ($remote_user !~ "^nemunaire|bombal_s$") {
|
||||
return 403;
|
||||
}
|
||||
|
||||
proxy_pass http://localhost:8081;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /submit/ {
|
||||
auth_pam "Secure Zone";
|
||||
auth_pam_service_name "nginx-srs";
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
rewrite ^/submit/(.*)$ /submission/$team/$1 break;
|
||||
|
||||
proxy_pass http://localhost:8080/;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /submit/name {
|
||||
auth_pam "Secure Zone";
|
||||
auth_pam_service_name "nginx-srs";
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
rewrite ^/submit/.*$ /chname/$team break;
|
||||
|
||||
proxy_pass http://localhost:8080/;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /openhint/ {
|
||||
auth_pam "Secure Zone";
|
||||
auth_pam_service_name "nginx-srs";
|
||||
|
||||
include /etc/nginx/auth.conf;
|
||||
|
||||
rewrite ^/openhint/(.*)$ /openhint/$team/$1 break;
|
||||
|
||||
proxy_pass http://localhost:8080/;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location = /time.json {
|
||||
proxy_pass http://localhost:8080/time.json;
|
||||
proxy_method GET;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
proxy_cache STATIC;
|
||||
proxy_cache_valid 1s;
|
||||
}
|
||||
|
||||
location = /events.json {
|
||||
proxy_pass http://localhost:8081/api/events;
|
||||
proxy_method GET;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host localhost;
|
||||
proxy_redirect off;
|
||||
proxy_cache STATIC;
|
||||
proxy_cache_valid 3s;
|
||||
}
|
||||
}
|
1
playbooks/roles/fic-frontend/files/static
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../../frontend/static/
|
3
playbooks/roles/fic-frontend/handlers/main.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
- name: restart nginx
|
||||
service: name=nginx state=restarted
|
70
playbooks/roles/fic-frontend/tasks/main.yml
Normal file
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
- name: install nginx
|
||||
apt: name=nginx-extras
|
||||
|
||||
- name: create fic user
|
||||
user:
|
||||
name=fic
|
||||
home=/home/fic
|
||||
group=nogroup
|
||||
|
||||
- name: remove default configuration
|
||||
file:
|
||||
path=/etc/nginx/sites-enabled/default
|
||||
state=absent
|
||||
notify: restart nginx
|
||||
|
||||
- name: copy htdocs
|
||||
copy:
|
||||
src=static/
|
||||
dest=/home/fic/frontend-htdocs
|
||||
|
||||
- name: copy frontend binary
|
||||
copy:
|
||||
src=frontend
|
||||
mode=755
|
||||
dest=/home/fic/frontend
|
||||
|
||||
- name: copy htpasswd
|
||||
copy:
|
||||
src=ficpasswd
|
||||
dest=/etc/nginx/ficpasswd
|
||||
notify: restart nginx
|
||||
|
||||
#- name: copy frontend configuration
|
||||
# copy:
|
||||
# src=nginx-frontend-pam.conf
|
||||
# dest=/etc/nginx/sites-available/frontend
|
||||
# notify: restart nginx
|
||||
- name: copy frontend configuration
|
||||
copy:
|
||||
src=nginx-frontend-htpasswd.conf
|
||||
dest=/etc/nginx/sites-available/frontend
|
||||
notify: restart nginx
|
||||
|
||||
- name: activate frontend configuration
|
||||
file:
|
||||
src=/etc/nginx/sites-available/frontend
|
||||
path=/etc/nginx/sites-enabled/frontend
|
||||
state=link
|
||||
notify: restart nginx
|
||||
|
||||
- name: enable and start nginx
|
||||
service:
|
||||
name=nginx
|
||||
enabled=yes
|
||||
state=started
|
||||
|
||||
- name: add frontend service
|
||||
copy:
|
||||
src=frontend.service
|
||||
dest=/lib/systemd/system/fic-frontend.service
|
||||
|
||||
- name: reload systemd
|
||||
command: systemctl daemon-reload
|
||||
|
||||
- name: enable and start fic-frontend
|
||||
service:
|
||||
name=fic-frontend
|
||||
enabled=yes
|
||||
state=started
|
36
playbooks/roles/nrpe/defaults/main.yml
Normal file
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
# Port number we should wait for connections on.
|
||||
nrpe_port: 5666
|
||||
|
||||
# Comma-delimited list of IP address or hostnames that are allowed to talk
|
||||
# to the NRPE daemon.
|
||||
nrpe_allowed_hosts: []
|
||||
|
||||
# Allow using seed from weak location, force use of /dev/[u]random instead
|
||||
# NRPE default: 1
|
||||
nrpe_allow_weak_random_seed: 0
|
||||
|
||||
# Mapping of command definitions that this daemon will run.
|
||||
# The mapping key is a command name, the value is a command line.
|
||||
# If the command is a core plugin or a plugin installed in {{nrpe_plugins_dir}},
|
||||
# then absolute path is not necessary, use just a filename (it will be
|
||||
# automatically prefixed for nrpe.cfg).
|
||||
#
|
||||
# Example:
|
||||
# check_swap: check_swap -w 60% -c 30%
|
||||
# check_postgres: check_postgres --action=connection
|
||||
# check_lemur: /home/hody/nrpe/check_lemur --alive
|
||||
nrpe_commands:
|
||||
# free disk space: warn <20%, critical <8% / inodes: warm <20%, critical <8%,
|
||||
# exclude-type: tmpfs, devtmpfs, none (binds)
|
||||
check_disk: check_disk -w 20% -c 8% -W 20% -K 8% -X tmpfs -X devtmpfs -X none
|
||||
# free swap space: warn <60%, critical <30%
|
||||
check_swap: check_swap -w 60% -c 30%
|
||||
check_load: "check_load \
|
||||
-w {{ ansible_processor_vcpus * 1.5 }},{{ ansible_processor_vcpus }},{{ ansible_processor_vcpus * 0.7 }} \
|
||||
-c {{ ansible_processor_vcpus * 3 }},{{ ansible_processor_vcpus * 1.5 }},{{ ansible_processor_vcpus * 0.9 }}"
|
||||
check_total_procs: check_procs -w 200 -c 250 -s DRSTZ
|
||||
check_zombie_procs: check_procs -w 3 -c 10 -s Z
|
||||
check_syslog_procs: 'check_procs -c 2: --command=syslog-ng'
|
||||
check_apt: 'check_apt'
|
||||
check_ntp_time: 'check_ntp_time -H fr.pool.ntp.org -w 1 -c 2'
|
3
playbooks/roles/nrpe/handlers/main.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
- name: restart nagios-nrpe-server
|
||||
service: name=nagios-nrpe-server state=restarted
|
13
playbooks/roles/nrpe/tasks/main.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
- name: install nagios-nrpe-server
|
||||
apt: name=nagios-nrpe-server
|
||||
|
||||
- name: configure nrpe
|
||||
template: >
|
||||
src=nrpe.cfg.j2
|
||||
dest=/etc/nagios/nrpe.cfg
|
||||
owner=root group=root mode=0640
|
||||
notify: restart nagios-nrpe-server
|
||||
|
||||
- name: enable and start nrpe daemon
|
||||
service: name=nagios-nrpe-server enabled=yes state=started
|
124
playbooks/roles/nrpe/templates/nrpe.cfg.j2
Normal file
|
@ -0,0 +1,124 @@
|
|||
# {{ ansible_managed }}
|
||||
|
||||
# LOG FACILITY
|
||||
# The syslog facility that should be used for logging purposes.
|
||||
log_facility=daemon
|
||||
|
||||
# PID FILE
|
||||
# The name of the file in which the NRPE daemon should write it's process ID
|
||||
# number. The file is only written if the NRPE daemon is started by the root
|
||||
# user and is running in standalone mode.
|
||||
pid_file=/run/nrpe.pid
|
||||
|
||||
# PORT NUMBER
|
||||
# Port number we should wait for connections on.
|
||||
# NOTE: This must be a non-priviledged port (i.e. > 1024).
|
||||
# NOTE: This option is ignored if NRPE is running under either inetd or xinetd
|
||||
server_port={{ nrpe_port }}
|
||||
|
||||
# SERVER ADDRESS
|
||||
# Address that nrpe should bind to in case there are more than one interface
|
||||
# and you do not want nrpe to bind on all interfaces.
|
||||
# NOTE: This option is ignored if NRPE is running under either inetd or xinetd
|
||||
#server_address=127.0.0.1
|
||||
|
||||
# NRPE USER
|
||||
# This determines the effective user that the NRPE daemon should run as.
|
||||
# You can either supply a username or a UID.
|
||||
#
|
||||
# NOTE: This option is ignored if NRPE is running under either inetd or xinetd
|
||||
nrpe_user=nagios
|
||||
|
||||
# NRPE GROUP
|
||||
# This determines the effective group that the NRPE daemon should run as.
|
||||
# You can either supply a group name or a GID.
|
||||
#
|
||||
# NOTE: This option is ignored if NRPE is running under either inetd or xinetd
|
||||
nrpe_group=nagios
|
||||
|
||||
# ALLOWED HOST ADDRESSES
|
||||
# This is an optional comma-delimited list of IP address or hostnames
|
||||
# that are allowed to talk to the NRPE daemon. Network addresses with a bit mask
|
||||
# (i.e. 192.168.1.0/24) are also supported. Hostname wildcards are not currently
|
||||
# supported.
|
||||
#
|
||||
# Note: The daemon only does rudimentary checking of the client's IP
|
||||
# address. I would highly recommend adding entries in your /etc/hosts.allow
|
||||
# file to allow only the specified host to connect to the port
|
||||
# you are running this daemon on.
|
||||
#
|
||||
# NOTE: This option is ignored if NRPE is running under either inetd or xinetd
|
||||
allowed_hosts={{ nrpe_allowed_hosts | join(',') }}
|
||||
|
||||
# COMMAND PREFIX
|
||||
# This option allows you to prefix all commands with a user-defined string.
|
||||
# A space is automatically added between the specified prefix string and the
|
||||
# command line from the command definition.
|
||||
#
|
||||
# *** THIS EXAMPLE MAY POSE A POTENTIAL SECURITY RISK, SO USE WITH CAUTION! ***
|
||||
# Usage scenario:
|
||||
# Execute restricted commmands using sudo. For this to work, you need to add
|
||||
# the nagios user to your /etc/sudoers. An example entry for alllowing
|
||||
# execution of the plugins from might be:
|
||||
#
|
||||
# nagios ALL=(ALL) NOPASSWD: /usr/lib/nagios/plugins/
|
||||
#
|
||||
# This lets the nagios user run all commands in that directory (and only them)
|
||||
# without asking for a password. If you do this, make sure you don't give
|
||||
# random users write access to that directory or its contents!
|
||||
# command_prefix=/usr/bin/sudo
|
||||
|
||||
# DEBUGGING OPTION
|
||||
# This option determines whether or not debugging messages are logged to the
|
||||
# syslog facility.
|
||||
# Values: 0=debugging off, 1=debugging on
|
||||
debug=0
|
||||
|
||||
# COMMAND TIMEOUT
|
||||
# This specifies the maximum number of seconds that the NRPE daemon will
|
||||
# allow plugins to finish executing before killing them off.
|
||||
command_timeout=60
|
||||
|
||||
# CONNECTION TIMEOUT
|
||||
# This specifies the maximum number of seconds that the NRPE daemon will
|
||||
# wait for a connection to be established before exiting. This is sometimes
|
||||
# seen where a network problem stops the SSL being established even though
|
||||
# all network sessions are connected. This causes the nrpe daemons to
|
||||
# accumulate, eating system resources. Do not set this too low.
|
||||
connection_timeout=300
|
||||
|
||||
# WEEK RANDOM SEED OPTION
|
||||
# This directive allows you to use SSL even if your system does not have
|
||||
# a /dev/random or /dev/urandom (on purpose or because the necessary patches
|
||||
# were not applied). The random number generator will be seeded from a file
|
||||
# which is either a file pointed to by the environment valiable $RANDFILE
|
||||
# or $HOME/.rnd. If neither exists, the pseudo random number generator will
|
||||
# be initialized and a warning will be issued.
|
||||
# Values: 0=only seed from /dev/[u]random, 1=also seed from weak randomness
|
||||
|
||||
allow_weak_random_seed={{ nrpe_allow_weak_random_seed }}
|
||||
|
||||
# COMMAND DEFINITIONS
|
||||
# Command definitions that this daemon will run. Definitions
|
||||
# are in the following format:
|
||||
#
|
||||
# command[<command_name>]=<command_line>
|
||||
#
|
||||
# When the daemon receives a request to return the results of <command_name>
|
||||
# it will execute the command specified by the <command_line> argument.
|
||||
#
|
||||
# Unlike Nagios, the command line cannot contain macros - it must be
|
||||
# typed exactly as it should be executed.
|
||||
#
|
||||
# Note: Any plugins that are used in the command lines must reside
|
||||
# on the machine that this daemon is running on!
|
||||
|
||||
{% for name, cmd in nrpe_commands | dictsort %}
|
||||
{# Command with absolute path. #}
|
||||
{% if cmd.startswith('/') %}
|
||||
command[{{ name }}]={{ cmd }}
|
||||
{# Command with core plugin. #}
|
||||
{% else %}
|
||||
command[{{ name }}]=/usr/lib/nagios/plugins/{{ cmd }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
2
playbooks/stage
Normal file
|
@ -0,0 +1,2 @@
|
|||
[frontend]
|
||||
prodsrs ansible_python_interpreter=/usr/bin/python2 ansible_host=srs.epita.fr ansible_port=1227 ansible_user=root
|