watcher-list: Make watcher list to work as ajax
@64c2f1b58f0d8cdbb908fa20161b0ed2802951c4
--- app/actions/AbstractProjectCheckAction.java
+++ app/actions/AbstractProjectCheckAction.java
... | ... | @@ -30,10 +30,7 @@ |
30 | 30 |
import play.mvc.Http.Context; |
31 | 31 |
import play.mvc.Result; |
32 | 32 |
import play.libs.F.Promise; |
33 |
-import utils.AccessControl; |
|
34 |
-import utils.AccessLogger; |
|
35 |
-import utils.ErrorViews; |
|
36 |
-import utils.RedirectUtil; |
|
33 |
+import utils.*; |
|
37 | 34 |
|
38 | 35 |
import static play.mvc.Controller.flash; |
39 | 36 |
import static play.mvc.Http.Context.current; |
... | ... | @@ -54,12 +51,11 @@ |
54 | 51 |
String projectName = null; |
55 | 52 |
|
56 | 53 |
PathParser parser = new PathParser(context); |
57 |
- if (current().request().getHeader(UserApp.USER_TOKEN_HEADER) != null) { |
|
54 |
+ PathVariable pathVariable = new PathVariable(current().request().path()); |
|
55 |
+ if (pathVariable.isApiCall()) { |
|
58 | 56 |
// eg. context.request().path() : /-_-api/v1/owners/doortts/projects/Test/posts |
59 |
- String[] base = context.request().path().split("/owners/"); |
|
60 |
- String[] partial = base[1].split("/"); |
|
61 |
- ownerLoginId = partial[0]; |
|
62 |
- projectName = partial[2]; |
|
57 |
+ ownerLoginId = pathVariable.getPathVariable("owners"); |
|
58 |
+ projectName = pathVariable.getPathVariable("projects"); |
|
63 | 59 |
} else { |
64 | 60 |
ownerLoginId = parser.getOwnerLoginId(); |
65 | 61 |
projectName = parser.getProjectName(); |
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
... | ... | @@ -6494,7 +6494,7 @@ |
6494 | 6494 |
} |
6495 | 6495 |
|
6496 | 6496 |
.show-watchers { |
6497 |
- display: inline-block; |
|
6497 |
+ display: none; |
|
6498 | 6498 |
margin-left: 3px; |
6499 | 6499 |
&:hover { |
6500 | 6500 |
cursor: pointer; |
--- app/controllers/api/ProjectApi.java
+++ app/controllers/api/ProjectApi.java
... | ... | @@ -9,7 +9,6 @@ |
9 | 9 |
import com.fasterxml.jackson.databind.JsonNode; |
10 | 10 |
import com.fasterxml.jackson.databind.node.ObjectNode; |
11 | 11 |
import controllers.MigrationApp; |
12 |
-import controllers.annotation.AnonymousCheck; |
|
13 | 12 |
import controllers.annotation.IsAllowed; |
14 | 13 |
import models.*; |
15 | 14 |
import models.enumeration.Operation; |
... | ... | @@ -19,7 +18,8 @@ |
19 | 18 |
import play.mvc.Controller; |
20 | 19 |
import play.mvc.Result; |
21 | 20 |
|
22 |
-import java.util.*; |
|
21 |
+import java.util.List; |
|
22 |
+import java.util.Optional; |
|
23 | 23 |
import java.util.stream.Collectors; |
24 | 24 |
|
25 | 25 |
import static controllers.MigrationApp.composePlainCommentsJson; |
+++ app/controllers/api/WatcherApi.java
... | ... | @@ -0,0 +1,64 @@ |
1 | +/** | |
2 | + * Yona, 21st Century Project Hosting SW | |
3 | + * | |
4 | + * Copyright 2016 the original author or authors. | |
5 | + */ | |
6 | + | |
7 | +package controllers.api; | |
8 | + | |
9 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
10 | +import models.*; | |
11 | +import org.apache.commons.lang3.StringUtils; | |
12 | +import play.db.ebean.Transactional; | |
13 | +import play.libs.Json; | |
14 | +import play.mvc.Controller; | |
15 | +import play.mvc.Result; | |
16 | +import utils.RouteUtil; | |
17 | + | |
18 | +import java.util.ArrayList; | |
19 | +import java.util.List; | |
20 | +import java.util.Set; | |
21 | + | |
22 | +import static play.libs.Json.toJson; | |
23 | + | |
24 | +public class WatcherApi extends Controller { | |
25 | + | |
26 | + @Transactional | |
27 | + public static Result getWatchers(String owner, String projectName, Long number) { | |
28 | + final int LIMIT = 100; | |
29 | + int counter = 0; | |
30 | + | |
31 | + Project project = Project.findByOwnerAndProjectName(owner, projectName); | |
32 | + AbstractPosting posting = null; | |
33 | + String type = request().getQueryString("type"); | |
34 | + if (StringUtils.isNotEmpty(type) && type.equals("issues")) { | |
35 | + posting = AbstractPosting.findByNumber(Issue.finder, project, number); | |
36 | + } else if (StringUtils.isNotEmpty(type) && type.equals("posts")){ | |
37 | + posting = AbstractPosting.findByNumber(Posting.finder, project, number); | |
38 | + } else { | |
39 | + return ok(); | |
40 | + } | |
41 | + | |
42 | + Set<User> watchers = posting.getWatchers(); | |
43 | + ObjectNode json = Json.newObject(); | |
44 | + List<ObjectNode> watcherList = new ArrayList<>(); | |
45 | + | |
46 | + for(User user: watchers){ | |
47 | + counter++; | |
48 | + | |
49 | + ObjectNode watcher = Json.newObject(); | |
50 | + watcher.put("name", user.name); | |
51 | + watcher.put("url", RouteUtil.getUrl(user)); | |
52 | + watcherList.add(watcher); | |
53 | + if (counter == LIMIT) { | |
54 | + break; | |
55 | + } | |
56 | + } | |
57 | + | |
58 | + json.put("totalWatchers", watchers.size()); | |
59 | + json.put("watchersInList", counter); | |
60 | + json.put("watchers", toJson(watcherList)); | |
61 | + return ok(json); | |
62 | + } | |
63 | + | |
64 | +} |
+++ app/utils/PathVariable.java
... | ... | @@ -0,0 +1,56 @@ |
1 | +/** | |
2 | + * Yona, 21st Century Project Hosting SW | |
3 | + * <p> | |
4 | + * Copyright Yona & Yobi Authors & NAVER Corp. | |
5 | + * https://yona.io | |
6 | + **/ | |
7 | +package utils; | |
8 | + | |
9 | +import org.apache.commons.lang3.StringUtils; | |
10 | + | |
11 | +import java.util.HashMap; | |
12 | +import java.util.Map; | |
13 | + | |
14 | +public class PathVariable { | |
15 | + String url; | |
16 | + public static final String rootPath = play.Configuration.root().getString("application.context", ""); | |
17 | + public static final String API_PREFIX = "/-_-api/v1/"; | |
18 | + private Map<String, String> pathVariable = new HashMap<>(); | |
19 | + private boolean isApiPathCall = false; | |
20 | + | |
21 | + public PathVariable(String url) { | |
22 | + String refinedUrl = url; | |
23 | + if(StringUtils.isNotEmpty(rootPath)){ | |
24 | + refinedUrl = refinedUrl.replaceFirst(rootPath, ""); | |
25 | + } | |
26 | + if(refinedUrl.startsWith(API_PREFIX)){ | |
27 | + refinedUrl = refinedUrl.replaceFirst(API_PREFIX, ""); | |
28 | + this.isApiPathCall = true; | |
29 | + decomposeToPathVariable(refinedUrl); | |
30 | + } | |
31 | + } | |
32 | + | |
33 | + /** | |
34 | + * ex> if url path is /-_-api/v1/owners/doortts/projects/test/issue/21 | |
35 | + * getPathVariable("owners") returns "doortts" | |
36 | + * getPathVariable("projects") returns "test" | |
37 | + */ | |
38 | + public String getPathVariable(String pathName) { | |
39 | + return this.pathVariable.get(pathName); | |
40 | + } | |
41 | + | |
42 | + public boolean isApiCall() { | |
43 | + return this.isApiPathCall; | |
44 | + } | |
45 | + | |
46 | + private void decomposeToPathVariable(String refinedUrl) { | |
47 | + String[] decomposed = refinedUrl.split("/"); | |
48 | + for (int i = 0; i < decomposed.length; i = i + 2) { | |
49 | + if (i + 1 > decomposed.length - 1) { | |
50 | + pathVariable.put(decomposed[i], ""); | |
51 | + } else { | |
52 | + pathVariable.put(decomposed[i], decomposed[i + 1]); | |
53 | + } | |
54 | + } | |
55 | + } | |
56 | +} |
--- app/utils/TemplateHelper.scala
+++ app/utils/TemplateHelper.scala
... | ... | @@ -29,34 +29,10 @@ |
29 | 29 |
|
30 | 30 |
object TemplateHelper { |
31 | 31 |
|
32 |
- def watcherList(posting: AbstractPosting): String = { |
|
33 |
- val LIMIT = 100 |
|
34 |
- var counter = 0 |
|
35 |
- var str = "" |
|
36 |
- val watchers = posting.getWatchers |
|
37 |
- breakable { |
|
38 |
- for(watcher <- watchers){ |
|
39 |
- counter += 1 |
|
40 |
- if(counter > LIMIT) break |
|
41 |
- var dummy = watcher.toString // ebean eagerly loading hack |
|
42 |
- str += "<a href='" + userInfo(watcher.loginId) + "' class='watcher-name'>" + watcher.name + "</a>" |
|
43 |
- } |
|
44 |
- } |
|
45 |
- |
|
46 |
- if( watchers.size > LIMIT ) { |
|
47 |
- str += Messages.get("watchers.more", (watchers.size - LIMIT).toString) |
|
48 |
- } |
|
49 |
- str |
|
50 |
- } |
|
51 |
- |
|
52 | 32 |
def showWatchers(posting: AbstractPosting): String = { |
53 |
- if(posting.getWatchers.size > 1 && UserApp.currentUser() != User.anonymous){ |
|
54 | 33 |
"<div class='show-watchers' data-toggle='tooltip' data-placement='top' data-trigger='hover' data-html='true' title='" + Messages.get("watchers") + "'>" + |
55 |
- "<button id='watch-button' type='button' class='ybtn'><i class='yobicon-emo-coffee'></i> " + posting.getWatchers.size.toString + "</button>" + |
|
34 |
+ "<button id='watcher-list-button' type='button' class='ybtn'><i class='yobicon-emo-coffee'></i><span class='watcherCount'></span></button>" + |
|
56 | 35 |
"</div>" |
57 |
- } else { |
|
58 |
- "" |
|
59 |
- } |
|
60 | 36 |
} |
61 | 37 |
|
62 | 38 |
def buildQueryString(call: Call, queryMap: Map[String, String]): String = { |
--- app/views/board/view.scala.html
+++ app/views/board/view.scala.html
... | ... | @@ -102,7 +102,7 @@ |
102 | 102 |
} |
103 | 103 |
</div> |
104 | 104 |
|
105 |
- <div class="watcher-list">@Html(watcherList(post))</div> |
|
105 |
+ <div class="watcher-list"></div> |
|
106 | 106 |
@** Comment **@ |
107 | 107 |
<div id="comments" class="board-comment-wrap"> |
108 | 108 |
@partial_comments(project, post) |
... | ... | @@ -142,6 +142,7 @@ |
142 | 142 |
<link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.css")"> |
143 | 143 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.caret.min.js")"></script> |
144 | 144 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.js")"></script> |
145 |
+<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.WatcherList.js")"></script> |
|
145 | 146 |
<script type="text/javascript"> |
146 | 147 |
$(document).ready(function(){ |
147 | 148 |
$yobi.loadModule("board.View", { |
... | ... | @@ -181,9 +182,14 @@ |
181 | 182 |
// detect comment which contains mention at me |
182 | 183 |
$(".comment-body:contains('@UserApp.currentUser().loginId')").closest(".comment").addClass("mentioned"); |
183 | 184 |
|
184 |
- $(".show-watchers").on("click", function(){ |
|
185 |
- $(".watcher-list").toggle(); |
|
186 |
- }) |
|
185 |
+ // Watcher List |
|
186 |
+ var watcherApiUrl = "@api.routes.WatcherApi.getWatchers(project.owner, project.name, post.getNumber)?type=posts"; |
|
187 |
+ $("#watch-button").on('click', function () { |
|
188 |
+ setTimeout(function () { |
|
189 |
+ watcherListApi(watcherApiUrl); |
|
190 |
+ }, 1000); |
|
191 |
+ }); |
|
192 |
+ watcherListApi(watcherApiUrl); |
|
187 | 193 |
}); |
188 | 194 |
</script> |
189 | 195 |
@common.select2() |
--- app/views/issue/view.scala.html
+++ app/views/issue/view.scala.html
... | ... | @@ -151,7 +151,7 @@ |
151 | 151 |
<a href="@routes.IssueApp.editIssueForm(project.owner, project.name, issue.getNumber)" class="ybtn">@Messages("button.edit")</a> |
152 | 152 |
} |
153 | 153 |
</div> |
154 |
- <div class="watcher-list">@Html(watcherList(issue))</div> |
|
154 |
+ <div class="watcher-list"></div> |
|
155 | 155 |
@** Comment **@ |
156 | 156 |
<div id="comments" class="board-comment-wrap"> |
157 | 157 |
<div id="timeline"> |
... | ... | @@ -332,6 +332,7 @@ |
332 | 332 |
<link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.css")"> |
333 | 333 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.caret.min.js")"></script> |
334 | 334 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.js")"></script> |
335 |
+<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.WatcherList.js")"></script> |
|
335 | 336 |
<script type="text/javascript"> |
336 | 337 |
$(function(){ |
337 | 338 |
// yobi.issue.View |
... | ... | @@ -367,9 +368,14 @@ |
367 | 368 |
// detect comment which contains mention at me |
368 | 369 |
$(".comment-body:contains('@UserApp.currentUser().loginId')").closest(".comment").addClass("mentioned"); |
369 | 370 |
|
370 |
- $(".show-watchers").on("click", function(){ |
|
371 |
- $(".watcher-list").toggle(); |
|
372 |
- }) |
|
371 |
+ // Watcher List |
|
372 |
+ var watcherApiUrl = "@api.routes.WatcherApi.getWatchers(project.owner, project.name, issue.getNumber)?type=issues"; |
|
373 |
+ $("#watch-button").on('click', function () { |
|
374 |
+ setTimeout(function () { |
|
375 |
+ watcherListApi(watcherApiUrl); |
|
376 |
+ }, 1000); |
|
377 |
+ }); |
|
378 |
+ watcherListApi(watcherApiUrl); |
|
373 | 379 |
}); |
374 | 380 |
</script> |
375 | 381 |
} |
--- app/views/layout.scala.html
+++ app/views/layout.scala.html
... | ... | @@ -29,11 +29,8 @@ |
29 | 29 |
<link rel="stylesheet" type="text/css" media="all" href="@routes.Assets.at("javascripts/lib/pikaday/pikaday.css")" /> |
30 | 30 |
<link rel="stylesheet" type="text/css" media="all" href="@routes.Assets.at("stylesheets/yobi.css")"> |
31 | 31 |
<link rel='stylesheet' href="@routes.Assets.at("javascripts/lib/nprogress/nprogress.css")"/> |
32 |
- |
|
33 | 32 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/yona-layout.js")"></script> |
34 |
-<script type="text/javascript"> |
|
35 |
- NProgress.configure({ minimum: 0.7 }); |
|
36 |
-</script> |
|
33 |
+ |
|
37 | 34 |
</head> |
38 | 35 |
|
39 | 36 |
<body class="@theme"> |
... | ... | @@ -43,5 +40,9 @@ |
43 | 40 |
@partial_update_notification() |
44 | 41 |
@content |
45 | 42 |
@common.scripts() |
43 |
+ |
|
44 |
+<script type="text/javascript"> |
|
45 |
+ NProgress.configure({ minimum: 0.7 }); |
|
46 |
+</script> |
|
46 | 47 |
</body> |
47 | 48 |
</html> |
--- app/views/projectMenu.scala.html
+++ app/views/projectMenu.scala.html
... | ... | @@ -55,7 +55,8 @@ |
55 | 55 |
<span class="menu-name">@Messages("title.projectHome")</span><span class="short-menu">H</span> |
56 | 56 |
</a> |
57 | 57 |
</li> |
58 |
- @if(project.menuSetting.code) { |
|
58 |
+ @defining(project.menuSetting){ menuSetting => |
|
59 |
+ @if(menuSetting.code) { |
|
59 | 60 |
@if(!project.isCodeAccessibleMemberOnly || project.hasMember(UserApp.currentUser())) { |
60 | 61 |
<li class="@isActiveMenu(MenuType.CODE)"> |
61 | 62 |
<a href="@routes.CodeApp.codeBrowser(project.owner, project.name)"> |
... | ... | @@ -64,14 +65,14 @@ |
64 | 65 |
</li> |
65 | 66 |
} |
66 | 67 |
} |
67 |
- @if(project.menuSetting.issue) { |
|
68 |
+ @if(menuSetting.issue) { |
|
68 | 69 |
<li class="@isActiveMenu(MenuType.ISSUE)"> |
69 | 70 |
<a href="@routes.IssueApp.issues(project.owner, project.name, "open")"> |
70 | 71 |
<span class="menu-name">@Messages("menu.issue")</span><span class="short-menu">I</span> @countingBadge(Issue.countIssues(project.id, State.OPEN)) |
71 | 72 |
</a> |
72 | 73 |
</li> |
73 | 74 |
} |
74 |
- @if(project.menuSetting.pullRequest && project.vcs.equals("GIT")) { |
|
75 |
+ @if(menuSetting.pullRequest && project.vcs.equals("GIT")) { |
|
75 | 76 |
@if(!project.isCodeAccessibleMemberOnly || project.hasMember(UserApp.currentUser())) { |
76 | 77 |
<li class="@isActiveMenu(MenuType.PULL_REQUEST)"> |
77 | 78 |
<a href="@getPullRequestURL(project)"> |
... | ... | @@ -80,7 +81,7 @@ |
80 | 81 |
</li> |
81 | 82 |
} |
82 | 83 |
} |
83 |
- @if(project.menuSetting.review) { |
|
84 |
+ @if(menuSetting.review) { |
|
84 | 85 |
@if(!project.isCodeAccessibleMemberOnly || project.hasMember(UserApp.currentUser())) { |
85 | 86 |
<li class="@isActiveMenu(MenuType.PROJECT_REVIEW)"> |
86 | 87 |
<a href="@routes.ReviewThreadApp.reviewThreads(project.owner, project.name)"> |
... | ... | @@ -90,14 +91,14 @@ |
90 | 91 |
</li> |
91 | 92 |
} |
92 | 93 |
} |
93 |
- @if(project.menuSetting.milestone) { |
|
94 |
+ @if(menuSetting.milestone) { |
|
94 | 95 |
<li class="@isActiveMenu(MenuType.MILESTONE)"> |
95 | 96 |
<a href="@routes.MilestoneApp.milestones(project.owner, project.name)"> |
96 | 97 |
<span class="menu-name">@Messages("milestone")</span><span class="short-menu">M</span> |
97 | 98 |
</a> |
98 | 99 |
</li> |
99 | 100 |
} |
100 |
- @if(project.menuSetting.board) { |
|
101 |
+ @if(menuSetting.board) { |
|
101 | 102 |
<li class="@isActiveMenu(MenuType.BOARD)"> |
102 | 103 |
<a href="@routes.BoardApp.posts(project.owner, project.name)"> |
103 | 104 |
<span class="menu-name">@Messages("menu.board")</span><span class="short-menu">B</span> |
... | ... | @@ -107,6 +108,7 @@ |
107 | 108 |
</a> |
108 | 109 |
</li> |
109 | 110 |
} |
111 |
+ } |
|
110 | 112 |
</ul> |
111 | 113 |
@if(AccessControl.isAllowed(UserApp.currentUser(), project.asResource(), Operation.UPDATE)){ |
112 | 114 |
<div class="project-setting"> |
... | ... | @@ -125,21 +127,22 @@ |
125 | 127 |
</div> |
126 | 128 |
<script type="text/javascript"> |
127 | 129 |
$(document).ready(function(){ |
130 |
+ @defining(project.menuSetting) { menuSetting => |
|
128 | 131 |
var htKeyMap = { |
129 | 132 |
"H": "@routes.ProjectApp.project(project.owner, project.name)" |
130 |
- @if(project.menuSetting.board) { |
|
133 |
+ @if(menuSetting.board) { |
|
131 | 134 |
,"B": "@routes.BoardApp.posts(project.owner, project.name)" |
132 | 135 |
} |
133 |
- @if(project.menuSetting.code) { |
|
136 |
+ @if(menuSetting.code) { |
|
134 | 137 |
,"C": "@routes.CodeApp.codeBrowser(project.owner, project.name)" |
135 | 138 |
} |
136 |
- @if(project.menuSetting.issue) { |
|
139 |
+ @if(menuSetting.issue) { |
|
137 | 140 |
,"I": "@routes.IssueApp.issues(project.owner, project.name,"open")" |
138 | 141 |
} |
139 |
- @if(project.menuSetting.milestone) { |
|
142 |
+ @if(menuSetting.milestone) { |
|
140 | 143 |
,"M": "@routes.MilestoneApp.milestones(project.owner, project.name)" |
141 | 144 |
} |
142 |
- @if(project.menuSetting.pullRequest && project.vcs.equals("GIT")){ |
|
145 |
+ @if(menuSetting.pullRequest && project.vcs.equals("GIT")){ |
|
143 | 146 |
,"F": "@getPullRequestURL(project)" |
144 | 147 |
} |
145 | 148 |
@requestHeader.session.get("loginId") match { |
... | ... | @@ -147,6 +150,7 @@ |
147 | 150 |
case _ => { } |
148 | 151 |
} |
149 | 152 |
}; |
153 |
+ } |
|
150 | 154 |
|
151 | 155 |
$yobi.loadModule("project.Global", { |
152 | 156 |
"htKeyMap": htKeyMap, |
--- conf/routes
+++ conf/routes
... | ... | @@ -35,6 +35,7 @@ |
35 | 35 |
GET /-_-api controllers.Application.index() |
36 | 36 |
GET /-_-api/v1/ controllers.Application.index() |
37 | 37 |
GET /-_-api/v1/owners/:owner/projects/:projectName/exports controllers.api.ProjectApi.exports(owner:String, projectName:String) |
38 |
+GET /-_-api/v1/owners/:owner/projects/:projectName/posts/$number<[0-9]+>/watchers controllers.api.WatcherApi.getWatchers(owner:String, projectName:String, number:Long) |
|
38 | 39 |
POST /-_-api/v1/owners/:owner/projects/:projectName/posts controllers.api.BoardApi.newPostByJson(owner:String, projectName:String) |
39 | 40 |
POST /-_-api/v1/owners/:owner/projects/:projectName/postlabel/:number controllers.api.BoardApi.updatePostLabel(owner:String, projectName:String, number:Long) |
40 | 41 |
GET /-_-api/v1/hello controllers.api.GlobalApi.hello() |
+++ public/javascripts/common/yobi.WatcherList.js
... | ... | @@ -0,0 +1,28 @@ |
1 | +var apiUrlMemo; | |
2 | +$(".show-watchers").on("click", function () { | |
3 | + $(".watcher-list").toggle(); | |
4 | +}); | |
5 | +function watcherListApi(apiUrl){ | |
6 | + | |
7 | + if (apiUrl) { | |
8 | + apiUrlMemo = apiUrl; | |
9 | + } | |
10 | + | |
11 | + $.get(apiUrl || apiUrlMemo) | |
12 | + .done(function (data) { | |
13 | + var watcherList = ""; | |
14 | + if( data.watchersInList > 0 ){ | |
15 | + $(".watcherCount").text(" " + data.totalWatchers); | |
16 | + $(".show-watchers").css("display", "inline-block"); | |
17 | + data.watchers.forEach(function (watcher) { | |
18 | + watcherList += '<a href="' + watcher.url + '" class="watcher-name">' + watcher.name + "</a>"; | |
19 | + }); | |
20 | + if(data.totalWatchers > data.watchersInList) { | |
21 | + watcherList += Messages("watchers.more", (data.totalWatchers - data.watchersInList)) | |
22 | + } | |
23 | + $(".watcher-list").html(watcherList); | |
24 | + } else { | |
25 | + $(".show-watchers").css("display", "none"); | |
26 | + } | |
27 | + }); | |
28 | +}(No newline at end of file) |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?