ctf-server
old server for hosting capture-the-flag
git clone https://9o.is/git/ctf-server.git
MapUtil.scala
(6905B)
1 package com.jcabrra
2 package snippet
3
4 import model._
5 import net.liftweb._
6 import http._
7 import js._
8 import JsCmds._
9 import JE._
10 import util._
11 import util.Helpers._
12 import xml.NodeSeq
13 import net.liftmodules.extras._
14
15 /**
16 * TODO Future: Use MVVM and clean this up
17 */
18 object MapUtil {
19 val (long,lat) = (
20 Props.get("center.long") openOr "-73.989015",
21 Props.get("center.lat") openOr "40.752459")
22 val zoomLevel = Props.get("center.zoomlevel") openOr "15"
23 val container = "map"
24
25 def genMap = {
26 type GPS = (Double, Double)
27 val challenges: Map[Challenge, GPS] = Challenge.findAll.map(c => (c, (c.long.get, c.lat.get))).toMap
28 val capturedFlags = User.currentUser.map(_.capturedFlags.toList) openOr Nil
29
30 val flags: Map[Flag, GPS] = Flag.findAll.map(f => (f, {
31 if(capturedFlags.exists(_.id == f.id)) (f.long.get, f.lat.get)
32 else f.genNearbyGPS
33 })).toMap
34
35 val unlockedFlags =
36 capturedFlags.flatMap(_.unlocks).flatMap(_.flags) :::
37 flags.map(_._1).filter(_.prerequisite.isEmpty).toList
38
39 val winningFlags = flags.map(_._1).filter(_.winner.get)
40
41 S.appendJs(JsRaw(s"""
42 var po = org.polymaps;
43 var map = po.map().container(
44 document.getElementById("$container").appendChild(po.svg("svg")))
45 .center({
46 lat : $lat,
47 lon : $long
48 }).zoom($zoomLevel).add(po.drag());
49 map.add(po.image().url(
50 po.url(
51 "http://{S}tile.cloudmade.com"
52 + "/1a1b06b230af4efdbb989ea99e9841af"
53 + "/999/256/{Z}/{X}/{Y}.png").hosts(
54 [ "a.", "b.", "c.", "" ])));
55 map.add(po.geoJson().features([${
56 // Generate Paths
57 val paths = flags.map { x =>
58 val flag = x._1
59 val (longA, latA) = x._2
60 flag.prerequisite.map { preFlag =>
61
62 val (longB, latB) = flags.get(preFlag).getOrElse((0,0))
63 s"""{"geometry":{"coordinates":[[$longA,$latA],[$longB,$latB]],"type":"LineString"}}"""
64 } mkString ","
65 }.foldLeft("")((a,b) => if(a.isEmpty || b.isEmpty) a+b else a+","+b)
66
67 paths
68 }]).on("load", loadFlagPaths));
69
70 function loadFlagPaths(e) {
71 ${
72 val ids = flags.flatMap(_._1.prerequisite).map(_.id.get).toList
73 var ret = ""
74 for (i <- 0 until ids.size) {
75 val captured = User.currentUser.exists(_.capturedFlags.exists(_.id.get == ids(i)))
76 ret += s"""
77 e.features[$i].element.id = "map-path${ids(i)}";
78 e.features[$i].element.addClass("${if(captured) "captured" else ""}");
79 """
80 }
81 ret
82 }
83 }
84
85 map.add(po.geoJson().features([${
86 // Generate flag points
87 val points = flags.map { x =>
88 val (nearLong, nearLat) = x._2
89 s"""{"geometry":{"coordinates":[$nearLong,$nearLat],"type":"Point"}}"""
90 } mkString ","
91
92 points
93 }]).on("load", loadFlagPoints));
94
95 function loadFlagPoints(e) {
96 ${
97 val ids = flags.map(_._1.id.get).toList
98 var ret = ""
99 for (i <- 0 until flags.size) {
100 val captured = capturedFlags.exists(_.id.get == ids(i))
101 val unlocked = unlockedFlags.exists(_.id.get == ids(i))
102 val winner = winningFlags.exists(_.id.get == ids(i))
103
104 ret += s"""
105 e.features[$i].element.id = "map-flag${ids(i)}";
106 e.features[$i].element.addClass("${if(captured) "captured" else ""}");
107 e.features[$i].element.addClass("${if(!captured && unlocked) "unlocked" else ""}");
108 e.features[$i].element.addClass("${if(winner) "winner" else ""}");
109 e.features[$i].element.setAttribute("r", "${if(winner) "15" else "8"}");
110 """
111 }
112 ret
113 }
114 }
115
116 map.add(po.geoJson().features([${
117 // Generate challenge points
118 val points = challenges.map { x =>
119 val (nearLong, nearLat) = x._2
120 s"""{"geometry":{"coordinates":[$nearLong,$nearLat],"type":"Point"}}"""
121 } mkString ","
122
123 points
124 }]).on("load", loadChallengePoints));
125
126 function loadChallengePoints(e) {
127 ${
128 val ids = challenges.map(_._1.id.get).toList
129 val radius = challenges.map(_._1.radius.get).toList
130 var ret = ""
131 for (i <- 0 until challenges.size) {
132 ret += s"""
133 e.features[$i].element.id = "map-challenge${ids(i)}";
134 e.features[$i].element.setAttribute("r", "${radius(i)}");
135 """
136 }
137 ret
138 }
139 }
140
141 ${
142 // Generate Popovers
143 challenges.map {
144 c =>
145 val challenge = c._1
146
147 def capturedBy: NodeSeq = challenge.flags.flatMap(_.capturedBy).map {
148 user => <img width='25' height='25' src={user.image.get}></img>
149 }
150
151 "$('#map-challenge"+challenge.id+"').popover({" +
152 s"""
153 "title" : "${challenge.name}",
154 "content" : "${
155
156 def escDoubleQuote(str: String): String = {
157 str.toList.map(_.toString).map(ch =>
158 if (ch == "\"") "\\\"" else ch).mkString
159 }
160
161 val cont = User.currentUser.map {
162 user =>
163
164 def process(guess: String): JsCmd = {
165 Challenges.processResponse(guess, user, challenge)
166 SetValById(challenge.name+"-input", "")
167 }
168
169 def capturedFlags: NodeSeq = challenge.capturedFlags(user).map {
170 flag => <p class='text-success'>{flag.answer}</p>
171 }
172
173 def input: NodeSeq =
174 if(challenge.fullySolvedBy(user)) NodeSeq.Empty
175 else SHtml.ajaxText("", false, process _,
176 "id" -> (challenge.name+"-input"), "placeholder" -> "guess")
177
178 if(challenge.meetsPrerequisite(user)) {
179 capturedFlags ++ <p>{challenge.hint}</p> ++ input ++ <p>{capturedBy}</p>
180 } else {
181 <p class='text-danger'>LOCKED!</p> ++ capturedBy
182 }
183 }.openOr(<p><a href='/login'>Join The Fun!</a></p> ++ capturedBy)
184 escDoubleQuote(cont.toString)
185 }",
186 "container" : "#$container",
187 "placement" : "bottom",
188 "html" : true
189 });
190
191 window.onload = function() {
192 var unlockedOpacity;
193 setInterval(function() {
194 unlockedOpacity = unlockedOpacity === '1' ? '0.5' : '1';
195 jQuery(".unlocked").css("fill-opacity", unlockedOpacity);
196 po.geoJson().reload;
197 }, 500);
198 };
199
200 map.on("move", function() {
201 jQuery(".popover").hide();
202 });
203 """
204 }.mkString
205 }
206 """).cmd)
207 "*" #> NodeSeq.Empty
208 }
209 }