Authored by yyq

tdk 交互

@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 2
3 const Router = require('koa-router'); 3 const Router = require('koa-router');
4 const _ = require('lodash'); 4 const _ = require('lodash');
  5 +const md5 = require('md5');
5 const pager = require('../utils/pager'); 6 const pager = require('../utils/pager');
6 7
7 let r = new Router(); 8 let r = new Router();
@@ -14,50 +15,81 @@ const TYPE_LIST = [ @@ -14,50 +15,81 @@ const TYPE_LIST = [
14 ]; 15 ];
15 16
16 const tdk = { 17 const tdk = {
  18 + _getDeaultList: async(ctx, next) => {
  19 +
  20 + },
17 21
18 // tdk 列表 22 // tdk 列表
19 index: async(ctx, next) => { 23 index: async(ctx, next) => {
20 let resData = {}; 24 let resData = {};
21 let type = ctx.query.type || 'skn', 25 let type = ctx.query.type || 'skn',
22 page = parseInt(`0${ctx.query.page}`, 10) || 1, 26 page = parseInt(`0${ctx.query.page}`, 10) || 1,
23 - limit = parseInt(`0${ctx.query.limit}`, 10) || 20; 27 + limit = parseInt(`0${ctx.query.limit}`, 10) || 20,
  28 + query = ctx.query.query;
24 let listKey = `tdk:${type}:links`, 29 let listKey = `tdk:${type}:links`,
25 startId = (page - 1) * limit, 30 startId = (page - 1) * limit,
26 typeObj = _.find(TYPE_LIST, {'type': type}) || {}; 31 typeObj = _.find(TYPE_LIST, {'type': type}) || {};
27 let hmget = []; 32 let hmget = [];
28 33
29 - await ctx.redis.multi([  
30 - ['llen', listKey],  
31 - ['lrange', listKey, startId, page * limit]  
32 - ]).execAsync().then(function(res) {  
33 - let total = res[0] || 1; 34 + if (query) {
  35 + query = _.trim(query);
  36 + resData.query = query;
34 37
35 - resData.pager = pager(Math.floor((total - 1) / limit) + 1, ctx.query);  
36 -  
37 - _.forEach(res[1], value => {  
38 - hmget.push(['hmget', `tdk:${type}:${value}`, 'key', 'title', 'keywords', 'description']);  
39 - });  
40 - }); 38 + query = type === 'url' ? md5(query) : query;
41 39
42 - await ctx.redis.multi(hmget).execAsync().then(function(res) {  
43 - let tdkList = []; 40 + await ctx.redis.multi([
  41 + ['exists', `tdk:${type}:${query}`],
  42 + ['hmget', `tdk:${type}:${query}`, 'key', 'title', 'keywords', 'description']
  43 + ]).execAsync().then(function(res) {
  44 + console.log(res);
  45 + if (!res[0]) {
  46 + return;
  47 + }
44 48
45 - _.forEach(res, value => {  
46 - tdkList.push({  
47 - id: ++startId, 49 + resData.tdkList = [{
  50 + id: 1,
48 typeName: typeObj.name, 51 typeName: typeObj.name,
49 typeLt: typeObj.lt, 52 typeLt: typeObj.lt,
50 - key: value[0],  
51 - title: value[1],  
52 - keywords: value[2],  
53 - description: value[3] 53 + key: res[1][0],
  54 + title: res[1][1],
  55 + keywords: res[1][2],
  56 + description: res[1][3]
  57 + }];
  58 + });
  59 + } else {
  60 + await ctx.redis.multi([
  61 + ['llen', listKey],
  62 + ['lrange', listKey, startId, page * limit]
  63 + ]).execAsync().then(function(res) {
  64 + let total = res[0] || 1;
  65 +
  66 + resData.pager = pager(Math.floor((total - 1) / limit) + 1, ctx.query);
  67 +
  68 + _.forEach(res[1], value => {
  69 + hmget.push(['hmget', `tdk:${type}:${value}`, 'key', 'title', 'keywords', 'description']);
54 }); 70 });
55 }); 71 });
56 72
57 - if (tdkList.length) {  
58 - resData.tdkList = tdkList;  
59 - }  
60 - }); 73 + await ctx.redis.multi(hmget).execAsync().then(function(res) {
  74 + let tdkList = [];
  75 +
  76 + _.forEach(res, value => {
  77 + tdkList.push({
  78 + id: ++startId,
  79 + typeName: typeObj.name,
  80 + typeLt: typeObj.lt,
  81 + key: value[0],
  82 + title: value[1],
  83 + keywords: value[2],
  84 + description: value[3]
  85 + });
  86 + });
  87 +
  88 + if (tdkList.length) {
  89 + resData.tdkList = tdkList;
  90 + }
  91 + });
  92 + }
61 93
62 await ctx.render('action/seo_tdk', Object.assign(resData, { 94 await ctx.render('action/seo_tdk', Object.assign(resData, {
63 title: 'TDK管理', 95 title: 'TDK管理',
@@ -69,37 +101,111 @@ const tdk = { @@ -69,37 +101,111 @@ const tdk = {
69 101
70 // 添加tdk 102 // 添加tdk
71 add: async(ctx, next) => { 103 add: async(ctx, next) => {
72 - let result = {code: 500, message: '非法参数'},  
73 - typeList = ['skn', 'article', 'shop', 'url']; 104 + let result = {code: 500, message: '非法参数'};
74 105
75 // skn, article, shop, url 106 // skn, article, shop, url
76 let type = ctx.request.body.type, 107 let type = ctx.request.body.type,
77 - val = ctx.request.body.val, 108 + key = ctx.request.body.key,
78 title = ctx.request.body.title || '', 109 title = ctx.request.body.title || '',
79 keywords = ctx.request.body.keywords || '', 110 keywords = ctx.request.body.keywords || '',
80 description = ctx.request.body.description || ''; 111 description = ctx.request.body.description || '';
81 112
82 113
83 // type 不合法, 返回错误 114 // type 不合法, 返回错误
84 - if (!_.find(typeList, type)) {  
85 - ctx.response.body = result; 115 + if (!_.find(TYPE_LIST, ['type', type])) {
  116 + return ctx.response.body = result;
  117 + }
  118 +
  119 + let hashKey = type === 'url' ? md5(key) : key;
  120 +
  121 + await ctx.redis.multi([
  122 + ['lpushx', `tdk:${type}:links`, hashKey],
  123 + ['hmset', `tdk:${type}:${hashKey}`, 'key', key, 'title', title, 'keywords', keywords,
  124 + 'description', description, 'modify_time', Date.parse(new Date()) / 1000]
  125 + ]).execAsync().then(function(res) {
  126 + if (res[1]) {
  127 + if (!res[0]) {
  128 + ctx.redis.lpush(`tdk:${type}:links`, hashKey);
  129 + }
  130 + Object.assign(result, {code:200, message: 'success'});
  131 + } else {
  132 + Object.assign(result, {code:400, message: 'failed'});
  133 + }
  134 + });
  135 +
  136 + ctx.response.body = result;
  137 + },
  138 +
  139 + edit: async(ctx, next) => {
  140 + let result = {code: 500, message: '非法参数'};
  141 +
  142 + // skn, article, shop, url
  143 + let type = ctx.request.body.type,
  144 + key = ctx.request.body.key,
  145 + title = ctx.request.body.title || '',
  146 + keywords = ctx.request.body.keywords || '',
  147 + description = ctx.request.body.description || '';
  148 +
  149 + // type 不合法, 返回错误
  150 + if (!_.find(TYPE_LIST, ['type', type])) {
  151 + return ctx.response.body = result;
86 } 152 }
87 153
88 - ctx.redis.multi([  
89 - ["lpush", `tdk:${type}:links`, val],  
90 - ["hmset", `tdk:${type}:${val}`, "title", title, "keywords", keywords, "description", description] 154 + let hashKey = type === 'url' ? md5(key) : key;
  155 +
  156 + await ctx.redis.multi([
  157 + ['hmset', `tdk:${type}:${hashKey}`, 'title', title, 'keywords', keywords,
  158 + 'description', description, 'modify_time', Date.parse(new Date()) / 1000]
91 ]).execAsync().then(function(res) { 159 ]).execAsync().then(function(res) {
92 - Object.assign(result, {code:200, message: 'success'}) 160 + if (res[0]) {
  161 + Object.assign(result, {code:200, message: 'success'});
  162 + } else {
  163 + Object.assign(result, {code:400, message: 'failed'});
  164 + }
93 }); 165 });
94 166
95 ctx.response.body = result; 167 ctx.response.body = result;
96 }, 168 },
  169 + delete: async(ctx, next) => {
  170 + let result = {code: 500, message: '非法参数'};
  171 + let list = ctx.request.body.list;
  172 + let delArr = ['del']
  173 + let multiArr = [];
  174 +
  175 + // list 不合法, 返回错误
  176 + if (!list || !list.length) {
  177 + return ctx.response.body = result;
  178 + }
  179 +
  180 + _.forEach(list, value => {
  181 + let hashKey = value.type === 'url' ? md5(value.key) : value.key;
  182 +
  183 + if (!_.find(TYPE_LIST, ['type', value.type])) {
  184 + return;
  185 + }
  186 +
  187 + multiArr.push(['lrem', `tdk:${value.type}:links`, 1, hashKey]);
  188 +
  189 + delArr.push(`tdk:${value.type}:${hashKey}`)
  190 + });
  191 +
  192 + if (multiArr.length) {
  193 + multiArr.push(delArr);
  194 +
  195 + await ctx.redis.multi(multiArr).execAsync().then(function(res) {
  196 + Object.assign(result, {code:200, message: 'success'});
  197 + });
  198 + }
97 199
  200 + ctx.response.body = result;
  201 + }
98 }; 202 };
99 203
100 r.get('/', tdk.index); 204 r.get('/', tdk.index);
101 r.get('/tdk', tdk.index); 205 r.get('/tdk', tdk.index);
102 r.post('/tdk/add', tdk.add); 206 r.post('/tdk/add', tdk.add);
  207 +r.post('/tdk/edit', tdk.edit);
  208 +r.post('/tdk/delete', tdk.delete);
103 209
104 210
105 module.exports = r; 211 module.exports = r;
@@ -20,14 +20,14 @@ @@ -20,14 +20,14 @@
20 border-bottom-right-radius: 0; 20 border-bottom-right-radius: 0;
21 } 21 }
22 22
23 - .seo-tdk-page .query-form .submit-btn { 23 + .seo-tdk-page .query-form .query-submit-btn {
24 height: 39px; 24 height: 39px;
25 border-top-left-radius: 0; 25 border-top-left-radius: 0;
26 border-bottom-left-radius: 0; 26 border-bottom-left-radius: 0;
27 } 27 }
28 28
29 .seo-tdk-page .query-key { 29 .seo-tdk-page .query-key {
30 - width: 220px; 30 + width: 300px;
31 height: 39px; 31 height: 39px;
32 font-size: 14px; 32 font-size: 14px;
33 vertical-align: middle; 33 vertical-align: middle;
@@ -47,6 +47,11 @@ @@ -47,6 +47,11 @@
47 margin: 0; 47 margin: 0;
48 } 48 }
49 49
  50 + .seo-tdk-page #table-tdk th,
  51 + .seo-tdk-page #table-tdk td {
  52 + height: 36px;
  53 + }
  54 +
50 .seo-tdk-page #pop{ 55 .seo-tdk-page #pop{
51 width:500px; 56 width:500px;
52 height: 434px; 57 height: 434px;
@@ -58,6 +63,12 @@ @@ -58,6 +63,12 @@
58 margin:auto; 63 margin:auto;
59 } 64 }
60 65
  66 + .seo-tdk-page #pop .cover-title {
  67 + position: absolute;
  68 + background: #fff;
  69 + margin-top: -34px;
  70 + }
  71 +
61 .seo-tdk-page #pop .control-label { 72 .seo-tdk-page #pop .control-label {
62 width: 60px; 73 width: 60px;
63 text-align: right; 74 text-align: right;
@@ -87,6 +98,10 @@ @@ -87,6 +98,10 @@
87 .seo-tdk-page #pop .controls { 98 .seo-tdk-page #pop .controls {
88 display: inline-block; 99 display: inline-block;
89 } 100 }
  101 +
  102 + .seo-tdk-page #pop .err-tip {
  103 + color: #a94442;
  104 + }
90 </style> 105 </style>
91 106
92 <div class="pageheader"> 107 <div class="pageheader">
@@ -110,24 +125,27 @@ @@ -110,24 +125,27 @@
110 <div class="contentpanel seo-tdk-page" style="padding-bottom:0;"> 125 <div class="contentpanel seo-tdk-page" style="padding-bottom:0;">
111 <div class="panel panel-default"> 126 <div class="panel panel-default">
112 <div class="panel-body"> 127 <div class="panel-body">
113 - <label style="margin-right:20px;"><input type="checkbox" style="margin-right:5px;">全选</label> 128 + <label style="margin-right:20px;"><input type="checkbox" id="check-all" style="margin-right:5px;">全选</label>
114 <a data-toggle="modal" href="#pop" class="btn btn-default" style="margin-right:10px;">增加</a> 129 <a data-toggle="modal" href="#pop" class="btn btn-default" style="margin-right:10px;">增加</a>
115 - <button class="btn btn-default" type="submit">删除</button>  
116 -  
117 - <div class="input-append query-form pull-right">  
118 - <div class="btn-group">  
119 - <button class="btn dropdown-toggle" data-toggle="dropdown" data-type="{{type}}">  
120 - {{typeName}}  
121 - <span class="caret"></span>  
122 - </button>  
123 - <ul class="dropdown-menu">  
124 - {{# typeList}}  
125 - <li><a href="?type={{type}}">{{name}}</a></li>  
126 - {{/ typeList}}  
127 - </ul>  
128 - </div>  
129 - <input class="span2 query-key" type="text">  
130 - <button class="btn submit-btn" type="button">搜索</button> 130 + <button class="btn btn-default delete-all" type="submit">删除</button>
  131 +
  132 + <div class="input-append pull-right">
  133 + <form id="query-form" action="/seo/tdk" class="query-form" method="get">
  134 + <div class="btn-group">
  135 + <button class="btn dropdown-toggle" data-toggle="dropdown">
  136 + {{typeName}}
  137 + <span class="caret"></span>
  138 + </button>
  139 + <ul class="dropdown-menu">
  140 + {{# typeList}}
  141 + <li><a href="?type={{type}}">{{name}}</a></li>
  142 + {{/ typeList}}
  143 + </ul>
  144 + </div>
  145 + <input type="hidden" name="type" value="{{type}}">
  146 + <input class="span2 query-key" type="text" name="query" value="{{query}}">
  147 + <button class="btn query-submit-btn" type="submit">搜索</button>
  148 + </form>
131 </div> 149 </div>
132 </div> 150 </div>
133 </div> 151 </div>
@@ -155,15 +173,21 @@ @@ -155,15 +173,21 @@
155 <td>{{keywords}}</td> 173 <td>{{keywords}}</td>
156 <td>{{description}}</td> 174 <td>{{description}}</td>
157 <td class="text-center"> 175 <td class="text-center">
158 - <span>编辑</span>  
159 - <span>删除</span> 176 + <a href="#pop" class="edit-tdk-btn" data-toggle="modal">编辑</a>
  177 + <a href="javascript:;" class="del-tdk-btn">删除</a>
160 </td> 178 </td>
161 </tr> 179 </tr>
162 {{/each}} 180 {{/each}}
  181 + {{#unless tdkList}}
  182 + <tr>
  183 + <td class="text-center" colspan="6">暂无数据</td>
  184 + </tr>
  185 + {{/unless}}
163 </tbody> 186 </tbody>
164 </table> 187 </table>
165 {{# pager}} 188 {{# pager}}
166 <div class="text-right"> 189 <div class="text-right">
  190 + {{#if pages}}
167 <ul class="pagination"> 191 <ul class="pagination">
168 {{# prePage}} 192 {{# prePage}}
169 <li><a href="{{url}}">上一页</a></li> 193 <li><a href="{{url}}">上一页</a></li>
@@ -175,6 +199,7 @@ @@ -175,6 +199,7 @@
175 <li><a href="{{url}}">下一页</a></li> 199 <li><a href="{{url}}">下一页</a></li>
176 {{/ nextPage}} 200 {{/ nextPage}}
177 </ul> 201 </ul>
  202 + {{/if}}
178 </div> 203 </div>
179 {{/ pager}} 204 {{/ pager}}
180 </div> 205 </div>
@@ -182,50 +207,206 @@ @@ -182,50 +207,206 @@
182 207
183 <div id="pop" class="modal fade in" style="display: none;"> 208 <div id="pop" class="modal fade in" style="display: none;">
184 <div class="modal-header"> 209 <div class="modal-header">
185 - <a class="close" data-dismiss="modal">×</a> 210 + <a class="close clear-input" data-dismiss="modal">×</a>
186 <h4>添加TDK</h4> 211 <h4>添加TDK</h4>
  212 + <h4 class="cover-title"></h4>
187 </div> 213 </div>
188 <div class="modal-body"> 214 <div class="modal-body">
189 - <ul>  
190 - <li>  
191 - <label class="control-label" for="select-type">类型:</label>  
192 - <div class="controls">  
193 - <select id="select-type">  
194 - {{# typeList}}  
195 - <option value="{{type}}">{{name}}</option>  
196 - {{/ typeList}}  
197 - </select>  
198 - </div>  
199 - </li>  
200 - <li>  
201 - <label class="control-label" for="input-key">SKN:</label>  
202 - <div class="controls">  
203 - <input type="text" id="input-key">  
204 - </div>  
205 - </li>  
206 - <li>  
207 - <label class="control-label" for="input-title">标题:</label>  
208 - <div class="controls">  
209 - <input type="text" id="input-title" class="full-w">  
210 - </div>  
211 - </li>  
212 - <li>  
213 - <label class="control-label" for="input-keywords">关键词:</label>  
214 - <div class="controls">  
215 - <input type="text" id="input-keywords" class="full-w">  
216 - </div>  
217 - </li>  
218 - <li>  
219 - <label class="control-label" for="input-description">描述:</label>  
220 - <div class="controls">  
221 - <textarea id="input-description" class="full-w"></textarea>  
222 - </div>  
223 - </li>  
224 - </ul> 215 + <ul>
  216 + <li>
  217 + <label class="control-label" for="select-type">类型:</label>
  218 + <div class="controls">
  219 + <select id="select-type">
  220 + {{# typeList}}
  221 + <option value="{{type}}">{{name}}</option>
  222 + {{/ typeList}}
  223 + </select>
  224 + </div>
  225 + </li>
  226 + <li>
  227 + <label class="control-label key-label" for="input-key">SKN:</label>
  228 + <div class="controls">
  229 + <input type="text" id="input-key">
  230 + </div>
  231 + </li>
  232 + <li>
  233 + <label class="control-label" for="input-title">标题:</label>
  234 + <div class="controls">
  235 + <input type="text" id="input-title" class="full-w">
  236 + </div>
  237 + </li>
  238 + <li>
  239 + <label class="control-label" for="input-keywords">关键词:</label>
  240 + <div class="controls">
  241 + <input type="text" id="input-keywords" class="full-w">
  242 + </div>
  243 + </li>
  244 + <li>
  245 + <label class="control-label" for="input-description">描述:</label>
  246 + <div class="controls">
  247 + <textarea id="input-description" class="full-w"></textarea>
  248 + </div>
  249 + </li>
  250 + </ul>
225 </div> 251 </div>
226 <div class="modal-footer"> 252 <div class="modal-footer">
227 - <a class="btn">关闭</a>  
228 - <a class="btn btn-primary">确定</a> 253 + <span class="err-tip"></span>
  254 + <a class="btn clear-input close-pop-btn" data-dismiss="modal">关闭</a>
  255 + <a class="btn btn-primary sure-btn">确定</a>
229 </div> 256 </div>
230 </div> 257 </div>
231 </div> 258 </div>
  259 +
  260 +<script>
  261 + var pop = {
  262 + ltList: {
  263 + skn: 'SKN:',
  264 + article: 'ID:',
  265 + shop: 'ShopId:',
  266 + url: 'URL:'
  267 + },
  268 + init: function() {
  269 + var that = this;
  270 +
  271 + this.$base = $('#pop');
  272 + this.$popTitle = $('.cover-title', this.$base);
  273 + this.$keyLabel = $('.key-label', this.$base);
  274 + this.$type = $('#select-type', this.$base);
  275 + this.$key = $('#input-key', this.$base);
  276 + this.$title = $('#input-title', this.$base);
  277 + this.$keywords = $('#input-keywords', this.$base);
  278 + this.$description = $('#input-description', this.$base);
  279 +
  280 + this.$closeBtn = $('.close-pop-btn', this.$base);
  281 + this.$errTip = $('.err-tip', this.$base);
  282 +
  283 + this.$base.on('change', '#select-type', function() {
  284 + that.$keyLabel.text(that.ltList[$(this).val()] || that.ltList.skn);
  285 + }).on('click', '.clear-input', function() {
  286 + that.clearInput();
  287 + }).on('click', '.sure-btn', function() {
  288 + var data;
  289 +
  290 + if (that.saving) {
  291 + return;
  292 + }
  293 +
  294 + data = that.packReqData();
  295 +
  296 + if (!data) {
  297 + that.$errTip.text('请填写完整tdk信息');
  298 + return;
  299 + }
  300 +
  301 + that.saving = true;
  302 +
  303 + $.ajax({
  304 + url: that.editInfo ? '/seo/tdk/edit' : '/seo/tdk/add',
  305 + type: 'POST',
  306 + data: data,
  307 + }).done(function(res) {
  308 + if (res.code === 200) {
  309 + history.go(0);
  310 + }
  311 + }).always(function() {
  312 + that.saving = false;
  313 + });
  314 + });
  315 + },
  316 + clearInput: function() {
  317 + this.editInfo = false;
  318 + this.$popTitle.empty();
  319 + this.$errTip.empty();
  320 + this.$type.val('skn').change();
  321 + this.$type.removeAttr('disabled')
  322 + this.$key.removeAttr('readonly');
  323 + $('input, textarea', this.$base).val('');
  324 + },
  325 + fillInput(info) {
  326 + if (info) {
  327 + this.editInfo = info;
  328 + this.$popTitle.text('编辑TDK');
  329 + this.$type.val(info.type).change().attr('disabled', true);
  330 + this.$key.attr('readonly', 'readonly').val(info.key);
  331 + this.$title.val(info.title);
  332 + this.$keywords.val(info.keywords);
  333 + this.$description.val(info.description);
  334 + }
  335 + this.$base.show();
  336 + },
  337 + packReqData() {
  338 + var data = {
  339 + type: this.$type.val(),
  340 + key: $.trim(this.$key.val()),
  341 + title: $.trim(this.$title.val()),
  342 + keywords: $.trim(this.$keywords.val()),
  343 + description: $.trim(this.$description.val())
  344 + };
  345 + var i;
  346 +
  347 + if (this.editInfo) {
  348 + data.type = this.editInfo.type;
  349 + data.key = this.editInfo.key;
  350 + }
  351 +
  352 + for (i in data) {
  353 + if (data.hasOwnProperty(i) && !data[i]) {
  354 + return false;
  355 + }
  356 + }
  357 +
  358 + return data;
  359 + },
  360 + close() {
  361 + this.$closeBtn.trigger('click');
  362 + }
  363 + };
  364 +
  365 + $(function(){
  366 + var $checkboxs = $('#table-tdk :checkbox');
  367 + var deling;
  368 +
  369 + pop.init();
  370 +
  371 + function delTdk(data) {
  372 + deling = true;
  373 +
  374 + $.ajax({
  375 + url: '/seo/tdk/delete',
  376 + type: 'POST',
  377 + data: {list: data},
  378 + }).done(function(res) {
  379 + if (res.code === 200) {
  380 + history.go(0);
  381 + }
  382 + }).always(function() {
  383 + deling = false;
  384 + });
  385 + };
  386 +
  387 + $('#check-all').click(function() {
  388 + if ($(this).attr('checked') === 'checked') {
  389 + $checkboxs.attr('checked', 'checked');
  390 + } else {
  391 + $checkboxs.removeAttr('checked');
  392 + }
  393 + });
  394 +
  395 + $('.delete-all').click(function() {
  396 + var arr = [];
  397 +
  398 + $('#table-tdk :checkbox:checked').each(function(){
  399 + arr.push($(this).closest('tr').data());
  400 + });
  401 + arr.length ? delTdk(arr) : false;
  402 + });
  403 +
  404 + $('#table-tdk').on('click', '.edit-tdk-btn', function(e) {
  405 + pop.fillInput($(e.target).closest('tr').data());
  406 + }).on('click', '.del-tdk-btn', function(e) {
  407 + var data = $(e.target).closest('tr').data();
  408 +
  409 + delTdk([data]);
  410 + });
  411 + });
  412 +</script>