_[]-
.")})
+ return
+ }
+
+
+ // check whether name already exists
+ if db.QueryRow("SELECT 1 FROM clans WHERE name = ?", c.PostForm("username")).
+ Scan(new(int)) != sql.ErrNoRows {
+ ccreateResp(c, errorMessage{T(c, "A clan with that name already exists!")})
+ return
+ }
+
+ // check whether tag already exists
+ if db.QueryRow("SELECT 1 FROM clans WHERE tag = ?", c.PostForm("tag")).
+ Scan(new(int)) != sql.ErrNoRows {
+ ccreateResp(c, errorMessage{T(c, "A clan with that tag already exists!")})
+ return
+ }
+
+
+ // recaptcha verify
+
+ tag := "0"
+ if c.PostForm("tag") != "" {
+ tag = c.PostForm("tag")
+ }
+
+ // The actual registration.
+
+ res, err := db.Exec(`INSERT INTO clans(name, description, icon, tag)
+ VALUES (?, ?, ?, ?);`,
+ username, c.PostForm("password"), c.PostForm("email"), tag)
+ if err != nil {
+ ccreateResp(c, errorMessage{T(c, "Whoops, an error slipped in. Clan might have been created, though. I don't know.")})
+ fmt.Println(err)
+ return
+ }
+ lid, _ := res.LastInsertId()
+
+ db.Exec("INSERT INTO `user_clans`(user, clan, perms) VALUES (?, ?, 8);", getContext(c).User.ID, lid)
+
+
+
+ addMessage(c, successMessage{T(c, "Clan created.")})
+ getSession(c).Save()
+ c.Redirect(302, "/c/"+strconv.Itoa(int(lid)))
+}
+
+func ccreateResp(c *gin.Context, messages ...message) {
+ resp(c, 200, "clans/create.html", &baseTemplateData{
+ TitleBar: "Create Clan",
+ KyutGrill: "register.jpg",
+ Scripts: []string{"https://www.google.com/recaptcha/api.js"},
+ Messages: messages,
+ FormData: normaliseURLValues(c.Request.PostForm),
+ })
+}
+
+func ccreationEnabled() bool {
+ var enabled bool
+ db.QueryRow("SELECT value_int FROM system_settings WHERE name = 'ccreation_enabled'").Scan(&enabled)
+ return enabled
+}
+
+// Check User In Query Is Same As User In Y Cookie
+
+
+func ccin(s string, ss []string) bool {
+ for _, x := range ss {
+ if x == s {
+ return true
+ }
+ }
+ return false
+}
+
+var cnameRegex = regexp.MustCompile(`^[A-Za-z0-9 '_\[\]-]{2,15}$`)
\ No newline at end of file
diff --git a/clan.go b/clan.go
new file mode 100644
index 0000000..03a8203
--- /dev/null
+++ b/clan.go
@@ -0,0 +1,302 @@
+package main
+
+import (
+ "database/sql"
+ "strconv"
+ "fmt"
+ "github.com/gin-gonic/gin"
+ "math/rand"
+ "time"
+)
+
+// TODO: replace with simple ResponseInfo containing userid
+type clanData struct {
+ baseTemplateData
+ ClanID int
+}
+
+
+func leaveClan(c *gin.Context) {
+ i := c.Param("cid")
+ // login check
+ if getContext(c).User.ID == 0 {
+ resp403(c)
+ return
+ }
+ if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND clan = ? AND perms = 8", getContext(c).User.ID, i).
+ Scan(new(int)) == sql.ErrNoRows {
+ // check if a nigga the clan
+ if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND clan = ?", getContext(c).User.ID, i).
+ Scan(new(int)) == sql.ErrNoRows {
+ addMessage(c, errorMessage{T(c, "Unexpected Error...")})
+ return
+ }
+ // idk how the fuck this gonna work but fuck it
+
+
+ db.Exec("DELETE FROM user_clans WHERE user = ? AND clan = ?", getContext(c).User.ID, i)
+ addMessage(c, successMessage{T(c, "Left clan.")})
+ getSession(c).Save()
+ c.Redirect(302, "/c/"+i)
+ } else {
+ //check if user even in clan!!!
+ if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND clan = ?", getContext(c).User.ID, i).
+ Scan(new(int)) == sql.ErrNoRows {
+ addMessage(c, errorMessage{T(c, "Unexpected Error...")})
+ return
+ }
+ // delete invites
+ db.Exec("DELETE FROM clans_invites WHERE clan = ?", i)
+ // delete all members out of clan :c
+ db.Exec("DELETE FROM user_clans WHERE clan = ?", i)
+ // delete clan :c
+ db.Exec("DELETE FROM clans WHERE id = ?", i)
+
+ addMessage(c, successMessage{T(c, "Disbanded Clan.")})
+ getSession(c).Save()
+ c.Redirect(302, "/clans?mode=0")
+ }
+
+
+}
+
+
+func clanPage(c *gin.Context) {
+ var (
+ clanID int
+ clanName string
+ clanDescription string
+ clanIcon string
+ )
+
+ // ctx := getContext(c)
+
+ i := c.Param("cid")
+ if _, err := strconv.Atoi(i); err != nil {
+ err := db.QueryRow("SELECT id, name, description, icon FROM clans WHERE name = ? LIMIT 1", i).Scan(&clanID, &clanName, &clanDescription, &clanIcon)
+ if err != nil && err != sql.ErrNoRows {
+ c.Error(err)
+ }
+ } else {
+ err := db.QueryRow(`SELECT id, name, description, icon FROM clans WHERE id = ? LIMIT 1`, i).Scan(&clanID, &clanName, &clanDescription, &clanIcon)
+ switch {
+ case err == nil:
+ case err == sql.ErrNoRows:
+ err := db.QueryRow("SELECT id, name, description, icon FROM clans WHERE name = ? LIMIT 1", i).Scan(&clanID, &clanName, &clanDescription, &clanIcon)
+ if err != nil && err != sql.ErrNoRows {
+ c.Error(err)
+ }
+ default:
+ c.Error(err)
+ }
+ }
+
+ data := new(clanData)
+ data.ClanID = clanID
+ defer resp(c, 200, "clansample.html", data)
+
+ if data.ClanID == 0 {
+ data.TitleBar = "Clan not found"
+ data.Messages = append(data.Messages, warningMessage{T(c, "That clan could not be found.")})
+ return
+ }
+
+ if getContext(c).User.Privileges&1 > 0 {
+ if db.QueryRow("SELECT 1 FROM clans WHERE clan = ?", clanID).Scan(new(string)) != sql.ErrNoRows {
+ var bg string
+ db.QueryRow("SELECT background FROM clans WHERE id = ?", clanID).Scan(&bg)
+ data.KyutGrill = bg
+ data.KyutGrillAbsolute = true
+ }
+ }
+
+ data.TitleBar = T(c, "%s's Clan Page", clanName)
+ data.DisableHH = true
+ // data.Scripts = append(data.Scripts, "/static/profile.js")
+}
+
+func checkCount(rows *sql.Rows) (count int) {
+ for rows.Next() {
+ err:= rows.Scan(&count)
+ if err != nil {
+ panic(err)
+ }
+ }
+ return count
+}
+
+var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+
+func randSeq(n int) string {
+ rand.Seed(time.Now().UnixNano()+int64(3))
+ b := make([]rune, n)
+ for i := range b {
+ b[i] = letters[rand.Intn(len(letters))]
+ }
+ return string(b)
+}
+
+func createInvite(c *gin.Context) {
+ctx := getContext(c)
+ if string(c.PostForm("password")) == "" && string(c.PostForm("email")) == "" && string(c.PostForm("tag")) == "" && string(c.PostForm("bg")) == "" {
+
+
+ if ctx.User.ID == 0 {
+ resp403(c)
+ return
+ }
+ // big perms check lol ok
+ var perms int
+ db.QueryRow("SELECT perms FROM user_clans WHERE user = ? AND perms = 8 LIMIT 1", ctx.User.ID).Scan(&perms)
+ // delete old invite
+ var clan int
+ db.QueryRow("SELECT clan FROM user_clans WHERE user = ? AND perms = 8 LIMIT 1", ctx.User.ID).Scan(&clan)
+ if clan == 0 {
+ resp403(c)
+ return
+ }
+
+ db.Exec("DELETE FROM clans_invites WHERE clan = ?", clan)
+
+ var s string
+
+ s = randSeq(8)
+
+ db.Exec("INSERT INTO clans_invites(clan, invite) VALUES (?, ?)", clan, s)
+ } else {
+ // big perms check lol ok
+ var perms int
+ db.QueryRow("SELECT perms FROM user_clans WHERE user = ? AND perms = 8 LIMIT 1", ctx.User.ID).Scan(&perms)
+ // delete old invite
+ var clan int
+ db.QueryRow("SELECT clan FROM user_clans WHERE user = ? AND perms = 8 LIMIT 1", ctx.User.ID).Scan(&clan)
+ if clan == 0 {
+ resp403(c)
+ return
+ }
+
+ tag := "0"
+ if c.PostForm("tag") != "" {
+ tag = c.PostForm("tag")
+ }
+
+ if db.QueryRow("SELECT 1 FROM clans WHERE tag = ? AND id != ?", c.PostForm("tag"), clan).
+ Scan(new(int)) != sql.ErrNoRows {
+ resp403(c)
+ addMessage(c, errorMessage{T(c, "A clan with that tag already exists...")})
+ return
+ }
+
+ db.Exec("UPDATE clans SET description = ?, icon = ?, tag = ?, background = ? WHERE id = ?", c.PostForm("password"), c.PostForm("email"), tag, c.PostForm("bg"), clan)
+ }
+ addMessage(c, successMessage{T(c, "Success")})
+ getSession(c).Save()
+ c.Redirect(302, "/settings/clansettings")
+}
+
+
+func clanInvite(c *gin.Context) {
+ i := c.Param("inv")
+
+ res := resolveInvite(i)
+ s := strconv.Itoa(res)
+ if res != 0 {
+
+ // check if a nigga logged in
+ if getContext(c).User.ID == 0 {
+ resp403(c)
+ return
+ }
+
+ // restricted stuff
+ if getContext(c).User.Privileges & 1 != 1 {
+ resp403(c)
+ return
+ }
+
+ // check if clan even exists?
+ if db.QueryRow("SELECT 1 FROM clans WHERE id = ?", res).
+ Scan(new(int)) == sql.ErrNoRows {
+
+ addMessage(c, errorMessage{T(c, "Clan doesn't exist.")})
+ getSession(c).Save()
+ c.Redirect(302, "/c/"+s)
+ return
+ }
+ // check if a nigga in a clan already
+ if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ?", getContext(c).User.ID).
+ Scan(new(int)) != sql.ErrNoRows {
+
+ addMessage(c, errorMessage{T(c, "Seems like you're already in a Clan")})
+ getSession(c).Save()
+ c.Redirect(302, "/c/"+s)
+ return
+ }
+
+ // idk how the fuck this gonna work but fuck it
+ var count int
+ var limit int
+ // members check
+ db.QueryRow("SELECT COUNT(*) FROM user_clans WHERE clan = ? ", res).Scan(&count)
+ db.QueryRow("SELECT mlimit FROM clans WHERE id = ? ", res).Scan(&limit)
+ if count >= limit {
+ addMessage(c, errorMessage{T(c, "Sorry, this clan is full.")})
+ getSession(c).Save()
+ c.Redirect(302, "/c/"+s)
+ return
+ }
+ // join
+ db.Exec("INSERT INTO `user_clans`(user, clan, perms) VALUES (?, ?, 1);", getContext(c).User.ID, res)
+ addMessage(c, successMessage{T(c, "Joined clan.")})
+ getSession(c).Save()
+ c.Redirect(302, "/c/"+s)
+ } else {
+ resp403(c)
+ addMessage(c, errorMessage{T(c, "nah nigga")})
+ }
+}
+
+func clanKick(c *gin.Context) {
+ if getContext(c).User.ID == 0 {
+ resp403(c)
+ return
+ }
+
+ if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND perms = 8", getContext(c).User.ID).
+ Scan(new(int)) == sql.ErrNoRows {
+ resp403(c)
+ return
+ }
+
+ member, err := strconv.ParseInt(c.PostForm("member"), 10, 32)
+ if err != nil {
+ fmt.Println(err)
+ }
+ if member == 0 {
+ resp403(c)
+ return
+ }
+
+ if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND perms = 1", member).
+ Scan(new(int)) == sql.ErrNoRows {
+ resp403(c)
+ return
+ }
+
+ db.Exec("DELETE FROM user_clans WHERE user = ?", member)
+ addMessage(c, successMessage{T(c, "Success.")})
+ getSession(c).Save()
+ c.Redirect(302, "/settings/clansettings")
+}
+
+func resolveInvite(c string)(int) {
+ var clanid int
+ row := db.QueryRow("SELECT clan FROM clans_invites WHERE invite = ?", c)
+ err := row.Scan(&clanid)
+
+ if err != nil {
+ fmt.Println(err)
+ }
+ fmt.Println(clanid)
+ return clanid
+}
\ No newline at end of file
diff --git a/helpers.go b/helpers.go
index 493cc10..6b072bc 100644
--- a/helpers.go
+++ b/helpers.go
@@ -190,7 +190,7 @@ func blogRedirect(c *gin.Context) {
a := c.Param("url")
red := blogRedirectMap[a]
if red == "" {
- red = "https://blog.ripple.moe"
+ red = "https://medium.com/@yozora"
}
c.Redirect(301, red)
}
diff --git a/main.go b/main.go
index f3fc990..934cb65 100644
--- a/main.go
+++ b/main.go
@@ -278,7 +278,13 @@ func generateEngine() *gin.Engine {
r.GET("/register/verify", verifyAccount)
r.GET("/register/welcome", welcome)
+ r.GET("/clans/create", ccreate)
+ r.POST("/clans/create", ccreateSubmit)
+ r.GET("/c/:cid", clanPage)
+
r.GET("/u/:user", userProfile)
+ r.GET("/rx/u/:user", relaxProfile)
+ r.GET("/ap/u/:user", autoProfile)
r.GET("/b/:bid", beatmapInfo)
r.POST("/pwreset", passwordReset)
@@ -303,6 +309,8 @@ func generateEngine() *gin.Engine {
r.POST("/settings/2fa/totp", totpSetup)
r.GET("/settings/discord/finish", discordFinish)
r.POST("/settings/profbackground/:type", profBackground)
+ r.POST("/settings/clansettings", createInvite)
+ r.POST("settings/clansettings/k", clanKick)
r.POST("/dev/tokens/create", createAPIToken)
r.POST("/dev/tokens/delete", deleteAPIToken)
@@ -317,14 +325,12 @@ func generateEngine() *gin.Engine {
r.GET("/oauth/token", oauth.Token)
r.POST("/oauth/token", oauth.Token)
+ r.GET("/clans/invite/:inv", clanInvite)
+
r.GET("/donate/rates", btcconversions.GetRates)
r.Any("/blog/*url", blogRedirect)
- r.GET("/help", func(c *gin.Context) {
- c.Redirect(301, "https://support.ripple.moe")
- })
-
loadSimplePages(r)
r.NoRoute(notFound)
diff --git a/profile.go b/profile.go
index 0b2c86e..de309ca 100644
--- a/profile.go
+++ b/profile.go
@@ -73,3 +73,122 @@ func userProfile(c *gin.Context) {
data.DisableHH = true
data.Scripts = append(data.Scripts, "/static/profile.js")
}
+func relaxProfile(c *gin.Context) {
+ var (
+ userID int
+ username string
+ privileges uint64
+ )
+
+ ctx := getContext(c)
+
+ u := c.Param("user")
+ if _, err := strconv.Atoi(u); err != nil {
+ err := db.QueryRow("SELECT id, username, privileges FROM users WHERE username = ? AND "+ctx.OnlyUserPublic()+" LIMIT 1", u).Scan(&userID, &username, &privileges)
+ if err != nil && err != sql.ErrNoRows {
+ c.Error(err)
+ }
+ } else {
+ err := db.QueryRow(`SELECT id, username, privileges FROM users WHERE id = ? AND `+ctx.OnlyUserPublic()+` LIMIT 1`, u).Scan(&userID, &username, &privileges)
+ switch {
+ case err == nil:
+ case err == sql.ErrNoRows:
+ err := db.QueryRow(`SELECT id, username, privileges FROM users WHERE username = ? AND `+ctx.OnlyUserPublic()+` LIMIT 1`, u).Scan(&userID, &username, &privileges)
+ if err != nil && err != sql.ErrNoRows {
+ c.Error(err)
+ }
+ default:
+ c.Error(err)
+ }
+ }
+
+ data := new(profileData)
+ data.UserID = userID
+
+ defer resp(c, 200, "profile_relax.html", data)
+
+ if data.UserID == 0 {
+ data.TitleBar = "User not found"
+ data.Messages = append(data.Messages, warningMessage{T(c, "That user could not be found.")})
+ return
+ }
+
+ if common.UserPrivileges(privileges)&common.UserPrivilegeDonor > 0 {
+ var profileBackground struct {
+ Type int
+ Value string
+ }
+ db.Get(&profileBackground, "SELECT type, value FROM profile_backgrounds WHERE uid = ?", data.UserID)
+ switch profileBackground.Type {
+ case 1:
+ data.KyutGrill = "/static/profbackgrounds/" + profileBackground.Value
+ data.KyutGrillAbsolute = true
+ case 2:
+ data.SolidColour = profileBackground.Value
+ }
+ }
+
+ data.TitleBar = T(c, "%s's profile", username)
+ data.DisableHH = true
+ data.Scripts = append(data.Scripts, "/static/profile_relax.js")
+}
+
+func autoProfile(c *gin.Context) {
+ var (
+ userID int
+ username string
+ privileges uint64
+ )
+
+ ctx := getContext(c)
+
+ u := c.Param("user")
+ if _, err := strconv.Atoi(u); err != nil {
+ err := db.QueryRow("SELECT id, username, privileges FROM users WHERE username = ? AND "+ctx.OnlyUserPublic()+" LIMIT 1", u).Scan(&userID, &username, &privileges)
+ if err != nil && err != sql.ErrNoRows {
+ c.Error(err)
+ }
+ } else {
+ err := db.QueryRow(`SELECT id, username, privileges FROM users WHERE id = ? AND `+ctx.OnlyUserPublic()+` LIMIT 1`, u).Scan(&userID, &username, &privileges)
+ switch {
+ case err == nil:
+ case err == sql.ErrNoRows:
+ err := db.QueryRow(`SELECT id, username, privileges FROM users WHERE username = ? AND `+ctx.OnlyUserPublic()+` LIMIT 1`, u).Scan(&userID, &username, &privileges)
+ if err != nil && err != sql.ErrNoRows {
+ c.Error(err)
+ }
+ default:
+ c.Error(err)
+ }
+ }
+
+ data := new(profileData)
+ data.UserID = userID
+
+ defer resp(c, 200, "profile_auto.html", data)
+
+ if data.UserID == 0 {
+ data.TitleBar = "User not found"
+ data.Messages = append(data.Messages, warningMessage{T(c, "That user could not be found.")})
+ return
+ }
+
+ if common.UserPrivileges(privileges)&common.UserPrivilegeDonor > 0 {
+ var profileBackground struct {
+ Type int
+ Value string
+ }
+ db.Get(&profileBackground, "SELECT type, value FROM profile_backgrounds WHERE uid = ?", data.UserID)
+ switch profileBackground.Type {
+ case 1:
+ data.KyutGrill = "/static/profbackgrounds/" + profileBackground.Value
+ data.KyutGrillAbsolute = true
+ case 2:
+ data.SolidColour = profileBackground.Value
+ }
+ }
+
+ data.TitleBar = T(c, "%s's profile", username)
+ data.DisableHH = true
+ data.Scripts = append(data.Scripts, "/static/profile_auto.js")
+}
diff --git a/register.go b/register.go
index 39fc6c6..4f7f147 100644
--- a/register.go
+++ b/register.go
@@ -96,7 +96,7 @@ func registerSubmit(c *gin.Context) {
errr := db.QueryRow("SELECT id FROM beta_keys WHERE key_md5 = ? AND allowed = 1", cmd5(c.PostForm("key")))
if errr.Scan(new(int)) == sql.ErrNoRows {
- registerResp(c, errorMessage{T(c, "key bad.")})
+ registerResp(c, errorMessage{T(c, "Your Invitation Code is invalid. Please use the form below to obtain one.")})
return
}
diff --git a/static/css/beatmaps.dfc480ea.chunk.css b/static/css/beatmaps.dfc480ea.chunk.css
new file mode 100644
index 0000000..438d1cb
--- /dev/null
+++ b/static/css/beatmaps.dfc480ea.chunk.css
@@ -0,0 +1,2 @@
+.map{width:100%;min-height:145px;display:inline-block;background:#fff;overflow:hidden;position:relative;border-radius:20px;-webkit-transition:.3s;transition:.3s;box-shadow:0 0 10px 0 rgba(0,0,0,.75)}.bodySearching{margin-top:10px;background-color:rgba(0,0,0,.25);border-radius:10px;padding:25px 10px}.status{font-weight:500;z-index:3;position:absolute;-webkit-transform:translateY(-100px) translateX(5px);transform:translateY(-100px) translateX(5px);padding:2px 10px;color:#fff;margin-left:5px;border-radius:20px;background:rgba(0,0,0,.6)}.name{font-size:1.3em;-webkit-transform:translateY(-59px) translateX(7px);transform:translateY(-59px) translateX(7px);font-weight:600}.artist,.name{color:#fff;position:absolute;text-shadow:0 0 12px #000;margin-left:10px}.artist{font-size:1.1em;font-weight:500;-webkit-transform:translateY(-31px) translateX(7px);transform:translateY(-31px) translateX(7px)}.map:hover{box-shadow:0 0 10px 0 #fff}.map-header{cursor:pointer;display:block;overflow:hidden;box-shadow:0 0 10px 0 rgba(0,0,0,.75)}.map-header,.map-header img{border-top-left-radius:20px;border-top-right-radius:20px;width:100%;height:130px}.map-header img{position:absolute;background:url(data:image/png;base64,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);opacity:1}.download{margin:10px 10px 10px 5px}.download,.link{color:#000;float:right}.link{margin:10px 3px}.ignore{color:#000;float:right;margin:10px 5px}.creator{color:#000;float:left;margin:9px 9px 9px 18px}@font-face{font-family:FontAwesomeExtra;src:url(/static/media/extra.6f1f24ae.ttf) format("truetype")}.faa{font-family:FontAwesomeExtra}.fa-extra-mode-osu:before{content:"\E800"}.fa-extra-mode-fruits:before{content:"\E801"}.wow-links>a{margin-right:10px;font-size:15px}.wow-links>a.clicked{color:#ff1493}.fa-extra-mode-mania:before{content:"\E802"}.fa-extra-mode-taiko:before{content:"\E803"}.diff{margin-right:2.5px;margin-top:13.5px}.diff2{margin-right:5px;margin-top:9.5px}#alldiff{max-width:240px;overflow:hidden;display:flex;align-items:center}
+/*# sourceMappingURL=main.dfc480ea.chunk.css.map */
diff --git a/static/dist.min.js b/static/dist.min.js
index e69c9e8..7b05bb4 100644
--- a/static/dist.min.js
+++ b/static/dist.min.js
@@ -42,4 +42,4 @@ w.hide())},show:function(e){if(e=e||function(){},w.debug("Showing pop-up",C.tran
e.each(n,function(n,i){if(e.isArray(i.results)&&(r=p.search.object(t,i.results,o)[0]))return!1})):(p.debug("Finding result in results object",t),r=p.search.object(t,n,o)[0]),r||!1}},select:{firstResult:function(){p.verbose("Selecting first result"),j.first().addClass(g.active)}},set:{focus:function(){T.addClass(g.focus)},loading:function(){T.addClass(g.loading)},value:function(e){p.verbose("Setting search input value",e),S.val(e)},type:function(e){e=e||h.type,"category"==h.type&&T.addClass(h.type)},buttonPressed:function(){A.addClass(g.pressed)}},remove:{loading:function(){T.removeClass(g.loading)},focus:function(){T.removeClass(g.focus)},buttonPressed:function(){A.removeClass(g.pressed)}},query:function(){var t=p.get.value(),n=p.read.cache(t);p.has.minimumCharacters()?(n?(p.debug("Reading result from cache",t),p.save.results(n.results),p.addResults(n.html),p.inject.id(n.results)):(p.debug("Querying for",t),e.isPlainObject(h.source)||e.isArray(h.source)?p.search.local(t):p.can.useAPI()?p.search.remote(t):p.error(x.source)),h.onSearchQuery.call(D,t)):p.hideResults()},search:{local:function(e){var t,n=p.search.object(e,h.content);p.set.loading(),p.save.results(n),p.debug("Returned local search results",n),t=p.generateResults({results:n}),p.remove.loading(),p.addResults(t),p.inject.id(n),p.write.cache(e,{html:t,results:n})},remote:function(e){T.api("is loading")&&T.api("abort"),p.setup.api(e),T.api("query")},object:function(t,n,o){var r=[],a=[],s=t.toString().replace(v.escape,"\\$&"),l=new RegExp(v.beginsWith+s,"i"),c=function(t,n){var i=e.inArray(n,r)==-1,o=e.inArray(n,a)==-1;i&&o&&t.push(n)};return n=n||h.source,o=o!==i?o:h.searchFields,e.isArray(o)||(o=[o]),n===i||n===!1?(p.error(x.source),[]):(e.each(o,function(i,o){e.each(n,function(e,n){var i="string"==typeof n[o];i&&(n[o].search(l)!==-1?c(r,n):h.searchFullText&&p.fuzzySearch(t,n[o])&&c(a,n))})}),e.merge(r,a))}},fuzzySearch:function(e,t){var n=t.length,i=e.length;if("string"!=typeof e)return!1;if(e=e.toLowerCase(),t=t.toLowerCase(),i>n)return!1;if(i===n)return e===t;e:for(var o=0,r=0;o=h.minCharacters}},clear:{cache:function(e){var t=T.data(m.cache);e?e&&t&&t[e]&&(p.debug("Removing value from cache",e),delete t[e],T.data(m.cache,t)):(p.debug("Clearing cache",e),T.removeData(m.cache))}},read:{cache:function(e){var t=T.data(m.cache);return!!h.cache&&(p.verbose("Checking cache for generated html for query",e),"object"==typeof t&&t[e]!==i&&t[e])}},create:{id:function(e,t){var n,o,r=e+1;return t!==i?(n=String.fromCharCode(97+t),o=n+r,p.verbose("Creating category result id",o)):(o=r,p.verbose("Creating result id",o)),o},results:function(){0===E.length&&(E=e("").addClass(g.results).appendTo(T))}},inject:{result:function(e,t,n){p.verbose("Injecting result into results");var o=n!==i?E.children().eq(n).children(y.result).eq(t):E.children(y.result).eq(t);p.verbose("Injecting results metadata",o),o.data(m.result,e)},id:function(t){p.debug("Injecting unique ids into results");var n=0,o=0;return"category"===h.type?e.each(t,function(t,r){o=0,e.each(r.results,function(e,t){var a=r.results[e];a.id===i&&(a.id=p.create.id(o,n)),p.inject.result(a,o,n),o++}),n++}):e.each(t,function(e,n){var r=t[e];r.id===i&&(r.id=p.create.id(o)),p.inject.result(r,o),o++}),t}},save:{results:function(e){p.verbose("Saving current search results to metadata",e),T.data(m.results,e)}},write:{cache:function(e,t){var n=T.data(m.cache)!==i?T.data(m.cache):{};h.cache&&(p.verbose("Writing generated html to cache",e,t),n[e]=t,T.data(m.cache,n))}},addResults:function(t){return e.isFunction(h.onResultsAdd)&&h.onResultsAdd.call(E,t)===!1?(p.debug("onResultsAdd callback cancelled default action"),!1):void(t?(E.html(t),p.refreshResults(),h.selectFirstResult&&p.select.firstResult(),p.showResults()):p.hideResults())},showResults:function(){p.is.visible()||(p.can.transition()?(p.debug("Showing results with css animations"),E.transition({animation:h.transition+" in",debug:h.debug,verbose:h.verbose,duration:h.duration,queue:!0})):(p.debug("Showing results with javascript"),E.stop().fadeIn(h.duration,h.easing)),h.onResultsOpen.call(E))},hideResults:function(){p.is.visible()&&(p.can.transition()?(p.debug("Hiding results with css animations"),E.transition({animation:h.transition+" out",debug:h.debug,verbose:h.verbose,duration:h.duration,queue:!0})):(p.debug("Hiding results with javascript"),E.stop().fadeOut(h.duration,h.easing)),h.onResultsClose.call(E))},generateResults:function(t){p.debug("Generating html from response",t);var n=h.templates[h.type],i=e.isPlainObject(t[b.results])&&!e.isEmptyObject(t[b.results]),o=e.isArray(t[b.results])&&t[b.results].length>0,r="";return i||o?(h.maxResults>0&&(i?"standard"==h.type&&p.error(x.maxResults):t[b.results]=t[b.results].slice(0,h.maxResults)),e.isFunction(n)?r=n(t,b):p.error(x.noTemplate,!1)):h.showNoResults&&(r=p.displayMessage(x.noResults,"empty")),h.onResults.call(D,t),r},displayMessage:function(e,t){return t=t||"standard",p.debug("Displaying message",e,t),p.addResults(h.templates.message(e,t)),h.templates.message(e,t)},setting:function(t,n){if(e.isPlainObject(t))e.extend(!0,h,t);else{if(n===i)return h[t];h[t]=n}},internal:function(t,n){if(e.isPlainObject(t))e.extend(!0,p,t);else{if(n===i)return p[t];p[t]=n}},debug:function(){!h.silent&&h.debug&&(h.performance?p.performance.log(arguments):(p.debug=Function.prototype.bind.call(console.info,console,h.name+":"),p.debug.apply(console,arguments)))},verbose:function(){!h.silent&&h.verbose&&h.debug&&(h.performance?p.performance.log(arguments):(p.verbose=Function.prototype.bind.call(console.info,console,h.name+":"),p.verbose.apply(console,arguments)))},error:function(){h.silent||(p.error=Function.prototype.bind.call(console.error,console,h.name+":"),p.error.apply(console,arguments))},performance:{log:function(e){var t,n,i;h.performance&&(t=(new Date).getTime(),i=l||t,n=t-i,l=t,c.push({Name:e[0],Arguments:[].slice.call(e,1)||"",Element:D,"Execution Time":n})),clearTimeout(p.performance.timer),p.performance.timer=setTimeout(p.performance.display,500)},display:function(){var t=h.name+":",n=0;l=!1,clearTimeout(p.performance.timer),e.each(c,function(e,t){n+=t["Execution Time"]}),t+=" "+n+"ms",s&&(t+=" '"+s+"'"),a.length>1&&(t+=" ("+a.length+")"),(console.group!==i||console.table!==i)&&c.length>0&&(console.groupCollapsed(t),console.table?console.table(c):e.each(c,function(e,t){console.log(t.Name+": "+t["Execution Time"]+"ms")}),console.groupEnd()),c=[]}},invoke:function(t,n,o){var a,s,l,c=O;return n=n||f,o=D||o,"string"==typeof t&&c!==i&&(t=t.split(/[\. ]/),a=t.length-1,e.each(t,function(n,o){var r=n!=a?o+t[n+1].charAt(0).toUpperCase()+t[n+1].slice(1):t;if(e.isPlainObject(c[r])&&n!=a)c=c[r];else{if(c[r]!==i)return s=c[r],!1;if(!e.isPlainObject(c[o])||n==a)return c[o]!==i&&(s=c[o],!1);c=c[o]}})),e.isFunction(s)?l=s.apply(o,n):s!==i&&(l=s),e.isArray(r)?r.push(l):r!==i?r=[r,l]:l!==i&&(r=l),s}},d?(O===i&&p.initialize(),p.invoke(u)):(O!==i&&O.invoke("destroy"),p.initialize())}),r!==i?r:this},e.fn.search.settings={name:"Search",namespace:"search",silent:!1,debug:!1,verbose:!1,performance:!0,type:"standard",minCharacters:1,selectFirstResult:!1,apiSettings:!1,source:!1,searchFields:["title","description"],displayField:"",searchFullText:!0,automatic:!0,hideDelay:0,searchDelay:200,maxResults:7,cache:!0,showNoResults:!0,transition:"scale",duration:200,easing:"easeOutExpo",onSelect:!1,onResultsAdd:!1,onSearchQuery:function(e){},onResults:function(e){},onResultsOpen:function(){},onResultsClose:function(){},className:{animating:"animating",active:"active",empty:"empty",focus:"focus",hidden:"hidden",loading:"loading",results:"results",pressed:"down"},error:{source:"Cannot search. No source used, and Semantic API module was not included",noResults:"Your search returned no results",logging:"Error in debug logging, exiting.",noEndpoint:"No search endpoint was specified",noTemplate:"A valid template name was not specified.",serverError:"There was an issue querying the server.",maxResults:"Results must be an array to use maxResults setting",method:"The method you called is not defined."},metadata:{cache:"cache",results:"results",result:"result"},regExp:{escape:/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,beginsWith:"(?:s|^)"},fields:{categories:"results",categoryName:"name",categoryResults:"results",description:"description",image:"image",price:"price",results:"results",title:"title",url:"url",action:"action",actionText:"text",actionURL:"url"},selector:{prompt:".prompt",searchButton:".search.button",results:".results",message:".results > .message",category:".category",result:".result",title:".title, .name"},templates:{escape:function(e){var t=/[&<>"'`]/g,n=/[&<>"'`]/,i={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},o=function(e){return i[e]};return n.test(e)?e.replace(t,o):e},message:function(e,t){var n="";return e!==i&&t!==i&&(n+=' "),n},category:function(t,n){var o="";return e.fn.search.settings.templates.escape,t[n.categoryResults]!==i&&(e.each(t[n.categoryResults],function(t,r){r[n.results]!==i&&r.results.length>0&&(o+='{{ .T "Rank" }} | +{{ .T "Clan" }} | +{{ .T "PP/Score" }} | +{{ .T "Playcount" }} | +
---|
#{{ .rank }} | +{{ .name }} | +{{ .chosen_mode.pp }}pp ({{ humanize .chosen_mode.total_score }}) | +{{ .chosen_mode.playcount }} | +
---|
+ | |
{{ $global.T "Global Rank" }} | +#{{ humanize .rank }} | +
{{ $global.T "PP" }} | +{{ humanize .chosen_mode.pp }} | +
{{ $global.T "Ranked Score" }} | +{{ humanize .chosen_mode.ranked_score }} | +
{{ $global.T "Total Score" }} | +{{ humanize .chosen_mode.total_score }} | +
{{ $global.T "Total Playcount" }} | +{{ humanize .chosen_mode.playcount }} | +
{{ $global.T "Total Replays Watched" }} | +{{ humanize .chosen_mode.replays_watched }} | +
{{ $global.T "Total Hits" }} | +{{ humanize .chosen_mode.total_hits }} | +
+ {{ .T "The leader of the clan." }}
+
+ {{ .T "The members of the clan." }}
+
+ {{ .T "If you need to get in touch with our support team (our Community Managers), you will need to either send an email to our support email address, or join our Discord Server. You can compose an email to it by clicking the button at the bottom of this page." | html }} +
++ {{ .T "When contacting the support mail, please make sure to send the email from the email address you signed up on Akatsuki with." | html }} +
+