Compare commits

...
This repository has been archived on 2025-06-10. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.

38 commits

Author SHA1 Message Date
89120bdaef Add role for backend 2016-12-14 13:14:47 +01:00
4a625bf3eb Add frontend playbook 2016-12-14 13:14:46 +01:00
3bcffbe251 Start playbooks with nrpe role 2016-12-14 13:14:45 +01:00
0083c6ec17 [WIP] Merge splitted files 2016-12-14 13:14:45 +01:00
84f0229044 Use 2017 logos 2016-12-14 13:12:39 +01:00
077cecdf1a frontend: interface can open hints 2016-12-14 13:12:39 +01:00
573d9650d0 frontend: able to receive opening hint 2016-12-14 13:12:38 +01:00
5d22bd3fbf backend: can open hint 2016-12-14 13:12:38 +01:00
0b1f8ea046 frontend: refactor and dispatch in many routes 2016-12-14 13:12:37 +01:00
724d8581a8 WIP misc 2016-12-14 13:12:37 +01:00
5db3f5eff9 WIP esthetic changes 2016-12-14 13:12:36 +01:00
4cd94a74c2 WIP: apply a coeff on given points 2016-12-14 13:12:36 +01:00
cda6fde9bf [WIP] frontend: add /rules page 2016-12-14 13:12:35 +01:00
8e0f7acb57 Partial resolution of exercices 2016-12-14 13:12:35 +01:00
dc8ecc9ede Multiple hints 2016-12-14 13:12:34 +01:00
73afd8124f WIP merge big splitted files before import 2016-12-14 13:12:33 +01:00
4cccf95aa8 backend: use fsnotify instead of the deprecated inotify 2016-12-14 13:12:33 +01:00
972503292d admin/api: use gorilla/mux instead of Go router 2016-12-14 13:12:32 +01:00
e2593e5f24 frontend: redesign download part 2016-12-04 20:26:39 +01:00
2472aebec4 frontend: add some glyphicons 2016-12-04 20:26:39 +01:00
cbe8ea987f frontend: move user box to the top of the page 2016-12-04 19:04:41 +01:00
4ff47fc46d backend: new option that unlock all challenges 2016-12-04 18:58:53 +01:00
105160334d themes: don't expect authors to be dirty 2016-11-25 15:26:43 +01:00
b4c17876c6 frontend: fail if TEAMS directory doesn't exists 2016-11-25 15:26:42 +01:00
3c84c6194d frontend: add resolution route 2016-11-25 15:26:42 +01:00
0c6e3e7771 admin: can give the static dir location 2016-11-25 15:26:41 +01:00
885cae4721 admin: can change the baseurl interface 2016-11-25 15:26:41 +01:00
25652847c4 by default, only listen on localhost 2016-11-25 15:26:40 +01:00
5592b2e676 fill_team: improve script reliability 2016-11-25 15:26:39 +01:00
b41180c7b0 admin: add ability to add files from local storage 2016-11-25 15:26:39 +01:00
a3940abed8 Use slate CSS theme on all pages 2016-11-19 16:40:42 +01:00
85cbab1d70 Add a logger prefix 2016-11-19 16:40:42 +01:00
c0c6762313 [admin] Improve interface 2016-11-19 16:40:41 +01:00
24cbed411e [admin] statistic generation 2016-11-19 16:40:40 +01:00
e11cd88ee2 [admin] Can retrieves tries rate 2016-11-19 16:40:40 +01:00
b618d4f886 Ready to publish solutions 2016-11-19 16:40:39 +01:00
1129bd68be frontend: log time.json GET 2016-11-19 16:40:39 +01:00
f88068c08d Add translation to french 2016-11-19 16:40:39 +01:00
91 changed files with 3185 additions and 1398 deletions

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
}

View file

@ -1,4 +1,4 @@
package main
package api
import (
"fmt"

156
admin/api/team.go Normal file
View 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
View 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
View 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
}

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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
View 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">&Eacute;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">&Eacute;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>
`

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
admin/static/img/fic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
admin/static/img/srs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View file

@ -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">&Eacute;quipes</a></li>
<li><a href="/themes">Thèmes</a></li>
<li><a href="/exercices">Exercices</a></li>
<li><a href="/events">&Eacute;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>

View file

@ -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
View file

@ -0,0 +1 @@
../../../frontend/static/js/angular-route.min.js

View file

@ -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(/</,"&lt;").replace(/>/,"&gt;"));}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
View file

@ -0,0 +1 @@
../../../frontend/static/js/angular.min.js

View file

@ -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)); });
}

File diff suppressed because one or more lines are too long

1
admin/static/js/bootstrap.min.js vendored Symbolic link
View file

@ -0,0 +1 @@
../../../frontend/static/js/bootstrap.min.js

1
admin/static/js/d3.v3.min.js vendored Symbolic link
View file

@ -0,0 +1 @@
../../../frontend/static/js/d3.v3.min.js

1
admin/static/js/i18n Symbolic link
View file

@ -0,0 +1 @@
../../../frontend/static/js/i18n/

File diff suppressed because one or more lines are too long

1
admin/static/js/jquery.min.js vendored Symbolic link
View file

@ -0,0 +1 @@
../../../frontend/static/js/jquery.min.js

View 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>

View 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
View 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)
}
}
}

View file

@ -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
View 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
View 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)
}
}

View file

@ -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 {

View 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
View 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
View 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
View 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
}

File diff suppressed because one or more lines are too long

View file

@ -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%);
}

File diff suppressed because one or more lines are too long

View file

@ -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 -->

View file

@ -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 -->

View file

@ -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 -->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

View file

@ -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 }} &ndash;</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 }} &ndash;</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>

View file

@ -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

File diff suppressed because one or more lines are too long

View 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;}
});
}]);

View file

@ -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>

View file

@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View 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é, &hellip; 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>

View file

@ -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> &ndash; 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>

View file

@ -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">

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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
View 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
}
}

View file

@ -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())
}

View file

@ -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"`

View file

@ -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
}
}

View file

@ -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
View file

@ -0,0 +1 @@
playbook.retry

4
playbooks/ansible.cfg Normal file
View file

@ -0,0 +1,4 @@
[defaults]
hostfile = stage
legacy_playbook_variables = no
pipelining = False

2
playbooks/group_vars/all Normal file
View file

@ -0,0 +1,2 @@
nrpe_allowed_hosts:
- montou.ra.nemunai.re

8
playbooks/playbook.yml Normal file
View file

@ -0,0 +1,8 @@
---
- name: Custom ansible playbook
hosts: all
roles:
- nrpe
- fic-backend
- fic-frontend

View file

@ -0,0 +1 @@
../../../../backend/backend

View 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

View file

@ -0,0 +1 @@
../../../../admin/static/

View 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

View file

@ -0,0 +1 @@
nemunaire:$apr1$GCAyuMBH$BGenYoXt1ZX7x7bt6bPa0.

View file

@ -0,0 +1 @@
../../../../frontend/frontend

View 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

View 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;
}
}

View 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;
}
}

View file

@ -0,0 +1 @@
../../../../frontend/static/

View file

@ -0,0 +1,3 @@
---
- name: restart nginx
service: name=nginx state=restarted

View 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

View 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'

View file

@ -0,0 +1,3 @@
---
- name: restart nagios-nrpe-server
service: name=nagios-nrpe-server state=restarted

View 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

View 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
View file

@ -0,0 +1,2 @@
[frontend]
prodsrs ansible_python_interpreter=/usr/bin/python2 ansible_host=srs.epita.fr ansible_port=1227 ansible_user=root