3 |
CTFPad REST API
4 |
5 |
Each request has to contain an apikey generated by CTFPad as HTTP header
6 | field X-Apikey
7 |
8 |
User endpoints
9 |
10 |
GET user/whoami
11 |
12 |
Returns the username to the provided apikey
13 |
14 |
Response body:
15 |
16 |
{
17 | "username": "username"
18 | }
19 |
20 |
21 |
GET user/scope
22 |
23 |
Returns the scope of the current user
24 |
25 |
Response body:
26 |
27 |
{
28 | "scope": 1
29 | }
30 |
31 |
32 |
PUT user/scope
33 |
34 |
Sets the scope of the current user
35 |
36 |
Request body:
37 |
38 |
{
39 | "scope": 2
40 | }
41 |
42 |
43 |
Response body:
44 |
45 |
{
46 | "scope": 2
47 | }
48 |
49 |
50 |
PUT user/scope/latest
51 |
52 |
Sets the scope of the current user to the newest CTF
53 |
54 |
Response body:
55 |
56 |
{
57 | "scope": 4
58 | }
59 |
60 |
61 |
CTF endpoints
62 |
63 |
GET ctfs
64 |
65 |
Lists all available CTFs
66 |
67 |
Response body:
68 |
69 |
{
70 | "ctfs": [ {
71 | "id": 1,
72 | "name": "some ctf"
73 | }, {
74 | "id": 2,
75 | "name": "anyCTF"
76 | } ]
77 | }
78 |
79 |
80 |
POST ctfs
81 |
82 |
Creates a new CTF with the given name
83 |
84 |
Request body:
85 |
86 |
{
87 | "name": "new CTF"
88 | }
89 |
90 |
91 |
Response body:
92 |
93 |
{
94 | "ctf": {
95 | "id": 3,
96 | "name": "new CTF"
97 | }
98 | }
99 |
100 |
101 |
GET ctfs/<$CTF>
102 |
103 |
Returns details about a specific CTF by id
104 |
105 |
Response body:
106 |
107 |
{
108 | "ctf": {
109 | "id": 2,
110 | "name": "anyCTF"
111 | }
112 | }
113 |
114 |
115 |
GET ctfs/<$CTF>/challenges
116 |
117 |
Lists challenges for a specific CTF
118 |
119 |
Response body:
120 |
121 |
{
122 | "challenges": [ {
123 | "id": 1,
124 | "title": "random web",
125 | "category": "web",
126 | "points": 100,
127 | "done": false
128 | }, {
129 | "id": 2,
130 | "title": "crypto awesome"
131 | "category": "crypto",
132 | "points": 250,
133 | "done": true
134 | } ]
135 | }
136 |
137 |
138 |
POST ctfs/<$CTF>/challenges
139 |
140 |
Creates a new challenge for a CTF
141 |
142 |
Request body:
143 |
144 |
{
145 | "challenge": {
146 | "title": "new chal",
147 | "category": "reversing",
148 | "points": 300
149 | }
150 | }
151 |
152 |
153 |
Response body:
154 |
155 |
{
156 | "challenge": {
157 | "id": 3,
158 | "title": "new chal",
159 | "category": "reversing",
160 | "points": 300,
161 | "done": false
162 | }
163 | }
164 |
165 |
166 |
GET ctfs/<$CTF>/files
167 |
168 |
Lists all available files for a ctf
169 |
170 |
Response body:
171 |
172 |
{
173 | "files": [ {
174 | "id": "4fb15f2bac64cccd6470753b9333534f2065ed14aca439043399a267cba7c6fb",
175 | "name": "test.py",
176 | "user": "stratumauhuur",
177 | "path": "/files/4fb15f2bac64cccd6470753b9333534f2065ed14aca439043399a267cba7c6fb/test.py"
178 | } ]
179 | }
180 |
181 |
182 |
POST ctfs/<$CTF>/files
183 |
184 |
Upload a file for a ctf
185 |
186 |
Request body:
187 |
188 |
{
189 | "files": @file
190 | }
191 |
192 |
193 |
Response body:
194 |
195 |
{
196 | "success": true,
197 | "id": "4a1da276d9cd0d245d6d186dc28148e2bc8c10b8aa19bdfeaf2e5d9dcc0ecd22"
198 | }
199 |
200 |
201 |
Challenge endpoints
202 |
203 |
GET challenges/<$CHALLENGE>
204 |
205 |
Returns details about a specific challenge
206 |
207 |
Response body:
208 |
209 |
{
210 | "challenge": {
211 | "id": 3,
212 | "title": "new chal",
213 | "category": "reversing",
214 | "points": 300,
215 | "done": false,
216 | "ctf": 1
217 | "filecount": 2,
218 | "assigned": [
219 | "stratumauhuur"
220 | ]
221 | }
222 | }
223 |
224 |
225 |
PUT challenges/<$CHALLENGE>/assign
226 |
227 |
Assigns the current user to the specified challenge
228 |
229 |
Response body:
230 |
231 |
{
232 | "assigned": [
233 | "stratumauhuur",
234 | "username"
235 | ]
236 | }
237 |
238 |
239 |
DELETE challenges/<$CHALLENGE>/assign
240 |
241 |
Unassigns the current user from the specified challenge
242 |
243 |
Response body:
244 |
245 |
{
246 | "assgined": [
247 | "stratumauhuur"
248 | ]
249 | }
250 |
251 |
252 |
PUT challenges/<$CHALLENGE>/done
253 |
254 |
Marks the challenge as done
255 |
256 |
DELETE challenges/<$CHALLENGE>/done
257 |
258 |
Marks the challenge as not done
259 |
260 |
GET challenges/<$CHALLENGE>/files
261 |
262 |
Lists the files for a specific challenge
263 |
264 |
Response body:
265 |
266 |
{
267 | "files": [ {
268 | "id": "4fb15f2bac64cccd6470753b9333534f2065ed14aca439043399a267cba7c6fb",
269 | "name": "test.py",
270 | "user": "stratumauhuur",
271 | "path": "/files/4fb15f2bac64cccd6470753b9333534f2065ed14aca439043399a267cba7c6fb/test.py"
272 | } ]
273 | }
274 |
275 |
276 |
POST challenges/<$CHALLENGE>/files
277 |
278 |
Upload a file for a challenge
279 |
280 |
Request body:
281 |
282 |
{
283 | "files": @file
284 | }
285 |
286 |
287 |
Response body:
288 |
289 |
{
290 | "success": true,
291 | "id": "4a1da276d9cd0d245d6d186dc28148e2bc8c10b8aa19bdfeaf2e5d9dcc0ecd22"
292 | }
293 |
294 |
295 |
--------------------------------------------------------------------------------
/database.coffee:
--------------------------------------------------------------------------------
1 | #database.coffee
2 | bcrypt = require 'bcrypt-nodejs'
3 | sqlite3 = require 'sqlite3'
4 | fs = require 'fs'
5 |
6 | # SQLITE DB
7 | stmts = {}
8 | sql = new sqlite3.Database 'ctfpad.sqlite', ->
9 | stmts.getUser = sql.prepare 'SELECT name,scope,apikey FROM user WHERE sessid = ?'
10 | stmts.getUserByApiKey = sql.prepare 'SELECT name,scope FROM user WHERE apikey = ? AND apikey NOT NULL'
11 | stmts.addUser = sql.prepare 'INSERT INTO user (name,pwhash) VALUES (?,?)'
12 | stmts.getUserPW = sql.prepare 'SELECT pwhash FROM user WHERE name = ?'
13 | stmts.insertSession = sql.prepare 'UPDATE user SET sessid = ? WHERE name = ?'
14 | stmts.voidSession = sql.prepare 'UPDATE user SET sessid = NULL WHERE sessid = ?'
15 | stmts.getChallenges = sql.prepare 'SELECT id,title,category,points,done FROM challenge WHERE ctf = ? ORDER BY category,points,id'
16 | stmts.getChallenge = sql.prepare 'SELECT * FROM challenge WHERE id = ?'
17 | stmts.addChallenge = sql.prepare 'INSERT INTO challenge (ctf, title, category, points) VALUES (?,?,?,?)'
18 | stmts.modifyChallenge = sql.prepare 'UPDATE challenge SET title = ?, category = ?, points = ? WHERE id = ?'
19 | stmts.setDone = sql.prepare 'UPDATE challenge SET done = ? WHERE id = ?'
20 | stmts.getCTFs = sql.prepare 'SELECT id,name FROM ctf ORDER BY id DESC'
21 | stmts.addCTF = sql.prepare 'INSERT INTO ctf (name) VALUES (?)'
22 | stmts.changeScope = sql.prepare 'UPDATE user SET scope = ? WHERE name = ?'
23 | stmts.isAssigned = sql.prepare 'SELECT COUNT(*) AS assigned FROM assigned WHERE user = ? AND challenge = ?'
24 | stmts.assign = sql.prepare 'INSERT INTO assigned VALUES (?,?)'
25 | stmts.unassign = sql.prepare 'DELETE FROM assigned WHERE user = ? AND challenge = ?'
26 | stmts.changePassword = sql.prepare 'UPDATE user SET pwhash = ? WHERE sessid = ?'
27 | stmts.getApiKeyFor = sql.prepare 'SELECT apikey FROM user WHERE sessid = ?'
28 | stmts.setApiKeyFor = sql.prepare 'UPDATE user SET apikey = ? WHERE sessid = ?'
29 | stmts.listAssignments = sql.prepare 'SELECT assigned.challenge,assigned.user FROM assigned JOIN challenge ON assigned.challenge = challenge.id JOIN user ON assigned.user = user.name WHERE challenge.ctf = ?'
30 | stmts.listAssignmentsForChallenge = sql.prepare 'SELECT user FROM assigned WHERE challenge = ?'
31 | stmts.getFiles = sql.prepare 'SELECT id,name,user,uploaded,mimetype FROM file WHERE CASE ? WHEN 1 THEN ctf WHEN 2 THEN challenge END = ?'
32 | stmts.addFile = sql.prepare 'INSERT INTO file (id, name, user, ctf, challenge, uploaded, mimetype) VALUES (?,?,?,?,?,?,?)'
33 | stmts.findFile = sql.prepare 'SELECT ctf,challenge FROM file WHERE id = ?'
34 | stmts.fileMimetype = sql.prepare 'SELECT mimetype FROM file WHERE id = ?'
35 | stmts.deleteFile = sql.prepare 'DELETE FROM file WHERE id = ?'
36 | stmts.getLatestCtfId = sql.prepare 'SELECT id FROM ctf ORDER BY id DESC LIMIT 1'
37 |
38 | #
39 | # EXPORTS
40 | #
41 | exports.validateSession = (sess, cb = ->) ->
42 | stmts.getUser.get [sess], H cb
43 |
44 | exports.checkPassword = (name, pw, cb = ->) ->
45 | stmts.getUserPW.get [name], H (row) ->
46 | unless row then cb false
47 | else bcrypt.compare pw, row.pwhash, (err, res) ->
48 | if err or not res then cb false
49 | else
50 | sess = newRandomId()
51 | cb sess
52 | stmts.insertSession.run [sess, name]
53 |
54 | exports.validateApiKey = (apikey, cb) ->
55 | stmts.getUserByApiKey.get [apikey], H cb
56 | stmts.getUserByApiKey.reset()
57 |
58 | exports.voidSession = (sessionId) -> stmts.voidSession.run [sessionId]
59 |
60 | exports.setChallengeDone = (chalId, done) ->
61 | stmts.setDone.run [(if done then 1 else 0), chalId]
62 |
63 | exports.getChallenges = (ctfId, cb = ->) ->
64 | stmts.getChallenges.all [ctfId], H cb
65 |
66 | exports.getChallenge = (challengeId, cb = ->) ->
67 | stmts.getChallenge.get [challengeId], H cb
68 | stmts.getChallenge.reset()
69 |
70 | exports.addChallenge = (ctfId, title, category, points, cb = ->) ->
71 | stmts.addChallenge.run [ctfId, title, category, points], (err) ->
72 | cb(this.lastID)
73 |
74 | exports.modifyChallenge = (chalId, title, category, points) ->
75 | stmts.modifyChallenge.run [title, category, points, chalId]
76 |
77 | exports.getCTFs = (cb = ->) ->
78 | stmts.getCTFs.all [], H cb
79 |
80 | exports.addCTF = (title, cb = ->) ->
81 | stmts.addCTF.run [title], (err) ->
82 | cb(this.lastID)
83 |
84 | exports.changeScope = (user, ctfid) ->
85 | stmts.changeScope.run [ctfid, user]
86 |
87 | exports.toggleAssign = (user, chalid, cb = ->) ->
88 | stmts.isAssigned.get [user, chalid], H (ans) ->
89 | if ans.assigned
90 | exports.unassign user, chalid
91 | cb false
92 | else
93 | exports.assign user, chalid
94 | cb true
95 | stmts.isAssigned.reset()
96 |
97 | exports.assign = (user, chalid, cb = ->) ->
98 | stmts.assign.run [user,chalid], cb
99 |
100 | exports.unassign = (user, chalid, cb = ->) ->
101 | stmts.unassign.run [user,chalid], cb
102 |
103 | exports.listAssignments = (ctfid, cb = ->) ->
104 | stmts.listAssignments.all [ctfid], H cb
105 | stmts.listAssignments.reset()
106 |
107 | exports.listAssignmentsForChallenge = (chalId, cb = ->) ->
108 | stmts.listAssignmentsForChallenge.all [chalId], H cb
109 | stmts.listAssignmentsForChallenge.reset()
110 |
111 | exports.changePassword = (sessid, newpw, cb = ->) ->
112 | bcrypt.hash newpw, bcrypt.genSaltSync(), null, (err, hash) ->
113 | if err then cb err
114 | else
115 | stmts.changePassword.run [hash, sessid]
116 | cb false
117 |
118 | exports.getApiKeyFor = (sessid, cb = ->) ->
119 | stmts.getApiKeyFor.get [sessid], H (row) ->
120 | cb if row then row.apikey else ''
121 | stmts.getApiKeyFor.reset()
122 |
123 | exports.newApiKeyFor = (sessid, cb = ->) ->
124 | apikey = newRandomId 32
125 | stmts.setApiKeyFor.run [apikey, sessid]
126 | setImmediate cb, apikey
127 |
128 | exports.addUser = (name, pw, cb = ->) ->
129 | bcrypt.hash pw, bcrypt.genSaltSync(), null, (err, hash) ->
130 | if err then cb err
131 | else
132 | stmts.addUser.run [name, hash], (err, ans) ->
133 | if err
134 | cb err
135 | else
136 | cb false
137 |
138 | exports.getCTFFiles = (id, cb = ->) ->
139 | stmts.getFiles.all [1, id], H cb
140 | stmts.getFiles.reset()
141 |
142 | exports.getChallengeFiles = (id, cb = ->) ->
143 | stmts.getFiles.all [2, id], H cb
144 | stmts.getFiles.reset()
145 |
146 | exports.addChallengeFile = (chal, name, user, mimetype, cb = ->) ->
147 | id = newRandomId(32)
148 | stmts.addFile.run [id, name, user, null, chal, new Date().getTime()/1000, mimetype], (err, ans) ->
149 | cb err, id
150 |
151 | exports.addCTFFile = (ctf, name, user, mimetype, cb = ->) ->
152 | id = newRandomId(32)
153 | stmts.addFile.run [id, name, user, ctf, null, new Date().getTime()/1000, mimetype], (err, ans) ->
154 | cb err, id
155 |
156 | exports.mimetypeForFile = (id, cb = ->) ->
157 | stmts.fileMimetype.get [id], H ({mimetype: mimetype}) -> cb(mimetype)
158 |
159 | exports.deleteFile = (fileid, cb = ->) ->
160 | stmts.findFile.get [fileid], H ({ctf:ctf, challenge:challenge}) ->
161 | stmts.deleteFile.run [fileid], (err) ->
162 | cb err, (if ctf then 0 else 1), (if ctf then ctf else challenge)
163 |
164 | exports.getLatestCtfId = (cb = ->) ->
165 | stmts.getLatestCtfId.get H (row) ->
166 | stmts.getLatestCtfId.reset ->
167 | cb(if row isnt undefined then row.id else -1)
168 |
169 | #
170 | # UTIL
171 | #
172 | H = (cb=->) ->
173 | return (err, ans) ->
174 | if err then console.log err
175 | else cb ans
176 |
177 | newRandomId = (length = 16) ->
178 | buf = new Buffer length
179 | fd = fs.openSync '/dev/urandom', 'r'
180 | fs.readSync fd, buf, 0, length, null
181 | buf.toString 'hex'
182 |
183 |
--------------------------------------------------------------------------------
/api.coffee:
--------------------------------------------------------------------------------
1 | exports.init = (app, db, upload, prefix) ->
2 | # UTIL
3 | validateApiKey = (req, res, cb = ->) ->
4 | db.validateApiKey req.header('X-Apikey'), (user) ->
5 | if user
6 | cb user
7 | else
8 | res.send 401
9 |
10 | recursiveTypeCheck = (proto, obj) ->
11 | unless obj
12 | return false
13 | for k,v of proto
14 | if typeof(v) is 'string'
15 | unless typeof(obj[k]) is v
16 | return false
17 | else if typeof(v) is 'object'
18 | unless recursiveTypeCheck(v, obj[k])
19 | return false
20 | return true
21 |
22 | validateArguments = (req, res, args) ->
23 | if recursiveTypeCheck args, req.body
24 | return true
25 | else
26 | res.send 400
27 | return false
28 |
29 | # USER Endpoints
30 | app.get "#{prefix}/user/whoami", (req, res) ->
31 | validateApiKey req, res, (user) ->
32 | res.json {username: user.name}
33 |
34 | app.get "#{prefix}/user/scope", (req, res) ->
35 | validateApiKey req, res, (user) ->
36 | res.json {scope: user.scope}
37 |
38 | app.put "#{prefix}/user/scope", (req, res) ->
39 | validateApiKey req, res, (user) ->
40 | if validateArguments req, res, {scope: 'number'}
41 | db.changeScope user.name, req.body.scope
42 | res.json {scope: req.body.scope}
43 | #TODO change ws.authenticated.scope
44 |
45 | app.put "#{prefix}/user/scope/latest", (req, res) ->
46 | validateApiKey req, res, (user) ->
47 | db.getLatestCtfId (id) ->
48 | db.changeScope user.name, id
49 | res.json {scope: id}
50 | #TODO change ws.authenticated.scope
51 |
52 | # CTF Endpoints
53 |
54 | app.get "#{prefix}/ctfs", (req, res) ->
55 | validateApiKey req, res, (user) ->
56 | db.getCTFs (ctfs) ->
57 | res.json {ctfs: ctfs}
58 |
59 | app.post "#{prefix}/ctfs", (req, res) ->
60 | validateApiKey req, res, (user) ->
61 | if validateArguments req, res, {name: 'string'}
62 | db.addCTF req.body.name, (id) ->
63 | res.json {ctf: {id: id, name: req.body.name}}
64 |
65 | app.get "#{prefix}/ctfs/:ctf", (req, res) ->
66 | try
67 | req.params.ctf = parseInt req.params.ctf
68 | catch e
69 | res.send 400
70 | return
71 | validateApiKey req, res, (user) ->
72 | db.getCTFs (ctfs) ->
73 | candidates = (ctf for ctf in ctfs when ctf.id is req.params.ctf)
74 | if candidates.length > 0
75 | res.json {ctf: candidates[0]}
76 | else
77 | res.send 404
78 |
79 | app.get "#{prefix}/ctfs/:ctf/challenges", (req, res) ->
80 | try
81 | req.params.ctf = parseInt req.params.ctf
82 | catch e
83 | res.send 400
84 | return
85 | validateApiKey req, res, (user) ->
86 | db.getChallenges req.params.ctf, (challenges) ->
87 | challenge.done = new Boolean(challenge.done) for challenge in challenges
88 | res.json {challenges: challenges}
89 |
90 | app.post "#{prefix}/ctfs/:ctf/challenges", (req, res) ->
91 | try
92 | req.params.ctf = parseInt req.params.ctf
93 | catch e
94 | res.send 400
95 | return
96 | validateApiKey req, res, (user) ->
97 | validArg = {challenge: {title: 'string', category: 'string', points: 'number'}}
98 | if validateArguments req, res, validArg
99 | db.addChallenge req.params.ctf, req.body.challenge.title,
100 | req.body.challenge.category, req.body.challenge.points, (id) ->
101 | res.json {challenge:
102 | id: id
103 | title: req.body.challenge.title
104 | category: req.body.challenge.category
105 | points: req.body.challenge.points
106 | done: false
107 | }
108 | exports.broadcast {type: 'ctfmodification'}, req.params.ctf
109 |
110 | app.get "#{prefix}/ctfs/:ctf/files", (req, res) ->
111 | try
112 | req.params.ctf = parseInt req.params.ctf
113 | catch e
114 | res.send 400
115 | return
116 | validateApiKey req, res, (user) ->
117 | db.getCTFFiles req.params.ctf, (files) ->
118 | file.path = "/file/#{file.id}/#{file.name}" for file in files
119 | res.json {files: files}
120 |
121 | app.post "#{prefix}/ctfs/:ctf/files", (req, res) ->
122 | validateApiKey req, res, (user) ->
123 | upload user, 'ctf', req.params.ctf, req, res
124 |
125 | #CHALLENGE Endpoints
126 | app.get "#{prefix}/challenges/:challenge", (req, res) ->
127 | try
128 | req.params.challenge = parseInt req.params.challenge
129 | catch e
130 | res.send 400
131 | return
132 | validateApiKey req, res, (user) ->
133 | db.getChallenge req.params.challenge, (challenge) ->
134 | unless challenge
135 | res.send 404
136 | return
137 | challenge.done = new Boolean challenge.done
138 | done = ->
139 | if challenge.filecount isnt undefined and challenge.assigned isnt undefined
140 | res.json {challenge: challenge}
141 | db.getChallengeFiles challenge.id, (files) ->
142 | challenge.filecount = files.length
143 | done()
144 | db.listAssignmentsForChallenge challenge.id, (assignments) ->
145 | challenge.assigned = (a.user for a in assignments)
146 | done()
147 |
148 | app.put "#{prefix}/challenges/:challenge/assign", (req, res) ->
149 | try
150 | req.params.challenge = parseInt req.params.challenge
151 | catch e
152 | res.send 400
153 | return
154 | validateApiKey req, res, (user) ->
155 | db.assign user.name, req.params.challenge, ->
156 | db.listAssignmentsForChallenge req.params.challenge, (assignments) ->
157 | res.json {assigned: (a.user for a in assignments)}
158 | exports.broadcast {type: 'assign', subject: req.params.challenge, data: [{name: user.name}, true]}
159 |
160 | app.delete "#{prefix}/challenges/:challenge/assign", (req, res) ->
161 | try
162 | req.params.challenge = parseInt req.params.challenge
163 | catch e
164 | res.send 400
165 | return
166 | validateApiKey req, res, (user) ->
167 | db.unassign user.name, req.params.challenge, ->
168 | db.listAssignmentsForChallenge req.params.challenge, (assignments) ->
169 | res.json {assigned: (a.user for a in assignments)}
170 | exports.broadcast {type: 'assign', subject: req.params.challenge, data: [{name: user.name}, false]}
171 |
172 | app.put "#{prefix}/challenges/:challenge/done", (req, res) ->
173 | try
174 | req.params.challenge = parseInt req.params.challenge
175 | catch e
176 | res.send 400
177 | return
178 | validateApiKey req, res, (user) ->
179 | db.setChallengeDone req.params.challenge, true
180 | exports.broadcast {type: 'done', subject: req.params.challenge, data: true}
181 | res.send 204
182 |
183 | app.delete "#{prefix}/challenges/:challenge/done", (req, res) ->
184 | try
185 | req.params.challenge = parseInt req.params.challenge
186 | catch e
187 | res.send 400
188 | return
189 | validateApiKey req, res, (user) ->
190 | db.setChallengeDone req.params.challenge, false
191 | exports.broadcast {type: 'done', subject: req.params.challenge, data: false}
192 | res.send 204
193 |
194 | app.get "#{prefix}/challenges/:challenge/files", (req, res) ->
195 | try
196 | req.params.challenge = parseInt req.params.challenge
197 | catch e
198 | res.send 400
199 | return
200 | validateApiKey req, res, (user) ->
201 | db.getChallengeFiles req.params.challenge, (files) ->
202 | file.path = "/file/#{file.id}/#{file.name}" for file in files
203 | res.json {files: files}
204 |
205 | app.post "#{prefix}/challenges/:challenge/files", (req, res) ->
206 | validateApiKey req, res, (user) ->
207 | upload user, 'challenge', req.params.challenge, req, res
208 |
209 |
210 |
211 |
212 |
--------------------------------------------------------------------------------
/web/js/jquery.timeago.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Timeago is a jQuery plugin that makes it easy to support automatically
3 | * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
4 | *
5 | * @name timeago
6 | * @version 1.4.1
7 | * @requires jQuery v1.2.3+
8 | * @author Ryan McGeary
9 | * @license MIT License - http://www.opensource.org/licenses/mit-license.php
10 | *
11 | * For usage and examples, visit:
12 | * http://timeago.yarp.com/
13 | *
14 | * Copyright (c) 2008-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
15 | */
16 |
17 | (function (factory) {
18 | if (typeof define === 'function' && define.amd) {
19 | // AMD. Register as an anonymous module.
20 | define(['jquery'], factory);
21 | } else {
22 | // Browser globals
23 | factory(jQuery);
24 | }
25 | }(function ($) {
26 | $.timeago = function(timestamp) {
27 | if (timestamp instanceof Date) {
28 | return inWords(timestamp);
29 | } else if (typeof timestamp === "string") {
30 | return inWords($.timeago.parse(timestamp));
31 | } else if (typeof timestamp === "number") {
32 | return inWords(new Date(timestamp));
33 | } else {
34 | return inWords($.timeago.datetime(timestamp));
35 | }
36 | };
37 | var $t = $.timeago;
38 |
39 | $.extend($.timeago, {
40 | settings: {
41 | refreshMillis: 60000,
42 | allowPast: true,
43 | allowFuture: false,
44 | localeTitle: false,
45 | cutoff: 0,
46 | strings: {
47 | prefixAgo: null,
48 | prefixFromNow: null,
49 | suffixAgo: "ago",
50 | suffixFromNow: "from now",
51 | inPast: 'any moment now',
52 | seconds: "less than a minute",
53 | minute: "about a minute",
54 | minutes: "%d minutes",
55 | hour: "about an hour",
56 | hours: "about %d hours",
57 | day: "a day",
58 | days: "%d days",
59 | month: "about a month",
60 | months: "%d months",
61 | year: "about a year",
62 | years: "%d years",
63 | wordSeparator: " ",
64 | numbers: []
65 | }
66 | },
67 |
68 | inWords: function(distanceMillis) {
69 | if(!this.settings.allowPast && ! this.settings.allowFuture) {
70 | throw 'timeago allowPast and allowFuture settings can not both be set to false.';
71 | }
72 |
73 | var $l = this.settings.strings;
74 | var prefix = $l.prefixAgo;
75 | var suffix = $l.suffixAgo;
76 | if (this.settings.allowFuture) {
77 | if (distanceMillis < 0) {
78 | prefix = $l.prefixFromNow;
79 | suffix = $l.suffixFromNow;
80 | }
81 | }
82 |
83 | if(!this.settings.allowPast && distanceMillis >= 0) {
84 | return this.settings.strings.inPast;
85 | }
86 |
87 | var seconds = Math.abs(distanceMillis) / 1000;
88 | var minutes = seconds / 60;
89 | var hours = minutes / 60;
90 | var days = hours / 24;
91 | var years = days / 365;
92 |
93 | function substitute(stringOrFunction, number) {
94 | var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
95 | var value = ($l.numbers && $l.numbers[number]) || number;
96 | return string.replace(/%d/i, value);
97 | }
98 |
99 | var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
100 | seconds < 90 && substitute($l.minute, 1) ||
101 | minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
102 | minutes < 90 && substitute($l.hour, 1) ||
103 | hours < 24 && substitute($l.hours, Math.round(hours)) ||
104 | hours < 42 && substitute($l.day, 1) ||
105 | days < 30 && substitute($l.days, Math.round(days)) ||
106 | days < 45 && substitute($l.month, 1) ||
107 | days < 365 && substitute($l.months, Math.round(days / 30)) ||
108 | years < 1.5 && substitute($l.year, 1) ||
109 | substitute($l.years, Math.round(years));
110 |
111 | var separator = $l.wordSeparator || "";
112 | if ($l.wordSeparator === undefined) { separator = " "; }
113 | return $.trim([prefix, words, suffix].join(separator));
114 | },
115 |
116 | parse: function(iso8601) {
117 | var s = $.trim(iso8601);
118 | s = s.replace(/\.\d+/,""); // remove milliseconds
119 | s = s.replace(/-/,"/").replace(/-/,"/");
120 | s = s.replace(/T/," ").replace(/Z/," UTC");
121 | s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
122 | s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
123 | return new Date(s);
124 | },
125 | datetime: function(elem) {
126 | var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
127 | return $t.parse(iso8601);
128 | },
129 | isTime: function(elem) {
130 | // jQuery's `is()` doesn't play well with HTML5 in IE
131 | return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
132 | }
133 | });
134 |
135 | // functions that can be called via $(el).timeago('action')
136 | // init is default when no action is given
137 | // functions are called with context of a single element
138 | var functions = {
139 | init: function(){
140 | var refresh_el = $.proxy(refresh, this);
141 | refresh_el();
142 | var $s = $t.settings;
143 | if ($s.refreshMillis > 0) {
144 | this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
145 | }
146 | },
147 | update: function(time){
148 | var parsedTime = $t.parse(time);
149 | $(this).data('timeago', { datetime: parsedTime });
150 | if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleString());
151 | refresh.apply(this);
152 | },
153 | updateFromDOM: function(){
154 | $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
155 | refresh.apply(this);
156 | },
157 | dispose: function () {
158 | if (this._timeagoInterval) {
159 | window.clearInterval(this._timeagoInterval);
160 | this._timeagoInterval = null;
161 | }
162 | }
163 | };
164 |
165 | $.fn.timeago = function(action, options) {
166 | var fn = action ? functions[action] : functions.init;
167 | if(!fn){
168 | throw new Error("Unknown function name '"+ action +"' for timeago");
169 | }
170 | // each over objects here and call the requested function
171 | this.each(function(){
172 | fn.call(this, options);
173 | });
174 | return this;
175 | };
176 |
177 | function refresh() {
178 | //check if it's still visible
179 | if(!$.contains(document.documentElement,this)){
180 | //stop if it has been removed
181 | $(this).timeago("dispose");
182 | return this;
183 | }
184 |
185 | var data = prepareData(this);
186 | var $s = $t.settings;
187 |
188 | if (!isNaN(data.datetime)) {
189 | if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
190 | $(this).text(inWords(data.datetime));
191 | }
192 | }
193 | return this;
194 | }
195 |
196 | function prepareData(element) {
197 | element = $(element);
198 | if (!element.data("timeago")) {
199 | element.data("timeago", { datetime: $t.datetime(element) });
200 | var text = $.trim(element.text());
201 | if ($t.settings.localeTitle) {
202 | element.attr("title", element.data('timeago').datetime.toLocaleString());
203 | } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
204 | element.attr("title", text);
205 | }
206 | }
207 | return element.data("timeago");
208 | }
209 |
210 | function inWords(date) {
211 | return $t.inWords(distance(date));
212 | }
213 |
214 | function distance(date) {
215 | return (new Date().getTime() - date.getTime());
216 | }
217 |
218 | // fix for IE6 suckage
219 | document.createElement("abbr");
220 | document.createElement("time");
221 | }));
222 |
--------------------------------------------------------------------------------
/web/js/ctfpad.coffee:
--------------------------------------------------------------------------------
1 | $ ->
2 | proto = if location.protocol is 'http:' then 'ws' else 'wss'
3 | sock = new WebSocket "#{proto}#{location.href.substring location.protocol.length-1, location.href.lastIndexOf '/'}"
4 | sock.onopen = ->
5 | sock.send "\"#{sessid}\""
6 | sock.onclose = ->
7 | unless window.preventSocketAlert
8 | alert 'the websocket has been disconnected, reloading the page'
9 | document.location.reload()
10 | sock.onmessage = (event) ->
11 | msg = JSON.parse event.data
12 | console.log msg
13 | if msg.type is 'done'
14 | self = $("input[data-chalid='#{msg.subject}']")
15 | self.prop 'checked', msg.data
16 | self.parent().next().css 'text-decoration', if msg.data then 'line-through' else 'none'
17 | if msg.data
18 | self.parent().parent().addClass 'done'
19 | else
20 | self.parent().parent().removeClass 'done'
21 | updateProgress()
22 | else if msg.type is 'assign'
23 | self = $(".labels[data-chalid='#{msg.subject}']")
24 | if msg.data[1]
25 | self.append $("",
227 | options: {
228 | disabled: false,
229 |
230 | // callbacks
231 | create: null
232 | },
233 | _createWidget: function( options, element ) {
234 | element = $( element || this.defaultElement || this )[ 0 ];
235 | this.element = $( element );
236 | this.uuid = uuid++;
237 | this.eventNamespace = "." + this.widgetName + this.uuid;
238 | this.options = $.widget.extend( {},
239 | this.options,
240 | this._getCreateOptions(),
241 | options );
242 |
243 | this.bindings = $();
244 | this.hoverable = $();
245 | this.focusable = $();
246 |
247 | if ( element !== this ) {
248 | $.data( element, this.widgetFullName, this );
249 | this._on( true, this.element, {
250 | remove: function( event ) {
251 | if ( event.target === element ) {
252 | this.destroy();
253 | }
254 | }
255 | });
256 | this.document = $( element.style ?
257 | // element within the document
258 | element.ownerDocument :
259 | // element is window or document
260 | element.document || element );
261 | this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
262 | }
263 |
264 | this._create();
265 | this._trigger( "create", null, this._getCreateEventData() );
266 | this._init();
267 | },
268 | _getCreateOptions: $.noop,
269 | _getCreateEventData: $.noop,
270 | _create: $.noop,
271 | _init: $.noop,
272 |
273 | destroy: function() {
274 | this._destroy();
275 | // we can probably remove the unbind calls in 2.0
276 | // all event bindings should go through this._on()
277 | this.element
278 | .unbind( this.eventNamespace )
279 | // 1.9 BC for #7810
280 | // TODO remove dual storage
281 | .removeData( this.widgetName )
282 | .removeData( this.widgetFullName )
283 | // support: jquery <1.6.3
284 | // http://bugs.jquery.com/ticket/9413
285 | .removeData( $.camelCase( this.widgetFullName ) );
286 | this.widget()
287 | .unbind( this.eventNamespace )
288 | .removeAttr( "aria-disabled" )
289 | .removeClass(
290 | this.widgetFullName + "-disabled " +
291 | "ui-state-disabled" );
292 |
293 | // clean up events and states
294 | this.bindings.unbind( this.eventNamespace );
295 | this.hoverable.removeClass( "ui-state-hover" );
296 | this.focusable.removeClass( "ui-state-focus" );
297 | },
298 | _destroy: $.noop,
299 |
300 | widget: function() {
301 | return this.element;
302 | },
303 |
304 | option: function( key, value ) {
305 | var options = key,
306 | parts,
307 | curOption,
308 | i;
309 |
310 | if ( arguments.length === 0 ) {
311 | // don't return a reference to the internal hash
312 | return $.widget.extend( {}, this.options );
313 | }
314 |
315 | if ( typeof key === "string" ) {
316 | // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
317 | options = {};
318 | parts = key.split( "." );
319 | key = parts.shift();
320 | if ( parts.length ) {
321 | curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
322 | for ( i = 0; i < parts.length - 1; i++ ) {
323 | curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
324 | curOption = curOption[ parts[ i ] ];
325 | }
326 | key = parts.pop();
327 | if ( arguments.length === 1 ) {
328 | return curOption[ key ] === undefined ? null : curOption[ key ];
329 | }
330 | curOption[ key ] = value;
331 | } else {
332 | if ( arguments.length === 1 ) {
333 | return this.options[ key ] === undefined ? null : this.options[ key ];
334 | }
335 | options[ key ] = value;
336 | }
337 | }
338 |
339 | this._setOptions( options );
340 |
341 | return this;
342 | },
343 | _setOptions: function( options ) {
344 | var key;
345 |
346 | for ( key in options ) {
347 | this._setOption( key, options[ key ] );
348 | }
349 |
350 | return this;
351 | },
352 | _setOption: function( key, value ) {
353 | this.options[ key ] = value;
354 |
355 | if ( key === "disabled" ) {
356 | this.widget()
357 | .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
358 | .attr( "aria-disabled", value );
359 | this.hoverable.removeClass( "ui-state-hover" );
360 | this.focusable.removeClass( "ui-state-focus" );
361 | }
362 |
363 | return this;
364 | },
365 |
366 | enable: function() {
367 | return this._setOption( "disabled", false );
368 | },
369 | disable: function() {
370 | return this._setOption( "disabled", true );
371 | },
372 |
373 | _on: function( suppressDisabledCheck, element, handlers ) {
374 | var delegateElement,
375 | instance = this;
376 |
377 | // no suppressDisabledCheck flag, shuffle arguments
378 | if ( typeof suppressDisabledCheck !== "boolean" ) {
379 | handlers = element;
380 | element = suppressDisabledCheck;
381 | suppressDisabledCheck = false;
382 | }
383 |
384 | // no element argument, shuffle and use this.element
385 | if ( !handlers ) {
386 | handlers = element;
387 | element = this.element;
388 | delegateElement = this.widget();
389 | } else {
390 | // accept selectors, DOM elements
391 | element = delegateElement = $( element );
392 | this.bindings = this.bindings.add( element );
393 | }
394 |
395 | $.each( handlers, function( event, handler ) {
396 | function handlerProxy() {
397 | // allow widgets to customize the disabled handling
398 | // - disabled as an array instead of boolean
399 | // - disabled class as method for disabling individual parts
400 | if ( !suppressDisabledCheck &&
401 | ( instance.options.disabled === true ||
402 | $( this ).hasClass( "ui-state-disabled" ) ) ) {
403 | return;
404 | }
405 | return ( typeof handler === "string" ? instance[ handler ] : handler )
406 | .apply( instance, arguments );
407 | }
408 |
409 | // copy the guid so direct unbinding works
410 | if ( typeof handler !== "string" ) {
411 | handlerProxy.guid = handler.guid =
412 | handler.guid || handlerProxy.guid || $.guid++;
413 | }
414 |
415 | var match = event.match( /^(\w+)\s*(.*)$/ ),
416 | eventName = match[1] + instance.eventNamespace,
417 | selector = match[2];
418 | if ( selector ) {
419 | delegateElement.delegate( selector, eventName, handlerProxy );
420 | } else {
421 | element.bind( eventName, handlerProxy );
422 | }
423 | });
424 | },
425 |
426 | _off: function( element, eventName ) {
427 | eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
428 | element.unbind( eventName ).undelegate( eventName );
429 | },
430 |
431 | _delay: function( handler, delay ) {
432 | function handlerProxy() {
433 | return ( typeof handler === "string" ? instance[ handler ] : handler )
434 | .apply( instance, arguments );
435 | }
436 | var instance = this;
437 | return setTimeout( handlerProxy, delay || 0 );
438 | },
439 |
440 | _hoverable: function( element ) {
441 | this.hoverable = this.hoverable.add( element );
442 | this._on( element, {
443 | mouseenter: function( event ) {
444 | $( event.currentTarget ).addClass( "ui-state-hover" );
445 | },
446 | mouseleave: function( event ) {
447 | $( event.currentTarget ).removeClass( "ui-state-hover" );
448 | }
449 | });
450 | },
451 |
452 | _focusable: function( element ) {
453 | this.focusable = this.focusable.add( element );
454 | this._on( element, {
455 | focusin: function( event ) {
456 | $( event.currentTarget ).addClass( "ui-state-focus" );
457 | },
458 | focusout: function( event ) {
459 | $( event.currentTarget ).removeClass( "ui-state-focus" );
460 | }
461 | });
462 | },
463 |
464 | _trigger: function( type, event, data ) {
465 | var prop, orig,
466 | callback = this.options[ type ];
467 |
468 | data = data || {};
469 | event = $.Event( event );
470 | event.type = ( type === this.widgetEventPrefix ?
471 | type :
472 | this.widgetEventPrefix + type ).toLowerCase();
473 | // the original event may come from any element
474 | // so we need to reset the target on the new event
475 | event.target = this.element[ 0 ];
476 |
477 | // copy original event properties over to the new event
478 | orig = event.originalEvent;
479 | if ( orig ) {
480 | for ( prop in orig ) {
481 | if ( !( prop in event ) ) {
482 | event[ prop ] = orig[ prop ];
483 | }
484 | }
485 | }
486 |
487 | this.element.trigger( event, data );
488 | return !( $.isFunction( callback ) &&
489 | callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
490 | event.isDefaultPrevented() );
491 | }
492 | };
493 |
494 | $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
495 | $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
496 | if ( typeof options === "string" ) {
497 | options = { effect: options };
498 | }
499 | var hasOptions,
500 | effectName = !options ?
501 | method :
502 | options === true || typeof options === "number" ?
503 | defaultEffect :
504 | options.effect || defaultEffect;
505 | options = options || {};
506 | if ( typeof options === "number" ) {
507 | options = { duration: options };
508 | }
509 | hasOptions = !$.isEmptyObject( options );
510 | options.complete = callback;
511 | if ( options.delay ) {
512 | element.delay( options.delay );
513 | }
514 | if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
515 | element[ method ]( options );
516 | } else if ( effectName !== method && element[ effectName ] ) {
517 | element[ effectName ]( options.duration, options.easing, callback );
518 | } else {
519 | element.queue(function( next ) {
520 | $( this )[ method ]();
521 | if ( callback ) {
522 | callback.call( element[ 0 ] );
523 | }
524 | next();
525 | });
526 | }
527 | };
528 | });
529 |
530 | }));
531 |
--------------------------------------------------------------------------------
/web/css/github-markdown.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: octicons-anchor;
3 | src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==) format('woff');
4 | }
5 |
6 | html {
7 | font-family: sans-serif;
8 | -ms-text-size-adjust: 100%;
9 | -webkit-text-size-adjust: 100%
10 | }
11 |
12 | body {
13 | min-width: 1020px;
14 | font: 13px/1.4 Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
15 | /* color: #333; */
16 | background-color: #fff;
17 | }
18 |
19 | table {
20 | border-collapse: collapse;
21 | border-spacing: 0
22 | }
23 |
24 | td,th {
25 | padding: 0
26 | }
27 |
28 | * {
29 | -moz-box-sizing: border-box;
30 | box-sizing: border-box;
31 | }
32 |
33 | .container {
34 | width: 980px;
35 | margin-right: auto;
36 | margin-left: auto
37 | }
38 |
39 | .container:before {
40 | display: table;
41 | content: ""
42 | }
43 |
44 | .container:after {
45 | display: table;
46 | clear: both;
47 | content: ""
48 | }
49 |
50 | .boxed-group {
51 | position: relative;
52 | border-radius: 3px;
53 | margin-bottom: 30px
54 | }
55 |
56 | #readme .markdown-body, #readme .plain {
57 | background-color: #fff;
58 | border: 1px solid #ddd;
59 | border-bottom-left-radius: 3px;
60 | border-bottom-right-radius: 3px;
61 | padding: 30px;
62 | word-wrap: break-word;
63 | }
64 |
65 | #readme .plain pre {
66 | font-size: 15px;
67 | white-space: pre-wrap
68 | }
69 |
70 | .repository-with-sidebar:before {
71 | display: table;
72 | content: ""
73 | }
74 |
75 | .repository-with-sidebar:after {
76 | display: table;
77 | clear: both;
78 | content: ""
79 | }
80 |
81 | .repository-with-sidebar .repository-sidebar {
82 | float: right;
83 | width: 38px
84 | }
85 |
86 | .repository-with-sidebar .repository-sidebar .sidebar-button {
87 | width: 100%;
88 | margin: 0 0 10px;
89 | text-align: center
90 | }
91 |
92 | .repository-with-sidebar .repository-sidebar h3 {
93 | margin-bottom: 5px;
94 | font-size: 11px;
95 | font-weight: normal;
96 | color: #999
97 | }
98 |
99 | .repository-with-sidebar .repository-sidebar .clone-url {
100 | display: none;
101 | margin-top: -5px
102 | }
103 |
104 | .repository-with-sidebar .repository-sidebar .clone-url.open {
105 | display: block
106 | }
107 |
108 | .repository-with-sidebar .repository-sidebar .clone-options {
109 | margin: 8px 0 15px;
110 | font-size: 11px;
111 | color: #666
112 | }
113 |
114 | .repository-with-sidebar .repository-sidebar .clone-options .octicon-question {
115 | position: relative;
116 | bottom: 1px;
117 | font-size: 11px;
118 | color: #000;
119 | cursor: pointer
120 | }
121 |
122 | .repository-with-sidebar .repository-content {
123 | /* float: left; */
124 | width: 920px;
125 | }
126 |
127 | .repository-with-sidebar.with-full-navigation .repository-content {
128 | width: 790px;
129 | }
130 |
131 | .repository-with-sidebar.with-full-navigation .repository-sidebar {
132 | width: 170px
133 | }
134 |
135 | .repository-with-sidebar.with-full-navigation .sunken-menu-group .tooltipped:before, .repository-with-sidebar.with-full-navigation .sunken-menu-group .tooltipped:after {
136 | display: none
137 | }
138 |
139 | .markdown-body {
140 | -ms-text-size-adjust: 100%;
141 | -webkit-text-size-adjust: 100%;
142 | color: #333;
143 | overflow: hidden;
144 | font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;
145 | font-size: 16px;
146 | line-height: 1.6;
147 | word-wrap: break-word;
148 | }
149 |
150 | .markdown-body a {
151 | background: transparent;
152 | }
153 |
154 | .markdown-body a:active,
155 | .markdown-body a:hover {
156 | outline: 0;
157 | }
158 |
159 | .markdown-body strong {
160 | font-weight: bold;
161 | }
162 |
163 | .markdown-body h1 {
164 | font-size: 2em;
165 | margin: 0.67em 0;
166 | }
167 |
168 | .markdown-body img {
169 | border: 0;
170 | }
171 |
172 | .markdown-body hr {
173 | -moz-box-sizing: content-box;
174 | box-sizing: content-box;
175 | height: 0;
176 | }
177 |
178 | .markdown-body pre {
179 | overflow: auto;
180 | }
181 |
182 | .markdown-body code,
183 | .markdown-body kbd,
184 | .markdown-body pre {
185 | font-family: monospace, monospace;
186 | font-size: 1em;
187 | }
188 |
189 | .markdown-body input {
190 | color: inherit;
191 | font: inherit;
192 | margin: 0;
193 | }
194 |
195 | .markdown-body html input[disabled] {
196 | cursor: default;
197 | }
198 |
199 | .markdown-body input {
200 | line-height: normal;
201 | }
202 |
203 | .markdown-body input[type="checkbox"] {
204 | -moz-box-sizing: border-box;
205 | box-sizing: border-box;
206 | padding: 0;
207 | }
208 |
209 | .markdown-body table {
210 | border-collapse: collapse;
211 | border-spacing: 0;
212 | }
213 |
214 | .markdown-body td,
215 | .markdown-body th {
216 | padding: 0;
217 | }
218 |
219 | .markdown-body * {
220 | -moz-box-sizing: border-box;
221 | box-sizing: border-box;
222 | }
223 |
224 | .markdown-body input {
225 | font: 13px/1.4 Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
226 | }
227 |
228 | .markdown-body a {
229 | color: #4183c4;
230 | text-decoration: none;
231 | }
232 |
233 | .markdown-body a:hover,
234 | .markdown-body a:focus,
235 | .markdown-body a:active {
236 | text-decoration: underline;
237 | }
238 |
239 | .markdown-body hr {
240 | height: 0;
241 | margin: 15px 0;
242 | overflow: hidden;
243 | background: transparent;
244 | border: 0;
245 | border-bottom: 1px solid #ddd;
246 | }
247 |
248 | .markdown-body hr:before {
249 | display: table;
250 | content: "";
251 | }
252 |
253 | .markdown-body hr:after {
254 | display: table;
255 | clear: both;
256 | content: "";
257 | }
258 |
259 | .markdown-body h1,
260 | .markdown-body h2,
261 | .markdown-body h3,
262 | .markdown-body h4,
263 | .markdown-body h5,
264 | .markdown-body h6 {
265 | margin-top: 15px;
266 | margin-bottom: 15px;
267 | line-height: 1.1;
268 | }
269 |
270 | .markdown-body h1 {
271 | font-size: 30px;
272 | }
273 |
274 | .markdown-body h2 {
275 | font-size: 21px;
276 | }
277 |
278 | .markdown-body h3 {
279 | font-size: 16px;
280 | }
281 |
282 | .markdown-body h4 {
283 | font-size: 14px;
284 | }
285 |
286 | .markdown-body h5 {
287 | font-size: 12px;
288 | }
289 |
290 | .markdown-body h6 {
291 | font-size: 11px;
292 | }
293 |
294 | .markdown-body blockquote {
295 | margin: 0;
296 | }
297 |
298 | .markdown-body ul,
299 | .markdown-body ol {
300 | padding: 0;
301 | margin-top: 0;
302 | margin-bottom: 0;
303 | }
304 |
305 | .markdown-body ol ol,
306 | .markdown-body ul ol {
307 | list-style-type: lower-roman;
308 | }
309 |
310 | .markdown-body ul ul ol,
311 | .markdown-body ul ol ol,
312 | .markdown-body ol ul ol,
313 | .markdown-body ol ol ol {
314 | list-style-type: lower-alpha;
315 | }
316 |
317 | .markdown-body dd {
318 | margin-left: 0;
319 | }
320 |
321 | .markdown-body code {
322 | font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
323 | }
324 |
325 | .markdown-body pre {
326 | margin-top: 0;
327 | margin-bottom: 0;
328 | font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
329 | }
330 |
331 | .markdown-body .octicon {
332 | font: normal normal 16px octicons-anchor;
333 | line-height: 1;
334 | display: inline-block;
335 | text-decoration: none;
336 | -webkit-font-smoothing: antialiased;
337 | -moz-osx-font-smoothing: grayscale;
338 | -webkit-user-select: none;
339 | -moz-user-select: none;
340 | -ms-user-select: none;
341 | user-select: none;
342 | }
343 |
344 | .markdown-body .octicon-link:before {
345 | content: '\f05c';
346 | }
347 |
348 | .markdown-body>*:first-child {
349 | margin-top: 0 !important;
350 | }
351 |
352 | .markdown-body>*:last-child {
353 | margin-bottom: 0 !important;
354 | }
355 |
356 | .markdown-body .anchor {
357 | position: absolute;
358 | top: 0;
359 | bottom: 0;
360 | left: 0;
361 | display: block;
362 | padding-right: 6px;
363 | padding-left: 30px;
364 | margin-left: -30px;
365 | }
366 |
367 | .markdown-body .anchor:focus {
368 | outline: none;
369 | }
370 |
371 | .markdown-body h1,
372 | .markdown-body h2,
373 | .markdown-body h3,
374 | .markdown-body h4,
375 | .markdown-body h5,
376 | .markdown-body h6 {
377 | position: relative;
378 | margin-top: 1em;
379 | margin-bottom: 16px;
380 | font-weight: bold;
381 | line-height: 1.4;
382 | }
383 |
384 | .markdown-body h1 .octicon-link,
385 | .markdown-body h2 .octicon-link,
386 | .markdown-body h3 .octicon-link,
387 | .markdown-body h4 .octicon-link,
388 | .markdown-body h5 .octicon-link,
389 | .markdown-body h6 .octicon-link {
390 | display: none;
391 | color: #000;
392 | vertical-align: middle;
393 | }
394 |
395 | .markdown-body h1:hover .anchor,
396 | .markdown-body h2:hover .anchor,
397 | .markdown-body h3:hover .anchor,
398 | .markdown-body h4:hover .anchor,
399 | .markdown-body h5:hover .anchor,
400 | .markdown-body h6:hover .anchor {
401 | padding-left: 8px;
402 | margin-left: -30px;
403 | line-height: 1;
404 | text-decoration: none;
405 | }
406 |
407 | .markdown-body h1:hover .anchor .octicon-link,
408 | .markdown-body h2:hover .anchor .octicon-link,
409 | .markdown-body h3:hover .anchor .octicon-link,
410 | .markdown-body h4:hover .anchor .octicon-link,
411 | .markdown-body h5:hover .anchor .octicon-link,
412 | .markdown-body h6:hover .anchor .octicon-link {
413 | display: inline-block;
414 | }
415 |
416 | .markdown-body h1 {
417 | padding-bottom: 0.3em;
418 | font-size: 2.25em;
419 | line-height: 1.2;
420 | border-bottom: 1px solid #eee;
421 | }
422 |
423 | .markdown-body h2 {
424 | padding-bottom: 0.3em;
425 | font-size: 1.75em;
426 | line-height: 1.225;
427 | border-bottom: 1px solid #eee;
428 | }
429 |
430 | .markdown-body h3 {
431 | font-size: 1.5em;
432 | line-height: 1.43;
433 | }
434 |
435 | .markdown-body h4 {
436 | font-size: 1.25em;
437 | }
438 |
439 | .markdown-body h5 {
440 | font-size: 1em;
441 | }
442 |
443 | .markdown-body h6 {
444 | font-size: 1em;
445 | color: #777;
446 | }
447 |
448 | .markdown-body p,
449 | .markdown-body blockquote,
450 | .markdown-body ul,
451 | .markdown-body ol,
452 | .markdown-body dl,
453 | .markdown-body table,
454 | .markdown-body pre {
455 | margin-top: 0;
456 | margin-bottom: 16px;
457 | }
458 |
459 | .markdown-body hr {
460 | height: 4px;
461 | padding: 0;
462 | margin: 16px 0;
463 | background-color: #e7e7e7;
464 | border: 0 none;
465 | }
466 |
467 | .markdown-body ul,
468 | .markdown-body ol {
469 | padding-left: 2em;
470 | }
471 |
472 | .markdown-body ul ul,
473 | .markdown-body ul ol,
474 | .markdown-body ol ol,
475 | .markdown-body ol ul {
476 | margin-top: 0;
477 | margin-bottom: 0;
478 | }
479 |
480 | .markdown-body li>p {
481 | margin-top: 16px;
482 | }
483 |
484 | .markdown-body dl {
485 | padding: 0;
486 | }
487 |
488 | .markdown-body dl dt {
489 | padding: 0;
490 | margin-top: 16px;
491 | font-size: 1em;
492 | font-style: italic;
493 | font-weight: bold;
494 | }
495 |
496 | .markdown-body dl dd {
497 | padding: 0 16px;
498 | margin-bottom: 16px;
499 | }
500 |
501 | .markdown-body blockquote {
502 | padding: 0 15px;
503 | color: #777;
504 | border-left: 4px solid #ddd;
505 | }
506 |
507 | .markdown-body blockquote>:first-child {
508 | margin-top: 0;
509 | }
510 |
511 | .markdown-body blockquote>:last-child {
512 | margin-bottom: 0;
513 | }
514 |
515 | .markdown-body table {
516 | display: block;
517 | width: 100%;
518 | overflow: auto;
519 | word-break: normal;
520 | word-break: keep-all;
521 | }
522 |
523 | .markdown-body table th {
524 | font-weight: bold;
525 | }
526 |
527 | .markdown-body table th,
528 | .markdown-body table td {
529 | padding: 6px 13px;
530 | border: 1px solid #ddd;
531 | }
532 |
533 | .markdown-body table tr {
534 | background-color: #fff;
535 | border-top: 1px solid #ccc;
536 | }
537 |
538 | .markdown-body table tr:nth-child(2n) {
539 | background-color: #f8f8f8;
540 | }
541 |
542 | .markdown-body img {
543 | max-width: 100%;
544 | -moz-box-sizing: border-box;
545 | box-sizing: border-box;
546 | }
547 |
548 | .markdown-body code {
549 | padding: 0;
550 | padding-top: 0.2em;
551 | padding-bottom: 0.2em;
552 | margin: 0;
553 | font-size: 85%;
554 | background-color: rgba(0,0,0,0.04);
555 | border-radius: 3px;
556 | }
557 |
558 | .markdown-body code:before,
559 | .markdown-body code:after {
560 | letter-spacing: -0.2em;
561 | content: "\00a0";
562 | }
563 |
564 | .markdown-body pre>code {
565 | padding: 0;
566 | margin: 0;
567 | font-size: 100%;
568 | word-break: normal;
569 | white-space: pre;
570 | background: transparent;
571 | border: 0;
572 | }
573 |
574 | .markdown-body .highlight {
575 | margin-bottom: 16px;
576 | }
577 |
578 | .markdown-body .highlight pre,
579 | .markdown-body pre {
580 | padding: 16px;
581 | overflow: auto;
582 | font-size: 85%;
583 | line-height: 1.45;
584 | background-color: #f7f7f7;
585 | border-radius: 3px;
586 | }
587 |
588 | .markdown-body .highlight pre {
589 | margin-bottom: 0;
590 | word-break: normal;
591 | }
592 |
593 | .markdown-body pre {
594 | word-wrap: normal;
595 | }
596 |
597 | .markdown-body pre code {
598 | display: inline;
599 | max-width: initial;
600 | padding: 0;
601 | margin: 0;
602 | overflow: initial;
603 | line-height: inherit;
604 | word-wrap: normal;
605 | background-color: transparent;
606 | border: 0;
607 | }
608 |
609 | .markdown-body pre code:before,
610 | .markdown-body pre code:after {
611 | content: normal;
612 | }
613 |
614 | .markdown-body .pl-c {
615 | color: #969896;
616 | }
617 |
618 | .markdown-body .pl-c1,
619 | .markdown-body .pl-mdh,
620 | .markdown-body .pl-mm,
621 | .markdown-body .pl-mp,
622 | .markdown-body .pl-mr,
623 | .markdown-body .pl-s1 .pl-v,
624 | .markdown-body .pl-s3,
625 | .markdown-body .pl-sc,
626 | .markdown-body .pl-sv {
627 | color: #0086b3;
628 | }
629 |
630 | .markdown-body .pl-e,
631 | .markdown-body .pl-en {
632 | color: #795da3;
633 | }
634 |
635 | .markdown-body .pl-s1 .pl-s2,
636 | .markdown-body .pl-smi,
637 | .markdown-body .pl-smp,
638 | .markdown-body .pl-stj,
639 | .markdown-body .pl-vo,
640 | .markdown-body .pl-vpf {
641 | color: #333;
642 | }
643 |
644 | .markdown-body .pl-ent {
645 | color: #63a35c;
646 | }
647 |
648 | .markdown-body .pl-k,
649 | .markdown-body .pl-s,
650 | .markdown-body .pl-st {
651 | color: #a71d5d;
652 | }
653 |
654 | .markdown-body .pl-pds,
655 | .markdown-body .pl-s1,
656 | .markdown-body .pl-s1 .pl-pse .pl-s2,
657 | .markdown-body .pl-sr,
658 | .markdown-body .pl-sr .pl-cce,
659 | .markdown-body .pl-sr .pl-sra,
660 | .markdown-body .pl-sr .pl-sre,
661 | .markdown-body .pl-src,
662 | .markdown-body .pl-v {
663 | color: #df5000;
664 | }
665 |
666 | .markdown-body .pl-id {
667 | color: #b52a1d;
668 | }
669 |
670 | .markdown-body .pl-ii {
671 | background-color: #b52a1d;
672 | color: #f8f8f8;
673 | }
674 |
675 | .markdown-body .pl-sr .pl-cce {
676 | color: #63a35c;
677 | font-weight: bold;
678 | }
679 |
680 | .markdown-body .pl-ml {
681 | color: #693a17;
682 | }
683 |
684 | .markdown-body .pl-mh,
685 | .markdown-body .pl-mh .pl-en,
686 | .markdown-body .pl-ms {
687 | color: #1d3e81;
688 | font-weight: bold;
689 | }
690 |
691 | .markdown-body .pl-mq {
692 | color: #008080;
693 | }
694 |
695 | .markdown-body .pl-mi {
696 | color: #333;
697 | font-style: italic;
698 | }
699 |
700 | .markdown-body .pl-mb {
701 | color: #333;
702 | font-weight: bold;
703 | }
704 |
705 | .markdown-body .pl-md,
706 | .markdown-body .pl-mdhf {
707 | background-color: #ffecec;
708 | color: #bd2c00;
709 | }
710 |
711 | .markdown-body .pl-mdht,
712 | .markdown-body .pl-mi1 {
713 | background-color: #eaffea;
714 | color: #55a532;
715 | }
716 |
717 | .markdown-body .pl-mdr {
718 | color: #795da3;
719 | font-weight: bold;
720 | }
721 |
722 | .markdown-body .pl-mo {
723 | color: #1d3e81;
724 | }
725 |
726 | .markdown-body kbd {
727 | background-color: #e7e7e7;
728 | background-image: -webkit-linear-gradient(#fefefe, #e7e7e7);
729 | background-image: linear-gradient(#fefefe, #e7e7e7);
730 | background-repeat: repeat-x;
731 | display: inline-block;
732 | padding: 3px 5px;
733 | font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
734 | line-height: 10px;
735 | color: #000;
736 | border: 1px solid #cfcfcf;
737 | border-radius: 2px;
738 | }
739 |
740 | .markdown-body .task-list-item {
741 | list-style-type: none;
742 | }
743 |
744 | .markdown-body .task-list-item+.task-list-item {
745 | margin-top: 3px;
746 | }
747 |
748 | .markdown-body .task-list-item input {
749 | float: left;
750 | margin: 0.3em 0 0.25em -1.6em;
751 | vertical-align: middle;
752 | }
753 |
754 | .markdown-body :checked+.radio-label {
755 | z-index: 1;
756 | position: relative;
757 | border-color: #4183c4;
758 | }
759 |
--------------------------------------------------------------------------------
/web/js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Bootstrap.js by @fat & @mdo
3 | * plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js, bootstrap-scrollspy.js, bootstrap-tab.js, bootstrap-tooltip.js, bootstrap-popover.js, bootstrap-affix.js, bootstrap-alert.js, bootstrap-button.js, bootstrap-collapse.js, bootstrap-carousel.js, bootstrap-typeahead.js
4 | * Copyright 2013 Twitter, Inc.
5 | * http://www.apache.org/licenses/LICENSE-2.0.txt
6 | */
7 | !function(a){a(function(){a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery),!function(a){var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in").attr("aria-hidden",!1),b.enforceFocus(),c?b.$element.one(a.support.transition.end,function(){b.$element.focus().trigger("shown")}):b.$element.focus().trigger("shown")})},hide:function(b){b&&b.preventDefault();var c=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,this.escape(),a(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),a.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var b=this;a(document).on("focusin.modal",function(a){b.$element[0]!==a.target&&!b.$element.has(a.target).length&&b.$element.focus()})},escape:function(){var a=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(b){b.which==27&&a.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),b.hideModal()},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),b.hideModal()})},hideModal:function(){var a=this;this.$element.hide(),this.backdrop(function(){a.removeBackdrop(),a.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('
').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?a.proxy(this.$element[0].focus,this.$element[0]):a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!b)return;e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,b):b()):b&&b()}};var c=a.fn.modal;a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a.fn.modal.noConflict=function(){return a.fn.modal=c,this},a(document).on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());b.preventDefault(),e.modal(f).one("hide",function(){c.focus()})})}(window.jQuery),!function(a){function d(){a(".dropdown-backdrop").remove(),a(b).each(function(){e(a(this)).removeClass("open")})}function e(b){var c=b.attr("data-target"),d;c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,"")),d=c&&a(c);if(!d||!d.length)d=b.parent();return d}var b="[data-toggle=dropdown]",c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),f,g;if(c.is(".disabled, :disabled"))return;return f=e(c),g=f.hasClass("open"),d(),g||("ontouchstart"in document.documentElement&&a('
').insertBefore(a(this)).on("click",d),f.toggleClass("open")),c.focus(),!1},keydown:function(c){var d,f,g,h,i,j;if(!/(38|40|27)/.test(c.keyCode))return;d=a(this),c.preventDefault(),c.stopPropagation();if(d.is(".disabled, :disabled"))return;h=e(d),i=h.hasClass("open");if(!i||i&&c.keyCode==27)return c.which==27&&h.find(b).focus(),d.click();f=a("[role=menu] li:not(.divider):visible a",h);if(!f.length)return;j=f.index(f.filter(":focus")),c.keyCode==38&&j>0&&j--,c.keyCode==40&&j
a",this.$body=a("body"),this.refresh(),this.process()}b.prototype={constructor:b,refresh:function(){var b=this,c;this.offsets=a([]),this.targets=a([]),c=this.$body.find(this.selector).map(function(){var c=a(this),d=c.data("target")||c.attr("href"),e=/^#\w/.test(d)&&a(d);return e&&e.length&&[[e.position().top+(!a.isWindow(b.$scrollElement.get(0))&&b.$scrollElement.scrollTop()),d]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,c=b-this.$scrollElement.height(),d=this.offsets,e=this.targets,f=this.activeTarget,g;if(a>=c)return f!=(g=e.last()[0])&&this.activate(g);for(g=d.length;g--;)f!=e[g]&&a>=d[g]&&(!d[g+1]||a<=d[g+1])&&this.activate(e[g])},activate:function(b){var c,d;this.activeTarget=b,a(this.selector).parent(".active").removeClass("active"),d=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',c=a(d).parent("li").addClass("active"),c.parent(".dropdown-menu").length&&(c=c.closest("li.dropdown").addClass("active")),c.trigger("activate")}};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f,g;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active:last a")[0],g=a.Event("show",{relatedTarget:e}),b.trigger(g);if(g.isDefaultPrevented())return;f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),!function(a){var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f,g,h,i;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,g=this.options.trigger.split(" ");for(i=g.length;i--;)h=g[i],h=="click"?this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this)):h!="manual"&&(e=h=="hover"?"mouseenter":"focus",f=h=="hover"?"mouseleave":"blur",this.$element.on(e+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f+"."+this.type,this.options.selector,a.proxy(this.leave,this)));this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,this.$element.data(),b),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a.fn[this.type].defaults,d={},e;this._options&&a.each(this._options,function(a,b){c[a]!=b&&(d[a]=b)},this),e=a(b.currentTarget)[this.type](d).data(this.type);if(!e.options.delay||!e.options.delay.show)return e.show();clearTimeout(this.timeout),e.hoverState="in",this.timeout=setTimeout(function(){e.hoverState=="in"&&e.show()},e.options.delay.show)},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!c.options.delay||!c.options.delay.hide)return c.hide();c.hoverState="out",this.timeout=setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide)},show:function(){var b,c,d,e,f,g,h=a.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(h);if(h.isDefaultPrevented())return;b=this.tip(),this.setContent(),this.options.animation&&b.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,b[0],this.$element[0]):this.options.placement,b.detach().css({top:0,left:0,display:"block"}),this.options.container?b.appendTo(this.options.container):b.insertAfter(this.$element),c=this.getPosition(),d=b[0].offsetWidth,e=b[0].offsetHeight;switch(f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}this.applyPlacement(g,f),this.$element.trigger("shown")}},applyPlacement:function(a,b){var c=this.tip(),d=c[0].offsetWidth,e=c[0].offsetHeight,f,g,h,i;c.offset(a).addClass(b).addClass("in"),f=c[0].offsetWidth,g=c[0].offsetHeight,b=="top"&&g!=e&&(a.top=a.top+e-g,i=!0),b=="bottom"||b=="top"?(h=0,a.left<0&&(h=a.left*-2,a.left=0,c.offset(a),f=c[0].offsetWidth,g=c[0].offsetHeight),this.replaceArrow(h-d+f,f,"left")):this.replaceArrow(g-e,g,"top"),i&&c.offset(a)},replaceArrow:function(a,b,c){this.arrow().css(c,a?50*(1-a/b)+"%":"")},setContent:function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},hide:function(){function e(){var b=setTimeout(function(){c.off(a.support.transition.end).detach()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.detach()})}var b=this,c=this.tip(),d=a.Event("hide");this.$element.trigger(d);if(d.isDefaultPrevented())return;return c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?e():c.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var b=this.$element[0];return a.extend({},typeof b.getBoundingClientRect=="function"?b.getBoundingClientRect():{width:b.offsetWidth,height:b.offsetHeight},this.$element.offset())},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(b){var c=b?a(b.currentTarget)[this.type](this._options).data(this.type):this;c.tip().hasClass("in")?c.hide():c.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var c=a.fn.tooltip;a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},a.fn.tooltip.noConflict=function(){return a.fn.tooltip=c,this}}(window.jQuery),!function(a){var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=(typeof c.content=="function"?c.content.call(b[0]):c.content)||b.attr("data-content"),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:''}),a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),!function(a){var b=function(b,c){this.options=a.extend({},a.fn.affix.defaults,c),this.$window=a(window).on("scroll.affix.data-api",a.proxy(this.checkPosition,this)).on("click.affix.data-api",a.proxy(function(){setTimeout(a.proxy(this.checkPosition,this),1)},this)),this.$element=a(b),this.checkPosition()};b.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var b=a(document).height(),c=this.$window.scrollTop(),d=this.$element.offset(),e=this.options.offset,f=e.bottom,g=e.top,h="affix affix-top affix-bottom",i;typeof e!="object"&&(f=g=e),typeof g=="function"&&(g=e.top()),typeof f=="function"&&(f=e.bottom()),i=this.unpin!=null&&c+this.unpin<=d.top?!1:f!=null&&d.top+this.$element.height()>=b-f?"bottom":g!=null&&c<=g?"top":!1;if(this.affixed===i)return;this.affixed=i,this.unpin=i=="bottom"?d.top-c:null,this.$element.removeClass(h).addClass("affix"+(i?"-"+i:""))};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("affix"),f=typeof c=="object"&&c;e||d.data("affix",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.defaults={offset:0},a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery),!function(a){var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger(b=a.Event("close"));if(b.isDefaultPrevented())return;e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.alert.data-api",b,c.prototype.close)}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b,c,d,e;if(this.transitioning||this.$element.hasClass("in"))return;b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find("> .accordion-group > .in");if(d&&d.length){e=d.data("collapse");if(e&&e.transitioning)return;d.collapse("hide"),e||d.data("collapse",null)}this.$element[b](0),this.transition("addClass",a.Event("show"),"shown"),a.support.transition&&this.$element[b](this.$element[0][c])},hide:function(){var b;if(this.transitioning||!this.$element.hasClass("in"))return;b=this.dimension(),this.reset(this.$element[b]()),this.transition("removeClass",a.Event("hide"),"hidden"),this.$element[b](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a!==null?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c.type=="show"&&e.reset(),e.transitioning=0,e.$element.trigger(d)};this.$element.trigger(c);if(c.isDefaultPrevented())return;this.transitioning=1,this.$element[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=a.extend({},a.fn.collapse.defaults,d.data(),typeof c=="object"&&c);e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();c[a(e).hasClass("in")?"addClass":"removeClass"]("collapsed"),a(e).collapse(f)})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(b){var c=this.getActiveIndex(),d=this;if(b>this.$items.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){d.to(b)}):c==b?this.pause().cycle():this.slide(b>c?"next":"prev",a(this.$items[b]))},pause:function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this,j;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h](),j=a.Event("slide",{relatedTarget:e[0],direction:g});if(e.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")}));if(a.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(j);if(j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})}else{this.$element.trigger(j);if(j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=a.extend({},a.fn.carousel.defaults,typeof c=="object"&&c),g=typeof c=="string"?c:f.slide;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),c.data()),g;e.carousel(f),(g=c.attr("data-slide-to"))&&e.data("carousel").pause().to(g).cycle(),b.preventDefault()})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=a(this.options.menu),this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(a)).change(),this.hide()},updater:function(a){return a},show:function(){var b=a.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:b.top+b.height,left:b.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c;return this.query=this.$element.val(),!this.query||this.query.length"+b+""})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("focus",a.proxy(this.focus,this)).on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",a.proxy(this.keydown,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this)).on("mouseleave","li",a.proxy(this.mouseleave,this))},eventSupported:function(a){var b=a in this.$element;return b||(this.$element.setAttribute(a,"return;"),b=typeof this.$element[a]=="function"),b},move:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:a.preventDefault(),this.prev();break;case 40:a.preventDefault(),this.next()}a.stopPropagation()},keydown:function(b){this.suppressKeyPressRepeat=~a.inArray(b.keyCode,[40,38,9,13,27]),this.move(b)},keypress:function(a){if(this.suppressKeyPressRepeat)return;this.move(a)},keyup:function(a){switch(a.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},focus:function(a){this.focused=!0},blur:function(a){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(a){a.stopPropagation(),a.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(b){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")},mouseleave:function(a){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var c=a.fn.typeahead;a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'',item:'',minLength:1},a.fn.typeahead.Constructor=b,a.fn.typeahead.noConflict=function(){return a.fn.typeahead=c,this},a(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;c.typeahead(c.data())})}(window.jQuery)
--------------------------------------------------------------------------------