##// END OF EJS Templates
front-end: use At.js for MentionsAutoComplete...
domruf -
r7387:2e7ffb75 default
parent child Browse files
Show More
@@ -1,50 +1,52 b''
1 syntax: glob
1 syntax: glob
2 *.pyc
2 *.pyc
3 *.swp
3 *.swp
4 *.sqlite
4 *.sqlite
5 *.tox
5 *.tox
6 *.egg-info
6 *.egg-info
7 *.egg
7 *.egg
8 *.mo
8 *.mo
9 .eggs/
9 .eggs/
10 tarballcache/
10 tarballcache/
11
11
12 syntax: regexp
12 syntax: regexp
13 ^rcextensions
13 ^rcextensions
14 ^build
14 ^build
15 ^dist/
15 ^dist/
16 ^docs/build/
16 ^docs/build/
17 ^docs/_build/
17 ^docs/_build/
18 ^data$
18 ^data$
19 ^sql_dumps/
19 ^sql_dumps/
20 ^\.settings$
20 ^\.settings$
21 ^\.project$
21 ^\.project$
22 ^\.pydevproject$
22 ^\.pydevproject$
23 ^\.coverage$
23 ^\.coverage$
24 ^kallithea/front-end/node_modules$
24 ^kallithea/front-end/node_modules$
25 ^kallithea/front-end/package-lock\.json$
25 ^kallithea/front-end/package-lock\.json$
26 ^kallithea/front-end/tmp$
26 ^kallithea/front-end/tmp$
27 ^kallithea/public/codemirror$
27 ^kallithea/public/codemirror$
28 ^kallithea/public/css/select2-spinner\.gif$
28 ^kallithea/public/css/select2-spinner\.gif$
29 ^kallithea/public/css/select2\.png$
29 ^kallithea/public/css/select2\.png$
30 ^kallithea/public/css/select2x2\.png$
30 ^kallithea/public/css/select2x2\.png$
31 ^kallithea/public/css/style\.css$
31 ^kallithea/public/css/style\.css$
32 ^kallithea/public/css/style\.css\.map$
32 ^kallithea/public/css/style\.css\.map$
33 ^kallithea/public/js/bootstrap\.js$
33 ^kallithea/public/js/bootstrap\.js$
34 ^kallithea/public/js/dataTables\.bootstrap\.js$
34 ^kallithea/public/js/dataTables\.bootstrap\.js$
35 ^kallithea/public/js/jquery\.atwho\.min\.js$
36 ^kallithea/public/js/jquery\.caret\.min\.js$
35 ^kallithea/public/js/jquery\.dataTables\.js$
37 ^kallithea/public/js/jquery\.dataTables\.js$
36 ^kallithea/public/js/jquery\.flot\.js$
38 ^kallithea/public/js/jquery\.flot\.js$
37 ^kallithea/public/js/jquery\.flot\.selection\.js$
39 ^kallithea/public/js/jquery\.flot\.selection\.js$
38 ^kallithea/public/js/jquery\.flot\.time\.js$
40 ^kallithea/public/js/jquery\.flot\.time\.js$
39 ^kallithea/public/js/jquery\.min\.js$
41 ^kallithea/public/js/jquery\.min\.js$
40 ^kallithea/public/js/select2\.js$
42 ^kallithea/public/js/select2\.js$
41 ^theme\.less$
43 ^theme\.less$
42 ^kallithea\.db$
44 ^kallithea\.db$
43 ^test\.db$
45 ^test\.db$
44 ^Kallithea\.egg-info$
46 ^Kallithea\.egg-info$
45 ^my\.ini$
47 ^my\.ini$
46 ^fabfile.py
48 ^fabfile.py
47 ^\.idea$
49 ^\.idea$
48 ^\.cache$
50 ^\.cache$
49 ^\.pytest_cache$
51 ^\.pytest_cache$
50 /__pycache__$
52 /__pycache__$
@@ -1,311 +1,343 b''
1 Kallithea License
1 Kallithea License
2 =================
2 =================
3
3
4 Kallithea as a whole is copyrighted by various authors and is licensed under
4 Kallithea as a whole is copyrighted by various authors and is licensed under
5 the terms of the GNU General Public License, version 3 (GPLv3), which is a
5 the terms of the GNU General Public License, version 3 (GPLv3), which is a
6 license published by the Free Software Foundation,
6 license published by the Free Software Foundation,
7 Inc. [A copy of GPLv3](/COPYING) is included herein.
7 Inc. [A copy of GPLv3](/COPYING) is included herein.
8
8
9 Some individual files have copyright notices and those who offer changes to
9 Some individual files have copyright notices and those who offer changes to
10 those files should update the copyright notices in those specific files if
10 those files should update the copyright notices in those specific files if
11 they so chose.
11 they so chose.
12
12
13 However, the definitive list of copyright holders for this project is kept in
13 However, the definitive list of copyright holders for this project is kept in
14 [the about page template](kallithea/templates/about.html) so that it is
14 [the about page template](kallithea/templates/about.html) so that it is
15 displayed appropriately when Kallithea is installed. This is the most
15 displayed appropriately when Kallithea is installed. This is the most
16 important place to update copyright notices.
16 important place to update copyright notices.
17
17
18 Third-Party Code Incorporated in Kallithea
18 Third-Party Code Incorporated in Kallithea
19 ==========================================
19 ==========================================
20
20
21 Various third-party code under GPLv3-compatible licenses is included as part
21 Various third-party code under GPLv3-compatible licenses is included as part
22 of Kallithea.
22 of Kallithea.
23
23
24
24
25 Alembic
25 Alembic
26 -------
26 -------
27
27
28 Kallithea incorporates an [Alembic](http://alembic.zzzcomputing.com/en/latest/)
28 Kallithea incorporates an [Alembic](http://alembic.zzzcomputing.com/en/latest/)
29 "migration environment" in `kallithea/alembic`, portions of which is:
29 "migration environment" in `kallithea/alembic`, portions of which is:
30
30
31 Copyright © 2009-2016 by Michael Bayer.
31 Copyright © 2009-2016 by Michael Bayer.
32 Alembic is a trademark of Michael Bayer.
32 Alembic is a trademark of Michael Bayer.
33
33
34 and licensed under the MIT-permissive license, which is
34 and licensed under the MIT-permissive license, which is
35 [included in this distribution](MIT-Permissive-License.txt).
35 [included in this distribution](MIT-Permissive-License.txt).
36
36
37
37
38 Bootstrap
38 Bootstrap
39 ---------
39 ---------
40
40
41 Kallithea uses the web framework called
41 Kallithea uses the web framework called
42 [Bootstrap](http://getbootstrap.com/), which is:
42 [Bootstrap](http://getbootstrap.com/), which is:
43
43
44 Copyright © 2011-2016 Twitter, Inc.
44 Copyright © 2011-2016 Twitter, Inc.
45
45
46 and licensed under the MIT-permissive license, which is
46 and licensed under the MIT-permissive license, which is
47 [included in this distribution](MIT-Permissive-License.txt).
47 [included in this distribution](MIT-Permissive-License.txt).
48
48
49 It is not distributed with Kallithea, but will be downloaded
49 It is not distributed with Kallithea, but will be downloaded
50 using the ''kallithea-cli front-end-build'' command.
50 using the ''kallithea-cli front-end-build'' command.
51
51
52
52
53
53
54 Codemirror
54 Codemirror
55 ----------
55 ----------
56
56
57 Kallithea uses the Javascript system called
57 Kallithea uses the Javascript system called
58 [Codemirror](http://codemirror.net/), version 4.7.0, which is primarily:
58 [Codemirror](http://codemirror.net/), version 4.7.0, which is primarily:
59
59
60 Copyright &copy; 2013-2014 by Marijn Haverbeke <marijnh@gmail.com>
60 Copyright &copy; 2013-2014 by Marijn Haverbeke <marijnh@gmail.com>
61
61
62 and licensed under the MIT-permissive license, which is
62 and licensed under the MIT-permissive license, which is
63 [included in this distribution](MIT-Permissive-License.txt).
63 [included in this distribution](MIT-Permissive-License.txt).
64
64
65 Additional files from upstream Codemirror are copyrighted by various authors
65 Additional files from upstream Codemirror are copyrighted by various authors
66 and licensed under other permissive licenses.
66 and licensed under other permissive licenses.
67
67
68 It is not distributed with Kallithea, but will be downloaded
68 It is not distributed with Kallithea, but will be downloaded
69 using the ''kallithea-cli front-end-build'' command.
69 using the ''kallithea-cli front-end-build'' command.
70
70
71
71
72
72
73 jQuery
73 jQuery
74 ------
74 ------
75
75
76 Kallithea uses the Javascript system called
76 Kallithea uses the Javascript system called
77 [jQuery](http://jquery.org/).
77 [jQuery](http://jquery.org/).
78
78
79 It is Copyright 2013 jQuery Foundation and other contributors http://jquery.com/ and is under an
79 It is Copyright 2013 jQuery Foundation and other contributors http://jquery.com/ and is under an
80 [MIT-permissive license](MIT-Permissive-License.txt).
80 [MIT-permissive license](MIT-Permissive-License.txt).
81
81
82 It is not distributed with Kallithea, but will be downloaded
82 It is not distributed with Kallithea, but will be downloaded
83 using the ''kallithea-cli front-end-build'' command.
83 using the ''kallithea-cli front-end-build'' command.
84
84
85
85
86
86
87 At.js
88 -----
89
90 Kallithea uses the Javascript system called
91 [At.js](http://ichord.github.com/At.js),
92 which can be found together with its Corresponding Source in
93 https://github.com/ichord/At.js at tag v1.5.4.
94
95 It is Copyright 2013 chord.luo@gmail.com and is under an
96 [MIT-permissive license](MIT-Permissive-License.txt).
97
98 It is not distributed with Kallithea, but will be downloaded
99 using the ''kallithea-cli front-end-build'' command.
100
101
102
103 Caret.js
104 --------
105
106 Kallithea uses the Javascript system called
107 [Caret.js](http://ichord.github.com/Caret.js/),
108 which can be found together with its Corresponding Source in
109 https://github.com/ichord/Caret.js at tag v0.3.1.
110
111 It is Copyright 2013 chord.luo@gmail.com and is under an
112 [MIT-permissive license](MIT-Permissive-License.txt).
113
114 It is not distributed with Kallithea, but will be downloaded
115 using the ''kallithea-cli front-end-build'' command.
116
117
118
87 DataTables
119 DataTables
88 ----------
120 ----------
89
121
90 Kallithea uses the Javascript system called
122 Kallithea uses the Javascript system called
91 [DataTables](http://www.datatables.net/).
123 [DataTables](http://www.datatables.net/).
92
124
93 It is Copyright 2008-2015 SpryMedia Ltd. and is under an
125 It is Copyright 2008-2015 SpryMedia Ltd. and is under an
94 [MIT-permissive license](MIT-Permissive-License.txt).
126 [MIT-permissive license](MIT-Permissive-License.txt).
95
127
96 It is not distributed with Kallithea, but will be downloaded
128 It is not distributed with Kallithea, but will be downloaded
97 using the ''kallithea-cli front-end-build'' command.
129 using the ''kallithea-cli front-end-build'' command.
98
130
99
131
100
132
101 Mergely
133 Mergely
102 -------
134 -------
103
135
104 Kallithea incorporates some code from the Javascript system called
136 Kallithea incorporates some code from the Javascript system called
105 [Mergely](http://www.mergely.com/), version 3.3.9.
137 [Mergely](http://www.mergely.com/), version 3.3.9.
106 [Mergely's license](http://www.mergely.com/license.php), a
138 [Mergely's license](http://www.mergely.com/license.php), a
107 [copy of which is included in this repository](LICENSE-MERGELY.html),
139 [copy of which is included in this repository](LICENSE-MERGELY.html),
108 is (GPL|LGPL|MPL). Kallithea as GPLv3'd project chooses the GPL arm of that
140 is (GPL|LGPL|MPL). Kallithea as GPLv3'd project chooses the GPL arm of that
109 tri-license.
141 tri-license.
110
142
111
143
112
144
113 Select2
145 Select2
114 -------
146 -------
115
147
116 Kallithea uses the Javascript system called
148 Kallithea uses the Javascript system called
117 [Select2](http://ivaynberg.github.io/select2/), which is:
149 [Select2](http://ivaynberg.github.io/select2/), which is:
118
150
119 Copyright 2012 Igor Vaynberg (and probably others)
151 Copyright 2012 Igor Vaynberg (and probably others)
120
152
121 and is licensed [under the following license](https://github.com/ivaynberg/select2/blob/master/LICENSE):
153 and is licensed [under the following license](https://github.com/ivaynberg/select2/blob/master/LICENSE):
122
154
123 > This software is licensed under the Apache License, Version 2.0 (the
155 > This software is licensed under the Apache License, Version 2.0 (the
124 > "Apache License") or the GNU General Public License version 2 (the "GPL
156 > "Apache License") or the GNU General Public License version 2 (the "GPL
125 > License"). You may choose either license to govern your use of this
157 > License"). You may choose either license to govern your use of this
126 > software only upon the condition that you accept all of the terms of either
158 > software only upon the condition that you accept all of the terms of either
127 > the Apache License or the GPL License.
159 > the Apache License or the GPL License.
128
160
129 A [copy of the Apache License 2.0](Apache-License-2.0.txt) is also included
161 A [copy of the Apache License 2.0](Apache-License-2.0.txt) is also included
130 in this distribution.
162 in this distribution.
131
163
132 Kallithea will take the Apache license fork of the dual license, since
164 Kallithea will take the Apache license fork of the dual license, since
133 Kallithea is GPLv3'd.
165 Kallithea is GPLv3'd.
134
166
135 It is not distributed with Kallithea, but will be downloaded
167 It is not distributed with Kallithea, but will be downloaded
136 using the ''kallithea-cli front-end-build'' command.
168 using the ''kallithea-cli front-end-build'' command.
137
169
138
170
139
171
140 Select2-Bootstrap-CSS
172 Select2-Bootstrap-CSS
141 ---------------------
173 ---------------------
142
174
143 Kallithea uses some CSS from a system called
175 Kallithea uses some CSS from a system called
144 [Select2-bootstrap-css](https://github.com/t0m/select2-bootstrap-css), which
176 [Select2-bootstrap-css](https://github.com/t0m/select2-bootstrap-css), which
145 is:
177 is:
146
178
147 Copyright &copy; 2013 Tom Terrace (and likely others)
179 Copyright &copy; 2013 Tom Terrace (and likely others)
148
180
149 and licensed under the MIT-permissive license, which is
181 and licensed under the MIT-permissive license, which is
150 [included in this distribution](MIT-Permissive-License.txt).
182 [included in this distribution](MIT-Permissive-License.txt).
151
183
152 It is not distributed with Kallithea, but will be downloaded
184 It is not distributed with Kallithea, but will be downloaded
153 using the ''kallithea-cli front-end-build'' command.
185 using the ''kallithea-cli front-end-build'' command.
154
186
155
187
156
188
157 History.js
189 History.js
158 ----------
190 ----------
159
191
160 Kallithea incorporates some CSS from a system called History.js, which is
192 Kallithea incorporates some CSS from a system called History.js, which is
161
193
162 Copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
194 Copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
163
195
164 Redistribution and use in source and binary forms, with or without
196 Redistribution and use in source and binary forms, with or without
165 modification, are permitted provided that the following conditions are met:
197 modification, are permitted provided that the following conditions are met:
166
198
167 1. Redistributions of source code must retain the above copyright notice,
199 1. Redistributions of source code must retain the above copyright notice,
168 this list of conditions and the following disclaimer.
200 this list of conditions and the following disclaimer.
169
201
170 2. Redistributions in binary form must reproduce the above copyright notice,
202 2. Redistributions in binary form must reproduce the above copyright notice,
171 this list of conditions and the following disclaimer in the documentation
203 this list of conditions and the following disclaimer in the documentation
172 and/or other materials provided with the distribution.
204 and/or other materials provided with the distribution.
173
205
174 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
206 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
175 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
207 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
176 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
208 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
177 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
209 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
178 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
210 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
179 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
211 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
180 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
212 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
181 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
213 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
182 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
214 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
183 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
215 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
184 POSSIBILITY OF SUCH DAMAGE.
216 POSSIBILITY OF SUCH DAMAGE.
185
217
186
218
187
219
188 YUI
220 YUI
189 ---
221 ---
190
222
191 Kallithea incorporates parts of the Javascript system called
223 Kallithea incorporates parts of the Javascript system called
192 [YUI 2 β€” Yahoo! User Interface Library](http://yui.github.io/yui2/docs/yui_2.9.0_full/),
224 [YUI 2 β€” Yahoo! User Interface Library](http://yui.github.io/yui2/docs/yui_2.9.0_full/),
193 which is made available under the [BSD License](http://yuilibrary.com/license/):
225 which is made available under the [BSD License](http://yuilibrary.com/license/):
194
226
195 Copyright &copy; 2013 Yahoo! Inc. All rights reserved.
227 Copyright &copy; 2013 Yahoo! Inc. All rights reserved.
196
228
197 Redistribution and use of this software in source and binary forms, with or
229 Redistribution and use of this software in source and binary forms, with or
198 without modification, are permitted provided that the following conditions are
230 without modification, are permitted provided that the following conditions are
199 met:
231 met:
200
232
201 * Redistributions of source code must retain the above copyright notice, this
233 * Redistributions of source code must retain the above copyright notice, this
202 list of conditions and the following disclaimer.
234 list of conditions and the following disclaimer.
203
235
204 * Redistributions in binary form must reproduce the above copyright notice,
236 * Redistributions in binary form must reproduce the above copyright notice,
205 this list of conditions and the following disclaimer in the documentation
237 this list of conditions and the following disclaimer in the documentation
206 and/or other materials provided with the distribution.
238 and/or other materials provided with the distribution.
207
239
208 * Neither the name of Yahoo! Inc. nor the names of YUI's contributors may be
240 * Neither the name of Yahoo! Inc. nor the names of YUI's contributors may be
209 used to endorse or promote products derived from this software without
241 used to endorse or promote products derived from this software without
210 specific prior written permission of Yahoo! Inc.
242 specific prior written permission of Yahoo! Inc.
211
243
212 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
244 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
213 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
245 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
214 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
246 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
215 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
247 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
216 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
248 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
217 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
249 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
218 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
250 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
219 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
251 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
220 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
252 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
221 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
253 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
222
254
223
255
224 Kallithea includes a minified version of YUI 2.9. To build yui.2.9.js:
256 Kallithea includes a minified version of YUI 2.9. To build yui.2.9.js:
225
257
226 git clone https://github.com/yui/builder
258 git clone https://github.com/yui/builder
227 git clone https://github.com/yui/yui2
259 git clone https://github.com/yui/yui2
228 cd yui2/
260 cd yui2/
229 git checkout hudson-yui2-2800
261 git checkout hudson-yui2-2800
230 ln -sf JumpToPageDropDown.js src/paginator/js/JumpToPageDropdown.js # work around inconsistent casing
262 ln -sf JumpToPageDropDown.js src/paginator/js/JumpToPageDropdown.js # work around inconsistent casing
231 rm -f tmp.js
263 rm -f tmp.js
232 for m in yahoo event dom animation datasource autocomplete event-delegate; do
264 for m in yahoo event dom animation datasource autocomplete event-delegate; do
233 rm -f build/$m/$m.js
265 rm -f build/$m/$m.js
234 ( cd src/$m && ant build deploybuild ) && sed -e 's,@VERSION@,2.9.0,g' -e 's,@BUILD@,2800,g' build/$m/$m.js >> tmp.js
266 ( cd src/$m && ant build deploybuild ) && sed -e 's,@VERSION@,2.9.0,g' -e 's,@BUILD@,2800,g' build/$m/$m.js >> tmp.js
235 done
267 done
236 java -jar ../builder/componentbuild/lib/yuicompressor/yuicompressor-2.4.4.jar tmp.js -o yui.2.9.js
268 java -jar ../builder/componentbuild/lib/yuicompressor/yuicompressor-2.4.4.jar tmp.js -o yui.2.9.js
237
269
238 In compliance with GPLv3 the Corresponding Source for this Object Code is made
270 In compliance with GPLv3 the Corresponding Source for this Object Code is made
239 available on
271 available on
240 [https://kallithea-scm.org/repos/mirror](https://kallithea-scm.org/repos/mirror).
272 [https://kallithea-scm.org/repos/mirror](https://kallithea-scm.org/repos/mirror).
241
273
242
274
243
275
244 YUI Flot
276 YUI Flot
245 --------
277 --------
246
278
247 Kallithea incorporates some CSS from a system called
279 Kallithea incorporates some CSS from a system called
248 [Flot](http://code.google.com/p/flot/), which is:
280 [Flot](http://code.google.com/p/flot/), which is:
249
281
250 Copyright 2006 Google Inc.
282 Copyright 2006 Google Inc.
251
283
252 Licensed under the Apache License, Version 2.0 (the "License");
284 Licensed under the Apache License, Version 2.0 (the "License");
253 you may not use this file except in compliance with the License.
285 you may not use this file except in compliance with the License.
254
286
255 A [copy of the Apache License 2.0](Apache-License-2.0.txt) is also included
287 A [copy of the Apache License 2.0](Apache-License-2.0.txt) is also included
256 in this distribution.
288 in this distribution.
257
289
258
290
259
291
260 Flot
292 Flot
261 ----
293 ----
262
294
263 Kallithea uses some parts of a Javascript system called
295 Kallithea uses some parts of a Javascript system called
264 [Flot](http://www.flotcharts.org/), which is:
296 [Flot](http://www.flotcharts.org/), which is:
265
297
266 Copyright (c) 2007-2014 IOLA and Ole Laursen
298 Copyright (c) 2007-2014 IOLA and Ole Laursen
267
299
268 Permission is hereby granted, free of charge, to any person
300 Permission is hereby granted, free of charge, to any person
269 obtaining a copy of this software and associated documentation
301 obtaining a copy of this software and associated documentation
270 files (the "Software"), to deal in the Software without
302 files (the "Software"), to deal in the Software without
271 restriction, including without limitation the rights to use,
303 restriction, including without limitation the rights to use,
272 copy, modify, merge, publish, distribute, sublicense, and/or sell
304 copy, modify, merge, publish, distribute, sublicense, and/or sell
273 copies of the Software, and to permit persons to whom the
305 copies of the Software, and to permit persons to whom the
274 Software is furnished to do so, subject to the following
306 Software is furnished to do so, subject to the following
275 conditions:
307 conditions:
276
308
277 The above copyright notice and this permission notice shall be
309 The above copyright notice and this permission notice shall be
278 included in all copies or substantial portions of the Software.
310 included in all copies or substantial portions of the Software.
279
311
280 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
312 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
281 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
313 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
282 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
314 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
283 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
315 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
284 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
316 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
285 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
317 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
286 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
318 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
287 OTHER DEALINGS IN THE SOFTWARE.
319 OTHER DEALINGS IN THE SOFTWARE.
288
320
289 It is not distributed with Kallithea, but will be downloaded
321 It is not distributed with Kallithea, but will be downloaded
290 using the ''kallithea-cli front-end-build'' command.
322 using the ''kallithea-cli front-end-build'' command.
291
323
292
324
293
325
294 Icon fonts
326 Icon fonts
295 ----------
327 ----------
296
328
297 Kallithea incorporates subsets of both
329 Kallithea incorporates subsets of both
298 [Font Awesome](http://fontawesome.io) and
330 [Font Awesome](http://fontawesome.io) and
299 [GitHub Octicons](https://octicons.github.com) for icons. Font Awesome is:
331 [GitHub Octicons](https://octicons.github.com) for icons. Font Awesome is:
300
332
301 Copyright (c) 2016, Dave Gandy
333 Copyright (c) 2016, Dave Gandy
302
334
303 Octicons is:
335 Octicons is:
304
336
305 Copyright (c) 2012-2014 GitHub
337 Copyright (c) 2012-2014 GitHub
306
338
307 These two sets are distributed under [SIL OFL 1.1](http://scripts.sil.org/OFL)
339 These two sets are distributed under [SIL OFL 1.1](http://scripts.sil.org/OFL)
308 and have been combined into one font called "kallithea."
340 and have been combined into one font called "kallithea."
309
341
310
342
311 EOF
343 EOF
@@ -1,108 +1,110 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
14
15 import click
15 import click
16 import kallithea.bin.kallithea_cli_base as cli_base
16 import kallithea.bin.kallithea_cli_base as cli_base
17
17
18 import os
18 import os
19 import shutil
19 import shutil
20 import subprocess
20 import subprocess
21 import json
21 import json
22
22
23 import kallithea
23 import kallithea
24
24
25 @cli_base.register_command()
25 @cli_base.register_command()
26 @click.option('--install-deps/--no-install-deps', default=True,
26 @click.option('--install-deps/--no-install-deps', default=True,
27 help='Skip installation of dependencies, via "npm".')
27 help='Skip installation of dependencies, via "npm".')
28 @click.option('--generate/--no-generate', default=True,
28 @click.option('--generate/--no-generate', default=True,
29 help='Skip generation of front-end files.')
29 help='Skip generation of front-end files.')
30 def front_end_build(install_deps, generate):
30 def front_end_build(install_deps, generate):
31 """Build the front-end.
31 """Build the front-end.
32
32
33 Install required dependencies for the front-end and generate the necessary
33 Install required dependencies for the front-end and generate the necessary
34 files. This step is complementary to any 'pip install' step which only
34 files. This step is complementary to any 'pip install' step which only
35 covers Python dependencies.
35 covers Python dependencies.
36
36
37 The installation of front-end dependencies happens via the tool 'npm' which
37 The installation of front-end dependencies happens via the tool 'npm' which
38 is expected to be installed already.
38 is expected to be installed already.
39 """
39 """
40 front_end_dir = os.path.abspath(os.path.join(kallithea.__file__, '..', 'front-end'))
40 front_end_dir = os.path.abspath(os.path.join(kallithea.__file__, '..', 'front-end'))
41 public_dir = os.path.abspath(os.path.join(kallithea.__file__, '..', 'public'))
41 public_dir = os.path.abspath(os.path.join(kallithea.__file__, '..', 'public'))
42
42
43 if install_deps:
43 if install_deps:
44 click.echo("Running 'npm install' to install front-end dependencies from package.json")
44 click.echo("Running 'npm install' to install front-end dependencies from package.json")
45 subprocess.check_call(['npm', 'install'], cwd=front_end_dir)
45 subprocess.check_call(['npm', 'install'], cwd=front_end_dir)
46
46
47 if generate:
47 if generate:
48 tmp_dir = os.path.join(front_end_dir, 'tmp')
48 tmp_dir = os.path.join(front_end_dir, 'tmp')
49 if not os.path.isdir(tmp_dir):
49 if not os.path.isdir(tmp_dir):
50 os.mkdir(tmp_dir)
50 os.mkdir(tmp_dir)
51
51
52 click.echo("Building CSS styling based on Bootstrap")
52 click.echo("Building CSS styling based on Bootstrap")
53 with open(os.path.join(tmp_dir, 'pygments.css'), 'w') as f:
53 with open(os.path.join(tmp_dir, 'pygments.css'), 'w') as f:
54 subprocess.check_call(['pygmentize',
54 subprocess.check_call(['pygmentize',
55 '-S', 'default',
55 '-S', 'default',
56 '-f', 'html',
56 '-f', 'html',
57 '-a', '.code-highlight'],
57 '-a', '.code-highlight'],
58 stdout=f)
58 stdout=f)
59 lesscpath = os.path.join(front_end_dir, 'node_modules', '.bin', 'lessc')
59 lesscpath = os.path.join(front_end_dir, 'node_modules', '.bin', 'lessc')
60 lesspath = os.path.join(public_dir, 'less', 'main.less')
60 lesspath = os.path.join(public_dir, 'less', 'main.less')
61 csspath = os.path.join(public_dir, 'css', 'style.css')
61 csspath = os.path.join(public_dir, 'css', 'style.css')
62 subprocess.check_call([lesscpath, '--relative-urls', '--source-map',
62 subprocess.check_call([lesscpath, '--relative-urls', '--source-map',
63 '--source-map-less-inline', lesspath, csspath],
63 '--source-map-less-inline', lesspath, csspath],
64 cwd=front_end_dir)
64 cwd=front_end_dir)
65
65
66 click.echo("Preparing Bootstrap JS")
66 click.echo("Preparing Bootstrap JS")
67 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'bootstrap', 'dist', 'js', 'bootstrap.js'), os.path.join(public_dir, 'js', 'bootstrap.js'))
67 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'bootstrap', 'dist', 'js', 'bootstrap.js'), os.path.join(public_dir, 'js', 'bootstrap.js'))
68
68
69 click.echo("Preparing jQuery JS with Flot")
69 click.echo("Preparing jQuery JS with Flot, Caret and Atwho")
70 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery', 'dist', 'jquery.min.js'), os.path.join(public_dir, 'js', 'jquery.min.js'))
70 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery', 'dist', 'jquery.min.js'), os.path.join(public_dir, 'js', 'jquery.min.js'))
71 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.flot', 'jquery.flot.js'), os.path.join(public_dir, 'js', 'jquery.flot.js'))
71 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.flot', 'jquery.flot.js'), os.path.join(public_dir, 'js', 'jquery.flot.js'))
72 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.flot', 'jquery.flot.selection.js'), os.path.join(public_dir, 'js', 'jquery.flot.selection.js'))
72 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.flot', 'jquery.flot.selection.js'), os.path.join(public_dir, 'js', 'jquery.flot.selection.js'))
73 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.flot', 'jquery.flot.time.js'), os.path.join(public_dir, 'js', 'jquery.flot.time.js'))
73 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.flot', 'jquery.flot.time.js'), os.path.join(public_dir, 'js', 'jquery.flot.time.js'))
74 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.caret', 'dist', 'jquery.caret.min.js'), os.path.join(public_dir, 'js', 'jquery.caret.min.js'))
75 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'at.js', 'dist', 'js', 'jquery.atwho.min.js'), os.path.join(public_dir, 'js', 'jquery.atwho.min.js'))
74
76
75 click.echo("Preparing DataTables JS")
77 click.echo("Preparing DataTables JS")
76 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'datatables.net', 'js', 'jquery.dataTables.js'), os.path.join(public_dir, 'js', 'jquery.dataTables.js'))
78 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'datatables.net', 'js', 'jquery.dataTables.js'), os.path.join(public_dir, 'js', 'jquery.dataTables.js'))
77 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'datatables.net-bs', 'js', 'dataTables.bootstrap.js'), os.path.join(public_dir, 'js', 'dataTables.bootstrap.js'))
79 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'datatables.net-bs', 'js', 'dataTables.bootstrap.js'), os.path.join(public_dir, 'js', 'dataTables.bootstrap.js'))
78
80
79 click.echo("Preparing Select2 JS")
81 click.echo("Preparing Select2 JS")
80 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'select2', 'select2.js'), os.path.join(public_dir, 'js', 'select2.js'))
82 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'select2', 'select2.js'), os.path.join(public_dir, 'js', 'select2.js'))
81 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'select2', 'select2.png'), os.path.join(public_dir, 'css', 'select2.png'))
83 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'select2', 'select2.png'), os.path.join(public_dir, 'css', 'select2.png'))
82 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'select2', 'select2x2.png'), os.path.join(public_dir, 'css', 'select2x2.png'))
84 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'select2', 'select2x2.png'), os.path.join(public_dir, 'css', 'select2x2.png'))
83 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'select2', 'select2-spinner.gif'), os.path.join(public_dir, 'css', 'select2-spinner.gif'))
85 shutil.copy(os.path.join(front_end_dir, 'node_modules', 'select2', 'select2-spinner.gif'), os.path.join(public_dir, 'css', 'select2-spinner.gif'))
84
86
85 click.echo("Preparing CodeMirror JS")
87 click.echo("Preparing CodeMirror JS")
86 if os.path.isdir(os.path.join(public_dir, 'codemirror')):
88 if os.path.isdir(os.path.join(public_dir, 'codemirror')):
87 shutil.rmtree(os.path.join(public_dir, 'codemirror'))
89 shutil.rmtree(os.path.join(public_dir, 'codemirror'))
88 shutil.copytree(os.path.join(front_end_dir, 'node_modules', 'codemirror'), os.path.join(public_dir, 'codemirror'))
90 shutil.copytree(os.path.join(front_end_dir, 'node_modules', 'codemirror'), os.path.join(public_dir, 'codemirror'))
89
91
90 click.echo("Generating LICENSES.txt")
92 click.echo("Generating LICENSES.txt")
91 check_licensing_json_path = os.path.join(tmp_dir, 'licensing.json')
93 check_licensing_json_path = os.path.join(tmp_dir, 'licensing.json')
92 licensing_txt_path = os.path.join(public_dir, 'LICENSES.txt')
94 licensing_txt_path = os.path.join(public_dir, 'LICENSES.txt')
93 subprocess.check_call([
95 subprocess.check_call([
94 os.path.join(front_end_dir, 'node_modules', '.bin', 'license-checker'),
96 os.path.join(front_end_dir, 'node_modules', '.bin', 'license-checker'),
95 '--json',
97 '--json',
96 '--out', check_licensing_json_path,
98 '--out', check_licensing_json_path,
97 ], cwd=front_end_dir)
99 ], cwd=front_end_dir)
98 with open(check_licensing_json_path) as jsonfile:
100 with open(check_licensing_json_path) as jsonfile:
99 rows = json.loads(jsonfile.read())
101 rows = json.loads(jsonfile.read())
100 with open(licensing_txt_path, 'w') as out:
102 with open(licensing_txt_path, 'w') as out:
101 out.write("The Kallithea front-end was built using the following Node modules:\n\n")
103 out.write("The Kallithea front-end was built using the following Node modules:\n\n")
102 for name_version, values in sorted(rows.items()):
104 for name_version, values in sorted(rows.items()):
103 name, version = name_version.rsplit('@', 1)
105 name, version = name_version.rsplit('@', 1)
104 line = "%s from https://www.npmjs.com/package/%s/v/%s\n License: %s\n Repository: %s\n" % (
106 line = "%s from https://www.npmjs.com/package/%s/v/%s\n License: %s\n Repository: %s\n" % (
105 name_version, name, version, values['licenses'], values.get('repository', '-'))
107 name_version, name, version, values['licenses'], values.get('repository', '-'))
106 if values.get('copyright'):
108 if values.get('copyright'):
107 line += " Copyright: %s\n" % (values['copyright'])
109 line += " Copyright: %s\n" % (values['copyright'])
108 out.write(line + '\n')
110 out.write(line + '\n')
@@ -1,19 +1,21 b''
1 {
1 {
2 "name": "kallithea",
2 "name": "kallithea",
3 "private": true,
3 "private": true,
4 "dependencies": {
4 "dependencies": {
5 "at.js": "1.5.4",
5 "bootstrap": "3.3.7",
6 "bootstrap": "3.3.7",
6 "codemirror": "4.7",
7 "codemirror": "4.7",
7 "datatables.net": "1.10.13",
8 "datatables.net": "1.10.13",
8 "datatables.net-bs": "1.10.13",
9 "datatables.net-bs": "1.10.13",
9 "jquery": "1.12.3",
10 "jquery": "1.12.3",
11 "jquery.caret": "0.3.1",
10 "jquery.flot": "0.8.3",
12 "jquery.flot": "0.8.3",
11 "select2": "3.5.1",
13 "select2": "3.5.1",
12 "select2-bootstrap-css": "1.2.4"
14 "select2-bootstrap-css": "1.2.4"
13 },
15 },
14 "devDependencies": {
16 "devDependencies": {
15 "less": "~2.7",
17 "less": "~2.7",
16 "less-plugin-clean-css": "~1.5",
18 "less-plugin-clean-css": "~1.5",
17 "license-checker": "24.1.0"
19 "license-checker": "24.1.0"
18 }
20 }
19 }
21 }
@@ -1,1516 +1,1481 b''
1 /**
1 /**
2 Kallithea JS Files
2 Kallithea JS Files
3 **/
3 **/
4 'use strict';
4 'use strict';
5
5
6 if (typeof console == "undefined" || typeof console.log == "undefined"){
6 if (typeof console == "undefined" || typeof console.log == "undefined"){
7 console = { log: function() {} }
7 console = { log: function() {} }
8 }
8 }
9
9
10 /**
10 /**
11 * INJECT .format function into String
11 * INJECT .format function into String
12 * Usage: "My name is {0} {1}".format("Johny","Bravo")
12 * Usage: "My name is {0} {1}".format("Johny","Bravo")
13 * Return "My name is Johny Bravo"
13 * Return "My name is Johny Bravo"
14 * Inspired by https://gist.github.com/1049426
14 * Inspired by https://gist.github.com/1049426
15 */
15 */
16 String.prototype.format = function() {
16 String.prototype.format = function() {
17 function format() {
17 function format() {
18 var str = this;
18 var str = this;
19 var len = arguments.length+1;
19 var len = arguments.length+1;
20 var safe = undefined;
20 var safe = undefined;
21 var arg = undefined;
21 var arg = undefined;
22
22
23 // For each {0} {1} {n...} replace with the argument in that position. If
23 // For each {0} {1} {n...} replace with the argument in that position. If
24 // the argument is an object or an array it will be stringified to JSON.
24 // the argument is an object or an array it will be stringified to JSON.
25 for (var i=0; i < len; arg = arguments[i++]) {
25 for (var i=0; i < len; arg = arguments[i++]) {
26 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
26 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
27 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
27 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
28 }
28 }
29 return str;
29 return str;
30 }
30 }
31
31
32 // Save a reference of what may already exist under the property native.
32 // Save a reference of what may already exist under the property native.
33 // Allows for doing something like: if("".format.native) { /* use native */ }
33 // Allows for doing something like: if("".format.native) { /* use native */ }
34 format.native = String.prototype.format;
34 format.native = String.prototype.format;
35
35
36 // Replace the prototype property
36 // Replace the prototype property
37 return format;
37 return format;
38
38
39 }();
39 }();
40
40
41 String.prototype.strip = function(char) {
41 String.prototype.strip = function(char) {
42 if(char === undefined){
42 if(char === undefined){
43 char = '\\s';
43 char = '\\s';
44 }
44 }
45 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
45 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
46 }
46 }
47
47
48 String.prototype.lstrip = function(char) {
48 String.prototype.lstrip = function(char) {
49 if(char === undefined){
49 if(char === undefined){
50 char = '\\s';
50 char = '\\s';
51 }
51 }
52 return this.replace(new RegExp('^'+char+'+'),'');
52 return this.replace(new RegExp('^'+char+'+'),'');
53 }
53 }
54
54
55 String.prototype.rstrip = function(char) {
55 String.prototype.rstrip = function(char) {
56 if(char === undefined){
56 if(char === undefined){
57 char = '\\s';
57 char = '\\s';
58 }
58 }
59 return this.replace(new RegExp(''+char+'+$'),'');
59 return this.replace(new RegExp(''+char+'+$'),'');
60 }
60 }
61
61
62 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
62 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
63 under MIT license / public domain, see
63 under MIT license / public domain, see
64 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
64 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
65 if(!Array.prototype.indexOf) {
65 if(!Array.prototype.indexOf) {
66 Array.prototype.indexOf = function (searchElement, fromIndex) {
66 Array.prototype.indexOf = function (searchElement, fromIndex) {
67 if ( this === undefined || this === null ) {
67 if ( this === undefined || this === null ) {
68 throw new TypeError( '"this" is null or not defined' );
68 throw new TypeError( '"this" is null or not defined' );
69 }
69 }
70
70
71 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
71 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
72
72
73 fromIndex = +fromIndex || 0;
73 fromIndex = +fromIndex || 0;
74
74
75 if (Math.abs(fromIndex) === Infinity) {
75 if (Math.abs(fromIndex) === Infinity) {
76 fromIndex = 0;
76 fromIndex = 0;
77 }
77 }
78
78
79 if (fromIndex < 0) {
79 if (fromIndex < 0) {
80 fromIndex += length;
80 fromIndex += length;
81 if (fromIndex < 0) {
81 if (fromIndex < 0) {
82 fromIndex = 0;
82 fromIndex = 0;
83 }
83 }
84 }
84 }
85
85
86 for (;fromIndex < length; fromIndex++) {
86 for (;fromIndex < length; fromIndex++) {
87 if (this[fromIndex] === searchElement) {
87 if (this[fromIndex] === searchElement) {
88 return fromIndex;
88 return fromIndex;
89 }
89 }
90 }
90 }
91
91
92 return -1;
92 return -1;
93 };
93 };
94 }
94 }
95
95
96 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
96 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
97 under MIT license / public domain, see
97 under MIT license / public domain, see
98 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
98 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
99 if (!Array.prototype.filter)
99 if (!Array.prototype.filter)
100 {
100 {
101 Array.prototype.filter = function(fun /*, thisArg */)
101 Array.prototype.filter = function(fun /*, thisArg */)
102 {
102 {
103 if (this === void 0 || this === null)
103 if (this === void 0 || this === null)
104 throw new TypeError();
104 throw new TypeError();
105
105
106 var t = Object(this);
106 var t = Object(this);
107 var len = t.length >>> 0;
107 var len = t.length >>> 0;
108 if (typeof fun !== "function")
108 if (typeof fun !== "function")
109 throw new TypeError();
109 throw new TypeError();
110
110
111 var res = [];
111 var res = [];
112 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
112 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
113 for (var i = 0; i < len; i++)
113 for (var i = 0; i < len; i++)
114 {
114 {
115 if (i in t)
115 if (i in t)
116 {
116 {
117 var val = t[i];
117 var val = t[i];
118
118
119 // NOTE: Technically this should Object.defineProperty at
119 // NOTE: Technically this should Object.defineProperty at
120 // the next index, as push can be affected by
120 // the next index, as push can be affected by
121 // properties on Object.prototype and Array.prototype.
121 // properties on Object.prototype and Array.prototype.
122 // But that method's new, and collisions should be
122 // But that method's new, and collisions should be
123 // rare, so use the more-compatible alternative.
123 // rare, so use the more-compatible alternative.
124 if (fun.call(thisArg, val, i, t))
124 if (fun.call(thisArg, val, i, t))
125 res.push(val);
125 res.push(val);
126 }
126 }
127 }
127 }
128
128
129 return res;
129 return res;
130 };
130 };
131 }
131 }
132
132
133 /**
133 /**
134 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
134 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
135 * which is copyright Stephane Klein and was made available under the BSD License.
135 * which is copyright Stephane Klein and was made available under the BSD License.
136 *
136 *
137 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
137 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
138 */
138 */
139 var pyroutes = (function() {
139 var pyroutes = (function() {
140 var matchlist = {};
140 var matchlist = {};
141 var sprintf = (function() {
141 var sprintf = (function() {
142 function get_type(variable) {
142 function get_type(variable) {
143 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
143 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
144 }
144 }
145 function str_repeat(input, multiplier) {
145 function str_repeat(input, multiplier) {
146 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
146 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
147 return output.join('');
147 return output.join('');
148 }
148 }
149
149
150 var str_format = function() {
150 var str_format = function() {
151 if (!str_format.cache.hasOwnProperty(arguments[0])) {
151 if (!str_format.cache.hasOwnProperty(arguments[0])) {
152 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
152 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
153 }
153 }
154 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
154 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
155 };
155 };
156
156
157 str_format.format = function(parse_tree, argv) {
157 str_format.format = function(parse_tree, argv) {
158 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
158 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
159 for (i = 0; i < tree_length; i++) {
159 for (i = 0; i < tree_length; i++) {
160 node_type = get_type(parse_tree[i]);
160 node_type = get_type(parse_tree[i]);
161 if (node_type === 'string') {
161 if (node_type === 'string') {
162 output.push(parse_tree[i]);
162 output.push(parse_tree[i]);
163 }
163 }
164 else if (node_type === 'array') {
164 else if (node_type === 'array') {
165 match = parse_tree[i]; // convenience purposes only
165 match = parse_tree[i]; // convenience purposes only
166 if (match[2]) { // keyword argument
166 if (match[2]) { // keyword argument
167 arg = argv[cursor];
167 arg = argv[cursor];
168 for (k = 0; k < match[2].length; k++) {
168 for (k = 0; k < match[2].length; k++) {
169 if (!arg.hasOwnProperty(match[2][k])) {
169 if (!arg.hasOwnProperty(match[2][k])) {
170 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
170 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
171 }
171 }
172 arg = arg[match[2][k]];
172 arg = arg[match[2][k]];
173 }
173 }
174 }
174 }
175 else if (match[1]) { // positional argument (explicit)
175 else if (match[1]) { // positional argument (explicit)
176 arg = argv[match[1]];
176 arg = argv[match[1]];
177 }
177 }
178 else { // positional argument (implicit)
178 else { // positional argument (implicit)
179 arg = argv[cursor++];
179 arg = argv[cursor++];
180 }
180 }
181
181
182 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
182 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
183 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
183 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
184 }
184 }
185 switch (match[8]) {
185 switch (match[8]) {
186 case 'b': arg = arg.toString(2); break;
186 case 'b': arg = arg.toString(2); break;
187 case 'c': arg = String.fromCharCode(arg); break;
187 case 'c': arg = String.fromCharCode(arg); break;
188 case 'd': arg = parseInt(arg, 10); break;
188 case 'd': arg = parseInt(arg, 10); break;
189 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
189 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
190 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
190 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
191 case 'o': arg = arg.toString(8); break;
191 case 'o': arg = arg.toString(8); break;
192 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
192 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
193 case 'u': arg = Math.abs(arg); break;
193 case 'u': arg = Math.abs(arg); break;
194 case 'x': arg = arg.toString(16); break;
194 case 'x': arg = arg.toString(16); break;
195 case 'X': arg = arg.toString(16).toUpperCase(); break;
195 case 'X': arg = arg.toString(16).toUpperCase(); break;
196 }
196 }
197 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
197 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
198 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
198 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
199 pad_length = match[6] - String(arg).length;
199 pad_length = match[6] - String(arg).length;
200 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
200 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
201 output.push(match[5] ? arg + pad : pad + arg);
201 output.push(match[5] ? arg + pad : pad + arg);
202 }
202 }
203 }
203 }
204 return output.join('');
204 return output.join('');
205 };
205 };
206
206
207 str_format.cache = {};
207 str_format.cache = {};
208
208
209 str_format.parse = function(fmt) {
209 str_format.parse = function(fmt) {
210 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
210 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
211 while (_fmt) {
211 while (_fmt) {
212 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
212 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
213 parse_tree.push(match[0]);
213 parse_tree.push(match[0]);
214 }
214 }
215 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
215 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
216 parse_tree.push('%');
216 parse_tree.push('%');
217 }
217 }
218 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
218 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
219 if (match[2]) {
219 if (match[2]) {
220 arg_names |= 1;
220 arg_names |= 1;
221 var field_list = [], replacement_field = match[2], field_match = [];
221 var field_list = [], replacement_field = match[2], field_match = [];
222 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
222 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
223 field_list.push(field_match[1]);
223 field_list.push(field_match[1]);
224 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
224 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
225 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
225 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
226 field_list.push(field_match[1]);
226 field_list.push(field_match[1]);
227 }
227 }
228 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
228 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
229 field_list.push(field_match[1]);
229 field_list.push(field_match[1]);
230 }
230 }
231 else {
231 else {
232 throw('[sprintf] huh?');
232 throw('[sprintf] huh?');
233 }
233 }
234 }
234 }
235 }
235 }
236 else {
236 else {
237 throw('[sprintf] huh?');
237 throw('[sprintf] huh?');
238 }
238 }
239 match[2] = field_list;
239 match[2] = field_list;
240 }
240 }
241 else {
241 else {
242 arg_names |= 2;
242 arg_names |= 2;
243 }
243 }
244 if (arg_names === 3) {
244 if (arg_names === 3) {
245 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
245 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
246 }
246 }
247 parse_tree.push(match);
247 parse_tree.push(match);
248 }
248 }
249 else {
249 else {
250 throw('[sprintf] huh?');
250 throw('[sprintf] huh?');
251 }
251 }
252 _fmt = _fmt.substring(match[0].length);
252 _fmt = _fmt.substring(match[0].length);
253 }
253 }
254 return parse_tree;
254 return parse_tree;
255 };
255 };
256
256
257 return str_format;
257 return str_format;
258 })();
258 })();
259
259
260 var vsprintf = function(fmt, argv) {
260 var vsprintf = function(fmt, argv) {
261 argv.unshift(fmt);
261 argv.unshift(fmt);
262 return sprintf.apply(null, argv);
262 return sprintf.apply(null, argv);
263 };
263 };
264 return {
264 return {
265 'url': function(route_name, params) {
265 'url': function(route_name, params) {
266 var result = route_name;
266 var result = route_name;
267 if (typeof(params) != 'object'){
267 if (typeof(params) != 'object'){
268 params = {};
268 params = {};
269 }
269 }
270 if (matchlist.hasOwnProperty(route_name)) {
270 if (matchlist.hasOwnProperty(route_name)) {
271 var route = matchlist[route_name];
271 var route = matchlist[route_name];
272 // param substitution
272 // param substitution
273 for(var i=0; i < route[1].length; i++) {
273 for(var i=0; i < route[1].length; i++) {
274 if (!params.hasOwnProperty(route[1][i]))
274 if (!params.hasOwnProperty(route[1][i]))
275 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
275 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
276 }
276 }
277 result = sprintf(route[0], params);
277 result = sprintf(route[0], params);
278
278
279 var ret = [];
279 var ret = [];
280 //extra params => GET
280 //extra params => GET
281 for(var param in params){
281 for(var param in params){
282 if (route[1].indexOf(param) == -1){
282 if (route[1].indexOf(param) == -1){
283 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
283 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
284 }
284 }
285 }
285 }
286 var _parts = ret.join("&");
286 var _parts = ret.join("&");
287 if(_parts){
287 if(_parts){
288 result = result +'?'+ _parts
288 result = result +'?'+ _parts
289 }
289 }
290 }
290 }
291
291
292 return result;
292 return result;
293 },
293 },
294 'register': function(route_name, route_tmpl, req_params) {
294 'register': function(route_name, route_tmpl, req_params) {
295 if (typeof(req_params) != 'object') {
295 if (typeof(req_params) != 'object') {
296 req_params = [];
296 req_params = [];
297 }
297 }
298 var keys = [];
298 var keys = [];
299 for (var i=0; i < req_params.length; i++) {
299 for (var i=0; i < req_params.length; i++) {
300 keys.push(req_params[i]);
300 keys.push(req_params[i]);
301 }
301 }
302 matchlist[route_name] = [
302 matchlist[route_name] = [
303 unescape(route_tmpl),
303 unescape(route_tmpl),
304 keys
304 keys
305 ]
305 ]
306 },
306 },
307 '_routes': function(){
307 '_routes': function(){
308 return matchlist;
308 return matchlist;
309 }
309 }
310 }
310 }
311 })();
311 })();
312
312
313
313
314 /* Invoke all functions in callbacks */
314 /* Invoke all functions in callbacks */
315 var _run_callbacks = function(callbacks){
315 var _run_callbacks = function(callbacks){
316 if (callbacks !== undefined){
316 if (callbacks !== undefined){
317 var _l = callbacks.length;
317 var _l = callbacks.length;
318 for (var i=0;i<_l;i++){
318 for (var i=0;i<_l;i++){
319 var func = callbacks[i];
319 var func = callbacks[i];
320 if(typeof(func)=='function'){
320 if(typeof(func)=='function'){
321 try{
321 try{
322 func();
322 func();
323 }catch (err){};
323 }catch (err){};
324 }
324 }
325 }
325 }
326 }
326 }
327 }
327 }
328
328
329 /**
329 /**
330 * turns objects into GET query string
330 * turns objects into GET query string
331 */
331 */
332 var _toQueryString = function(o) {
332 var _toQueryString = function(o) {
333 if(typeof o !== 'object') {
333 if(typeof o !== 'object') {
334 return false;
334 return false;
335 }
335 }
336 var _p, _qs = [];
336 var _p, _qs = [];
337 for(_p in o) {
337 for(_p in o) {
338 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
338 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
339 }
339 }
340 return _qs.join('&');
340 return _qs.join('&');
341 };
341 };
342
342
343 /**
343 /**
344 * Load HTML into DOM using Ajax
344 * Load HTML into DOM using Ajax
345 *
345 *
346 * @param $target: load html async and place it (or an error message) here
346 * @param $target: load html async and place it (or an error message) here
347 * @param success: success callback function
347 * @param success: success callback function
348 * @param args: query parameters to pass to url
348 * @param args: query parameters to pass to url
349 */
349 */
350 function asynchtml(url, $target, success, args){
350 function asynchtml(url, $target, success, args){
351 if(args===undefined){
351 if(args===undefined){
352 args=null;
352 args=null;
353 }
353 }
354 $target.html(_TM['Loading ...']).css('opacity','0.3');
354 $target.html(_TM['Loading ...']).css('opacity','0.3');
355
355
356 return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
356 return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
357 .done(function(html) {
357 .done(function(html) {
358 $target.html(html);
358 $target.html(html);
359 $target.css('opacity','1.0');
359 $target.css('opacity','1.0');
360 //execute the given original callback
360 //execute the given original callback
361 if (success !== undefined && success) {
361 if (success !== undefined && success) {
362 success();
362 success();
363 }
363 }
364 })
364 })
365 .fail(function(jqXHR, textStatus, errorThrown) {
365 .fail(function(jqXHR, textStatus, errorThrown) {
366 if (textStatus == "abort")
366 if (textStatus == "abort")
367 return;
367 return;
368 $target.html('<span class="bg-danger">ERROR: {0}</span>'.format(textStatus));
368 $target.html('<span class="bg-danger">ERROR: {0}</span>'.format(textStatus));
369 $target.css('opacity','1.0');
369 $target.css('opacity','1.0');
370 })
370 })
371 ;
371 ;
372 };
372 };
373
373
374 var ajaxGET = function(url, success, failure) {
374 var ajaxGET = function(url, success, failure) {
375 if(failure === undefined) {
375 if(failure === undefined) {
376 failure = function(jqXHR, textStatus, errorThrown) {
376 failure = function(jqXHR, textStatus, errorThrown) {
377 if (textStatus != "abort")
377 if (textStatus != "abort")
378 alert("Ajax GET error: " + textStatus);
378 alert("Ajax GET error: " + textStatus);
379 };
379 };
380 }
380 }
381 return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
381 return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
382 .done(success)
382 .done(success)
383 .fail(failure);
383 .fail(failure);
384 };
384 };
385
385
386 var ajaxPOST = function(url, postData, success, failure) {
386 var ajaxPOST = function(url, postData, success, failure) {
387 postData['_authentication_token'] = _authentication_token;
387 postData['_authentication_token'] = _authentication_token;
388 var postData = _toQueryString(postData);
388 var postData = _toQueryString(postData);
389 if(failure === undefined) {
389 if(failure === undefined) {
390 failure = function(jqXHR, textStatus, errorThrown) {
390 failure = function(jqXHR, textStatus, errorThrown) {
391 if (textStatus != "abort")
391 if (textStatus != "abort")
392 alert("Error posting to server: " + textStatus);
392 alert("Error posting to server: " + textStatus);
393 };
393 };
394 }
394 }
395 return $.ajax({url: url, data: postData, type: 'POST', headers: {'X-PARTIAL-XHR': '1'}, cache: false})
395 return $.ajax({url: url, data: postData, type: 'POST', headers: {'X-PARTIAL-XHR': '1'}, cache: false})
396 .done(success)
396 .done(success)
397 .fail(failure);
397 .fail(failure);
398 };
398 };
399
399
400
400
401 /**
401 /**
402 * activate .show_more links
402 * activate .show_more links
403 * the .show_more must have an id that is the the id of an element to hide prefixed with _
403 * the .show_more must have an id that is the the id of an element to hide prefixed with _
404 * the parentnode will be displayed
404 * the parentnode will be displayed
405 */
405 */
406 var show_more_event = function(){
406 var show_more_event = function(){
407 $('.show_more').click(function(e){
407 $('.show_more').click(function(e){
408 var el = e.currentTarget;
408 var el = e.currentTarget;
409 $('#' + el.id.substring(1)).hide();
409 $('#' + el.id.substring(1)).hide();
410 $(el.parentNode).show();
410 $(el.parentNode).show();
411 });
411 });
412 };
412 };
413
413
414
414
415 var _onSuccessFollow = function(target){
415 var _onSuccessFollow = function(target){
416 var $target = $(target);
416 var $target = $(target);
417 var $f_cnt = $('#current_followers_count');
417 var $f_cnt = $('#current_followers_count');
418 if ($target.hasClass('follow')) {
418 if ($target.hasClass('follow')) {
419 $target.removeClass('follow').addClass('following');
419 $target.removeClass('follow').addClass('following');
420 $target.prop('title', _TM['Stop following this repository']);
420 $target.prop('title', _TM['Stop following this repository']);
421 if ($f_cnt.html()) {
421 if ($f_cnt.html()) {
422 var cnt = Number($f_cnt.html())+1;
422 var cnt = Number($f_cnt.html())+1;
423 $f_cnt.html(cnt);
423 $f_cnt.html(cnt);
424 }
424 }
425 } else {
425 } else {
426 $target.removeClass('following').addClass('follow');
426 $target.removeClass('following').addClass('follow');
427 $target.prop('title', _TM['Start following this repository']);
427 $target.prop('title', _TM['Start following this repository']);
428 if ($f_cnt.html()) {
428 if ($f_cnt.html()) {
429 var cnt = Number($f_cnt.html())-1;
429 var cnt = Number($f_cnt.html())-1;
430 $f_cnt.html(cnt);
430 $f_cnt.html(cnt);
431 }
431 }
432 }
432 }
433 }
433 }
434
434
435 var toggleFollowingRepo = function(target, follows_repository_id){
435 var toggleFollowingRepo = function(target, follows_repository_id){
436 var args = 'follows_repository_id=' + follows_repository_id;
436 var args = 'follows_repository_id=' + follows_repository_id;
437 args += '&amp;_authentication_token=' + _authentication_token;
437 args += '&amp;_authentication_token=' + _authentication_token;
438 $.post(TOGGLE_FOLLOW_URL, args, function(data){
438 $.post(TOGGLE_FOLLOW_URL, args, function(data){
439 _onSuccessFollow(target);
439 _onSuccessFollow(target);
440 });
440 });
441 return false;
441 return false;
442 };
442 };
443
443
444 var showRepoSize = function(target, repo_name){
444 var showRepoSize = function(target, repo_name){
445 var args = '_authentication_token=' + _authentication_token;
445 var args = '_authentication_token=' + _authentication_token;
446
446
447 if(!$("#" + target).hasClass('loaded')){
447 if(!$("#" + target).hasClass('loaded')){
448 $("#" + target).html(_TM['Loading ...']);
448 $("#" + target).html(_TM['Loading ...']);
449 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
449 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
450 $.post(url, args, function(data) {
450 $.post(url, args, function(data) {
451 $("#" + target).html(data);
451 $("#" + target).html(data);
452 $("#" + target).addClass('loaded');
452 $("#" + target).addClass('loaded');
453 });
453 });
454 }
454 }
455 return false;
455 return false;
456 };
456 };
457
457
458 /**
458 /**
459 * load tooltips dynamically based on data attributes, used for .lazy-cs changeset links
459 * load tooltips dynamically based on data attributes, used for .lazy-cs changeset links
460 */
460 */
461 var get_changeset_tooltip = function() {
461 var get_changeset_tooltip = function() {
462 var $target = $(this);
462 var $target = $(this);
463 var tooltip = $target.data('tooltip');
463 var tooltip = $target.data('tooltip');
464 if (!tooltip) {
464 if (!tooltip) {
465 var raw_id = $target.data('raw_id');
465 var raw_id = $target.data('raw_id');
466 var repo_name = $target.data('repo_name');
466 var repo_name = $target.data('repo_name');
467 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": raw_id});
467 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": raw_id});
468
468
469 $.ajax(url, {
469 $.ajax(url, {
470 async: false,
470 async: false,
471 success: function(data) {
471 success: function(data) {
472 tooltip = data["message"];
472 tooltip = data["message"];
473 }
473 }
474 });
474 });
475 $target.data('tooltip', tooltip);
475 $target.data('tooltip', tooltip);
476 }
476 }
477 return tooltip;
477 return tooltip;
478 };
478 };
479
479
480 /**
480 /**
481 * activate tooltips and popups
481 * activate tooltips and popups
482 */
482 */
483 var tooltip_activate = function(){
483 var tooltip_activate = function(){
484 function placement(p, e){
484 function placement(p, e){
485 if(e.getBoundingClientRect().top > 2*$(window).height()/3){
485 if(e.getBoundingClientRect().top > 2*$(window).height()/3){
486 return 'top';
486 return 'top';
487 }else{
487 }else{
488 return 'bottom';
488 return 'bottom';
489 }
489 }
490 }
490 }
491 $(document).ready(function(){
491 $(document).ready(function(){
492 $('[data-toggle="tooltip"]').tooltip({
492 $('[data-toggle="tooltip"]').tooltip({
493 container: 'body',
493 container: 'body',
494 placement: placement
494 placement: placement
495 });
495 });
496 $('[data-toggle="popover"]').popover({
496 $('[data-toggle="popover"]').popover({
497 html: true,
497 html: true,
498 container: 'body',
498 container: 'body',
499 placement: placement,
499 placement: placement,
500 trigger: 'hover',
500 trigger: 'hover',
501 template: '<div class="popover cs-popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
501 template: '<div class="popover cs-popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
502 });
502 });
503 $('.lazy-cs').tooltip({
503 $('.lazy-cs').tooltip({
504 title: get_changeset_tooltip,
504 title: get_changeset_tooltip,
505 placement: placement
505 placement: placement
506 });
506 });
507 });
507 });
508 };
508 };
509
509
510
510
511 /**
511 /**
512 * Quick filter widget
512 * Quick filter widget
513 *
513 *
514 * @param target: filter input target
514 * @param target: filter input target
515 * @param nodes: list of nodes in html we want to filter.
515 * @param nodes: list of nodes in html we want to filter.
516 * @param display_element function that takes current node from nodes and
516 * @param display_element function that takes current node from nodes and
517 * does hide or show based on the node
517 * does hide or show based on the node
518 */
518 */
519 var q_filter = (function() {
519 var q_filter = (function() {
520 var _namespace = {};
520 var _namespace = {};
521 var namespace = function (target) {
521 var namespace = function (target) {
522 if (!(target in _namespace)) {
522 if (!(target in _namespace)) {
523 _namespace[target] = {};
523 _namespace[target] = {};
524 }
524 }
525 return _namespace[target];
525 return _namespace[target];
526 };
526 };
527 return function (target, $nodes, display_element) {
527 return function (target, $nodes, display_element) {
528 var $nodes = $nodes;
528 var $nodes = $nodes;
529 var $q_filter_field = $('#' + target);
529 var $q_filter_field = $('#' + target);
530 var F = namespace(target);
530 var F = namespace(target);
531
531
532 $q_filter_field.keyup(function (e) {
532 $q_filter_field.keyup(function (e) {
533 clearTimeout(F.filterTimeout);
533 clearTimeout(F.filterTimeout);
534 F.filterTimeout = setTimeout(F.updateFilter, 600);
534 F.filterTimeout = setTimeout(F.updateFilter, 600);
535 });
535 });
536
536
537 F.filterTimeout = null;
537 F.filterTimeout = null;
538
538
539 F.updateFilter = function () {
539 F.updateFilter = function () {
540 // Reset timeout
540 // Reset timeout
541 F.filterTimeout = null;
541 F.filterTimeout = null;
542
542
543 var obsolete = [];
543 var obsolete = [];
544
544
545 var req = $q_filter_field.val().toLowerCase();
545 var req = $q_filter_field.val().toLowerCase();
546
546
547 var showing = 0;
547 var showing = 0;
548 $nodes.each(function () {
548 $nodes.each(function () {
549 var n = this;
549 var n = this;
550 var target_element = display_element(n);
550 var target_element = display_element(n);
551 if (req && n.innerHTML.toLowerCase().indexOf(req) == -1) {
551 if (req && n.innerHTML.toLowerCase().indexOf(req) == -1) {
552 $(target_element).hide();
552 $(target_element).hide();
553 }
553 }
554 else {
554 else {
555 $(target_element).show();
555 $(target_element).show();
556 showing += 1;
556 showing += 1;
557 }
557 }
558 });
558 });
559
559
560 $('#repo_count').html(showing);
560 $('#repo_count').html(showing);
561 /* FIXME: don't hardcode */
561 /* FIXME: don't hardcode */
562 }
562 }
563 }
563 }
564 })();
564 })();
565
565
566
566
567 /**
567 /**
568 * Comment handling
568 * Comment handling
569 */
569 */
570
570
571 // move comments to their right location, inside new trs
571 // move comments to their right location, inside new trs
572 function move_comments($anchorcomments) {
572 function move_comments($anchorcomments) {
573 $anchorcomments.each(function(i, anchorcomment) {
573 $anchorcomments.each(function(i, anchorcomment) {
574 var $anchorcomment = $(anchorcomment);
574 var $anchorcomment = $(anchorcomment);
575 var target_id = $anchorcomment.data('target-id');
575 var target_id = $anchorcomment.data('target-id');
576 var $comment_div = _get_add_comment_div(target_id);
576 var $comment_div = _get_add_comment_div(target_id);
577 var f_path = $anchorcomment.data('f_path');
577 var f_path = $anchorcomment.data('f_path');
578 var line_no = $anchorcomment.data('line_no');
578 var line_no = $anchorcomment.data('line_no');
579 if ($comment_div[0]) {
579 if ($comment_div[0]) {
580 $comment_div.append($anchorcomment.children());
580 $comment_div.append($anchorcomment.children());
581 if (f_path && line_no) {
581 if (f_path && line_no) {
582 _comment_div_append_add($comment_div, f_path, line_no);
582 _comment_div_append_add($comment_div, f_path, line_no);
583 } else {
583 } else {
584 _comment_div_append_form($comment_div, f_path, line_no);
584 _comment_div_append_form($comment_div, f_path, line_no);
585 }
585 }
586 } else {
586 } else {
587 $anchorcomment.before("<span class='bg-warning'>Comment to {0} line {1} which is outside the diff context:</span>".format(f_path || '?', line_no || '?'));
587 $anchorcomment.before("<span class='bg-warning'>Comment to {0} line {1} which is outside the diff context:</span>".format(f_path || '?', line_no || '?'));
588 }
588 }
589 });
589 });
590 linkInlineComments($('.firstlink'), $('.comment:first-child'));
590 linkInlineComments($('.firstlink'), $('.comment:first-child'));
591 }
591 }
592
592
593 // comment bubble was clicked - insert new tr and show form
593 // comment bubble was clicked - insert new tr and show form
594 function show_comment_form($bubble) {
594 function show_comment_form($bubble) {
595 var children = $bubble.closest('tr.line').children('[id]');
595 var children = $bubble.closest('tr.line').children('[id]');
596 var line_td_id = children[children.length - 1].id;
596 var line_td_id = children[children.length - 1].id;
597 var $comment_div = _get_add_comment_div(line_td_id);
597 var $comment_div = _get_add_comment_div(line_td_id);
598 var f_path = $bubble.closest('[data-f_path]').data('f_path');
598 var f_path = $bubble.closest('[data-f_path]').data('f_path');
599 var parts = line_td_id.split('_');
599 var parts = line_td_id.split('_');
600 var line_no = parts[parts.length-1];
600 var line_no = parts[parts.length-1];
601 comment_div_state($comment_div, f_path, line_no, true);
601 comment_div_state($comment_div, f_path, line_no, true);
602 }
602 }
603
603
604 // return comment div for target_id - add it if it doesn't exist yet
604 // return comment div for target_id - add it if it doesn't exist yet
605 function _get_add_comment_div(target_id) {
605 function _get_add_comment_div(target_id) {
606 var comments_box_id = 'comments-' + target_id;
606 var comments_box_id = 'comments-' + target_id;
607 var $comments_box = $('#' + comments_box_id);
607 var $comments_box = $('#' + comments_box_id);
608 if (!$comments_box.length) {
608 if (!$comments_box.length) {
609 var html = '<tr><td id="{0}" colspan="3" class="inline-comments"></td></tr>'.format(comments_box_id);
609 var html = '<tr><td id="{0}" colspan="3" class="inline-comments"></td></tr>'.format(comments_box_id);
610 $('#' + target_id).closest('tr').after(html);
610 $('#' + target_id).closest('tr').after(html);
611 $comments_box = $('#' + comments_box_id);
611 $comments_box = $('#' + comments_box_id);
612 }
612 }
613 return $comments_box;
613 return $comments_box;
614 }
614 }
615
615
616 // Set $comment_div state - showing or not showing form and Add button.
616 // Set $comment_div state - showing or not showing form and Add button.
617 // An Add button is shown on non-empty forms when no form is shown.
617 // An Add button is shown on non-empty forms when no form is shown.
618 // The form is controlled by show_form_opt - if undefined, form is only shown for general comments.
618 // The form is controlled by show_form_opt - if undefined, form is only shown for general comments.
619 function comment_div_state($comment_div, f_path, line_no, show_form_opt) {
619 function comment_div_state($comment_div, f_path, line_no, show_form_opt) {
620 var show_form = show_form_opt !== undefined ? show_form_opt : !f_path && !line_no;
620 var show_form = show_form_opt !== undefined ? show_form_opt : !f_path && !line_no;
621 var $forms = $comment_div.children('.comment-inline-form');
621 var $forms = $comment_div.children('.comment-inline-form');
622 var $buttonrow = $comment_div.children('.add-button-row');
622 var $buttonrow = $comment_div.children('.add-button-row');
623 var $comments = $comment_div.children('.comment:not(.submitting)');
623 var $comments = $comment_div.children('.comment:not(.submitting)');
624 $forms.remove();
624 $forms.remove();
625 $buttonrow.remove();
625 $buttonrow.remove();
626 if (show_form) {
626 if (show_form) {
627 _comment_div_append_form($comment_div, f_path, line_no);
627 _comment_div_append_form($comment_div, f_path, line_no);
628 } else if ($comments.length) {
628 } else if ($comments.length) {
629 _comment_div_append_add($comment_div, f_path, line_no);
629 _comment_div_append_add($comment_div, f_path, line_no);
630 } else {
630 } else {
631 $comment_div.parent('tr').remove();
631 $comment_div.parent('tr').remove();
632 }
632 }
633 }
633 }
634
634
635 // append an Add button to $comment_div and hook it up to show form
635 // append an Add button to $comment_div and hook it up to show form
636 function _comment_div_append_add($comment_div, f_path, line_no) {
636 function _comment_div_append_add($comment_div, f_path, line_no) {
637 var addlabel = TRANSLATION_MAP['Add Another Comment'];
637 var addlabel = TRANSLATION_MAP['Add Another Comment'];
638 var $add = $('<div class="add-button-row"><span class="btn btn-default btn-xs add-button">{0}</span></div>'.format(addlabel));
638 var $add = $('<div class="add-button-row"><span class="btn btn-default btn-xs add-button">{0}</span></div>'.format(addlabel));
639 $comment_div.append($add);
639 $comment_div.append($add);
640 $add.children('.add-button').click(function(e) {
640 $add.children('.add-button').click(function(e) {
641 comment_div_state($comment_div, f_path, line_no, true);
641 comment_div_state($comment_div, f_path, line_no, true);
642 });
642 });
643 }
643 }
644
644
645 // append a comment form to $comment_div
645 // append a comment form to $comment_div
646 function _comment_div_append_form($comment_div, f_path, line_no) {
646 function _comment_div_append_form($comment_div, f_path, line_no) {
647 var $form_div = $('#comment-inline-form-template').children()
647 var $form_div = $('#comment-inline-form-template').children()
648 .clone()
648 .clone()
649 .addClass('comment-inline-form');
649 .addClass('comment-inline-form');
650 $comment_div.append($form_div);
650 $comment_div.append($form_div);
651 var $preview = $comment_div.find("div.comment-preview");
651 var $preview = $comment_div.find("div.comment-preview");
652 var $form = $comment_div.find("form");
652 var $form = $comment_div.find("form");
653 var $textarea = $form.find('textarea');
653 var $textarea = $form.find('textarea');
654
654
655 $form.submit(function(e) {
655 $form.submit(function(e) {
656 e.preventDefault();
656 e.preventDefault();
657
657
658 var text = $textarea.val();
658 var text = $textarea.val();
659 var review_status = $form.find('input:radio[name=changeset_status]:checked').val();
659 var review_status = $form.find('input:radio[name=changeset_status]:checked').val();
660 var pr_close = $form.find('input:checkbox[name=save_close]:checked').length ? 'on' : '';
660 var pr_close = $form.find('input:checkbox[name=save_close]:checked').length ? 'on' : '';
661 var pr_delete = $form.find('input:checkbox[name=save_delete]:checked').length ? 'delete' : '';
661 var pr_delete = $form.find('input:checkbox[name=save_delete]:checked').length ? 'delete' : '';
662
662
663 if (!text && !review_status && !pr_close && !pr_delete) {
663 if (!text && !review_status && !pr_close && !pr_delete) {
664 alert("Please provide a comment");
664 alert("Please provide a comment");
665 return false;
665 return false;
666 }
666 }
667
667
668 if (pr_delete) {
668 if (pr_delete) {
669 if (text || review_status || pr_close) {
669 if (text || review_status || pr_close) {
670 alert('Cannot delete pull request while making other changes');
670 alert('Cannot delete pull request while making other changes');
671 return false;
671 return false;
672 }
672 }
673 if (!confirm('Confirm to delete this pull request')) {
673 if (!confirm('Confirm to delete this pull request')) {
674 return false;
674 return false;
675 }
675 }
676 var comments = $('.comment').size();
676 var comments = $('.comment').size();
677 if (comments > 0 &&
677 if (comments > 0 &&
678 !confirm('Confirm again to delete this pull request with {0} comments'.format(comments))) {
678 !confirm('Confirm again to delete this pull request with {0} comments'.format(comments))) {
679 return false;
679 return false;
680 }
680 }
681 }
681 }
682
682
683 if (review_status) {
683 if (review_status) {
684 var $review_status = $preview.find('.automatic-comment');
684 var $review_status = $preview.find('.automatic-comment');
685 var review_status_lbl = $("#comment-inline-form-template input.status_change_radio[value='" + review_status + "']").parent().text().strip();
685 var review_status_lbl = $("#comment-inline-form-template input.status_change_radio[value='" + review_status + "']").parent().text().strip();
686 $review_status.find('.comment-status-label').text(review_status_lbl);
686 $review_status.find('.comment-status-label').text(review_status_lbl);
687 $review_status.show();
687 $review_status.show();
688 }
688 }
689 $preview.find('.comment-text div').text(text);
689 $preview.find('.comment-text div').text(text);
690 $preview.show();
690 $preview.show();
691 $textarea.val('');
691 $textarea.val('');
692 if (f_path && line_no) {
692 if (f_path && line_no) {
693 $form.hide();
693 $form.hide();
694 }
694 }
695
695
696 var postData = {
696 var postData = {
697 'text': text,
697 'text': text,
698 'f_path': f_path,
698 'f_path': f_path,
699 'line': line_no,
699 'line': line_no,
700 'changeset_status': review_status,
700 'changeset_status': review_status,
701 'save_close': pr_close,
701 'save_close': pr_close,
702 'save_delete': pr_delete
702 'save_delete': pr_delete
703 };
703 };
704 var success = function(json_data) {
704 var success = function(json_data) {
705 if (pr_delete) {
705 if (pr_delete) {
706 location = json_data['location'];
706 location = json_data['location'];
707 } else {
707 } else {
708 $comment_div.append(json_data['rendered_text']);
708 $comment_div.append(json_data['rendered_text']);
709 comment_div_state($comment_div, f_path, line_no);
709 comment_div_state($comment_div, f_path, line_no);
710 linkInlineComments($('.firstlink'), $('.comment:first-child'));
710 linkInlineComments($('.firstlink'), $('.comment:first-child'));
711 if ((review_status || pr_close) && !f_path && !line_no) {
711 if ((review_status || pr_close) && !f_path && !line_no) {
712 // Page changed a lot - reload it after closing the submitted form
712 // Page changed a lot - reload it after closing the submitted form
713 comment_div_state($comment_div, f_path, line_no, false);
713 comment_div_state($comment_div, f_path, line_no, false);
714 location.reload(true);
714 location.reload(true);
715 }
715 }
716 }
716 }
717 };
717 };
718 var failure = function(x, s, e) {
718 var failure = function(x, s, e) {
719 $preview.removeClass('submitting').addClass('failed');
719 $preview.removeClass('submitting').addClass('failed');
720 var $status = $preview.find('.comment-submission-status');
720 var $status = $preview.find('.comment-submission-status');
721 $('<span>', {
721 $('<span>', {
722 'title': e,
722 'title': e,
723 text: _TM['Unable to post']
723 text: _TM['Unable to post']
724 }).replaceAll($status.contents());
724 }).replaceAll($status.contents());
725 $('<div>', {
725 $('<div>', {
726 'class': 'btn-group'
726 'class': 'btn-group'
727 }).append(
727 }).append(
728 $('<button>', {
728 $('<button>', {
729 'class': 'btn btn-default btn-xs',
729 'class': 'btn btn-default btn-xs',
730 text: _TM['Retry']
730 text: _TM['Retry']
731 }).click(function() {
731 }).click(function() {
732 $status.text(_TM['Submitting ...']);
732 $status.text(_TM['Submitting ...']);
733 $preview.addClass('submitting').removeClass('failed');
733 $preview.addClass('submitting').removeClass('failed');
734 ajaxPOST(AJAX_COMMENT_URL, postData, success, failure);
734 ajaxPOST(AJAX_COMMENT_URL, postData, success, failure);
735 }),
735 }),
736 $('<button>', {
736 $('<button>', {
737 'class': 'btn btn-default btn-xs',
737 'class': 'btn btn-default btn-xs',
738 text: _TM['Cancel']
738 text: _TM['Cancel']
739 }).click(function() {
739 }).click(function() {
740 comment_div_state($comment_div, f_path, line_no);
740 comment_div_state($comment_div, f_path, line_no);
741 })
741 })
742 ).appendTo($status);
742 ).appendTo($status);
743 };
743 };
744 ajaxPOST(AJAX_COMMENT_URL, postData, success, failure);
744 ajaxPOST(AJAX_COMMENT_URL, postData, success, failure);
745 });
745 });
746
746
747 // add event handler for hide/cancel buttons
747 // add event handler for hide/cancel buttons
748 $form.find('.hide-inline-form').click(function(e) {
748 $form.find('.hide-inline-form').click(function(e) {
749 comment_div_state($comment_div, f_path, line_no);
749 comment_div_state($comment_div, f_path, line_no);
750 });
750 });
751
751
752 tooltip_activate();
752 tooltip_activate();
753 if ($textarea.length > 0) {
753 if ($textarea.length > 0) {
754 MentionsAutoComplete($textarea, _USERS_AC_DATA);
754 MentionsAutoComplete($textarea);
755 }
755 }
756 if (f_path) {
756 if (f_path) {
757 $textarea.focus();
757 $textarea.focus();
758 }
758 }
759 }
759 }
760
760
761
761
762 function deleteComment(comment_id) {
762 function deleteComment(comment_id) {
763 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
763 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
764 var postData = {};
764 var postData = {};
765 var success = function(o) {
765 var success = function(o) {
766 $('#comment-'+comment_id).remove();
766 $('#comment-'+comment_id).remove();
767 // Ignore that this might leave a stray Add button (or have a pending form with another comment) ...
767 // Ignore that this might leave a stray Add button (or have a pending form with another comment) ...
768 }
768 }
769 ajaxPOST(url, postData, success);
769 ajaxPOST(url, postData, success);
770 }
770 }
771
771
772
772
773 /**
773 /**
774 * Double link comments
774 * Double link comments
775 */
775 */
776 var linkInlineComments = function($firstlinks, $comments){
776 var linkInlineComments = function($firstlinks, $comments){
777 if ($comments.length > 0) {
777 if ($comments.length > 0) {
778 $firstlinks.html('<a href="#{0}">First comment</a>'.format($comments.prop('id')));
778 $firstlinks.html('<a href="#{0}">First comment</a>'.format($comments.prop('id')));
779 }
779 }
780 if ($comments.length <= 1) {
780 if ($comments.length <= 1) {
781 return;
781 return;
782 }
782 }
783
783
784 $comments.each(function(i, e){
784 $comments.each(function(i, e){
785 var prev = '';
785 var prev = '';
786 if (i > 0){
786 if (i > 0){
787 var prev_anchor = $($comments.get(i-1)).prop('id');
787 var prev_anchor = $($comments.get(i-1)).prop('id');
788 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
788 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
789 }
789 }
790 var next = '';
790 var next = '';
791 if (i+1 < $comments.length){
791 if (i+1 < $comments.length){
792 var next_anchor = $($comments.get(i+1)).prop('id');
792 var next_anchor = $($comments.get(i+1)).prop('id');
793 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
793 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
794 }
794 }
795 $(this).find('.comment-prev-next-links').html(
795 $(this).find('.comment-prev-next-links').html(
796 '<div class="prev-comment">{0}</div>'.format(prev) +
796 '<div class="prev-comment">{0}</div>'.format(prev) +
797 '<div class="next-comment">{0}</div>'.format(next));
797 '<div class="next-comment">{0}</div>'.format(next));
798 });
798 });
799 }
799 }
800
800
801 /* activate files.html stuff */
801 /* activate files.html stuff */
802 var fileBrowserListeners = function(node_list_url, url_base){
802 var fileBrowserListeners = function(node_list_url, url_base){
803 var $node_filter = $('#node_filter');
803 var $node_filter = $('#node_filter');
804
804
805 var filterTimeout = null;
805 var filterTimeout = null;
806 var nodes = null;
806 var nodes = null;
807
807
808 var initFilter = function(){
808 var initFilter = function(){
809 $('#node_filter_box_loading').show();
809 $('#node_filter_box_loading').show();
810 $('#search_activate_id').hide();
810 $('#search_activate_id').hide();
811 $('#add_node_id').hide();
811 $('#add_node_id').hide();
812 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
812 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
813 .done(function(json) {
813 .done(function(json) {
814 nodes = json.nodes;
814 nodes = json.nodes;
815 $('#node_filter_box_loading').hide();
815 $('#node_filter_box_loading').hide();
816 $('#node_filter_box').show();
816 $('#node_filter_box').show();
817 $node_filter.focus();
817 $node_filter.focus();
818 if($node_filter.hasClass('init')){
818 if($node_filter.hasClass('init')){
819 $node_filter.val('');
819 $node_filter.val('');
820 $node_filter.removeClass('init');
820 $node_filter.removeClass('init');
821 }
821 }
822 })
822 })
823 .fail(function() {
823 .fail(function() {
824 console.log('fileBrowserListeners initFilter failed to load');
824 console.log('fileBrowserListeners initFilter failed to load');
825 })
825 })
826 ;
826 ;
827 }
827 }
828
828
829 var updateFilter = function(e) {
829 var updateFilter = function(e) {
830 return function(){
830 return function(){
831 // Reset timeout
831 // Reset timeout
832 filterTimeout = null;
832 filterTimeout = null;
833 var query = e.currentTarget.value.toLowerCase();
833 var query = e.currentTarget.value.toLowerCase();
834 var match = [];
834 var match = [];
835 var matches = 0;
835 var matches = 0;
836 var matches_max = 20;
836 var matches_max = 20;
837 if (query != ""){
837 if (query != ""){
838 for(var i=0;i<nodes.length;i++){
838 for(var i=0;i<nodes.length;i++){
839 var pos = nodes[i].name.toLowerCase().indexOf(query);
839 var pos = nodes[i].name.toLowerCase().indexOf(query);
840 if(query && pos != -1){
840 if(query && pos != -1){
841 matches++
841 matches++
842 //show only certain amount to not kill browser
842 //show only certain amount to not kill browser
843 if (matches > matches_max){
843 if (matches > matches_max){
844 break;
844 break;
845 }
845 }
846
846
847 var n = nodes[i].name;
847 var n = nodes[i].name;
848 var t = nodes[i].type;
848 var t = nodes[i].type;
849 var n_hl = n.substring(0,pos)
849 var n_hl = n.substring(0,pos)
850 + "<b>{0}</b>".format(n.substring(pos,pos+query.length))
850 + "<b>{0}</b>".format(n.substring(pos,pos+query.length))
851 + n.substring(pos+query.length);
851 + n.substring(pos+query.length);
852 var new_url = url_base.replace('__FPATH__',n);
852 var new_url = url_base.replace('__FPATH__',n);
853 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
853 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
854 }
854 }
855 if(match.length >= matches_max){
855 if(match.length >= matches_max){
856 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
856 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
857 break;
857 break;
858 }
858 }
859 }
859 }
860 }
860 }
861 if(query != ""){
861 if(query != ""){
862 $('#tbody').hide();
862 $('#tbody').hide();
863 $('#tbody_filtered').show();
863 $('#tbody_filtered').show();
864
864
865 if (match.length==0){
865 if (match.length==0){
866 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
866 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
867 }
867 }
868
868
869 $('#tbody_filtered').html(match.join(""));
869 $('#tbody_filtered').html(match.join(""));
870 }
870 }
871 else{
871 else{
872 $('#tbody').show();
872 $('#tbody').show();
873 $('#tbody_filtered').hide();
873 $('#tbody_filtered').hide();
874 }
874 }
875 }
875 }
876 };
876 };
877
877
878 $('#filter_activate').click(function(){
878 $('#filter_activate').click(function(){
879 initFilter();
879 initFilter();
880 });
880 });
881 $node_filter.click(function(){
881 $node_filter.click(function(){
882 if($node_filter.hasClass('init')){
882 if($node_filter.hasClass('init')){
883 $node_filter.val('');
883 $node_filter.val('');
884 $node_filter.removeClass('init');
884 $node_filter.removeClass('init');
885 }
885 }
886 });
886 });
887 $node_filter.keyup(function(e){
887 $node_filter.keyup(function(e){
888 clearTimeout(filterTimeout);
888 clearTimeout(filterTimeout);
889 filterTimeout = setTimeout(updateFilter(e),600);
889 filterTimeout = setTimeout(updateFilter(e),600);
890 });
890 });
891 };
891 };
892
892
893
893
894 var initCodeMirror = function(textarea_id, baseUrl, resetUrl){
894 var initCodeMirror = function(textarea_id, baseUrl, resetUrl){
895 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
895 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
896 mode: "null",
896 mode: "null",
897 lineNumbers: true,
897 lineNumbers: true,
898 indentUnit: 4,
898 indentUnit: 4,
899 autofocus: true
899 autofocus: true
900 });
900 });
901 CodeMirror.modeURL = baseUrl + "/codemirror/mode/%N/%N.js";
901 CodeMirror.modeURL = baseUrl + "/codemirror/mode/%N/%N.js";
902
902
903 $('#reset').click(function(e){
903 $('#reset').click(function(e){
904 window.location=resetUrl;
904 window.location=resetUrl;
905 });
905 });
906
906
907 $('#file_enable').click(function(){
907 $('#file_enable').click(function(){
908 $('#upload_file_container').hide();
908 $('#upload_file_container').hide();
909 $('#filename_container').show();
909 $('#filename_container').show();
910 $('#body').show();
910 $('#body').show();
911 });
911 });
912
912
913 $('#upload_file_enable').click(function(){
913 $('#upload_file_enable').click(function(){
914 $('#upload_file_container').show();
914 $('#upload_file_container').show();
915 $('#filename_container').hide();
915 $('#filename_container').hide();
916 $('#body').hide();
916 $('#body').hide();
917 });
917 });
918
918
919 return myCodeMirror
919 return myCodeMirror
920 };
920 };
921
921
922 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
922 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
923 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
923 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
924 }
924 }
925
925
926
926
927 var _getIdentNode = function(n){
927 var _getIdentNode = function(n){
928 //iterate thrugh nodes until matching interesting node
928 //iterate thrugh nodes until matching interesting node
929
929
930 if (typeof n == 'undefined'){
930 if (typeof n == 'undefined'){
931 return -1
931 return -1
932 }
932 }
933
933
934 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
934 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
935 return n
935 return n
936 }
936 }
937 else{
937 else{
938 return _getIdentNode(n.parentNode);
938 return _getIdentNode(n.parentNode);
939 }
939 }
940 };
940 };
941
941
942 /* generate links for multi line selects that can be shown by files.html page_highlights.
942 /* generate links for multi line selects that can be shown by files.html page_highlights.
943 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
943 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
944 var getSelectionLink = function(e) {
944 var getSelectionLink = function(e) {
945 //get selection from start/to nodes
945 //get selection from start/to nodes
946 if (typeof window.getSelection != "undefined") {
946 if (typeof window.getSelection != "undefined") {
947 var s = window.getSelection();
947 var s = window.getSelection();
948
948
949 var from = _getIdentNode(s.anchorNode);
949 var from = _getIdentNode(s.anchorNode);
950 var till = _getIdentNode(s.focusNode);
950 var till = _getIdentNode(s.focusNode);
951
951
952 var f_int = parseInt(from.id.replace('L',''));
952 var f_int = parseInt(from.id.replace('L',''));
953 var t_int = parseInt(till.id.replace('L',''));
953 var t_int = parseInt(till.id.replace('L',''));
954
954
955 var yoffset = 35;
955 var yoffset = 35;
956 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
956 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
957 if (ranges[0] > ranges[1]){
957 if (ranges[0] > ranges[1]){
958 //highlight from bottom
958 //highlight from bottom
959 yoffset = -yoffset;
959 yoffset = -yoffset;
960 ranges = [ranges[1], ranges[0]];
960 ranges = [ranges[1], ranges[0]];
961 }
961 }
962 var $hl_div = $('div#linktt');
962 var $hl_div = $('div#linktt');
963 // if we select more than 2 lines
963 // if we select more than 2 lines
964 if (ranges[0] != ranges[1]){
964 if (ranges[0] != ranges[1]){
965 if ($hl_div.length) {
965 if ($hl_div.length) {
966 $hl_div.html('');
966 $hl_div.html('');
967 } else {
967 } else {
968 $hl_div = $('<div id="linktt" class="hl-tip-box">');
968 $hl_div = $('<div id="linktt" class="hl-tip-box">');
969 $('body').prepend($hl_div);
969 $('body').prepend($hl_div);
970 }
970 }
971
971
972 $hl_div.append($('<a>').html(_TM['Selection Link']).prop('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
972 $hl_div.append($('<a>').html(_TM['Selection Link']).prop('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
973 var xy = $(till).offset();
973 var xy = $(till).offset();
974 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
974 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
975 $hl_div.show();
975 $hl_div.show();
976 }
976 }
977 else{
977 else{
978 $hl_div.hide();
978 $hl_div.hide();
979 }
979 }
980 }
980 }
981 };
981 };
982
982
983 /**
983 /**
984 * Autocomplete functionality
984 * Autocomplete functionality
985 */
985 */
986
986
987 // Custom search function for the DataSource of users
987 // Custom search function for the DataSource of users
988 var autocompleteMatchUsers = function (sQuery, myUsers) {
988 var autocompleteMatchUsers = function (sQuery, myUsers) {
989 // Case insensitive matching
989 // Case insensitive matching
990 var query = sQuery.toLowerCase();
990 var query = sQuery.toLowerCase();
991 var i = 0;
991 var i = 0;
992 var l = myUsers.length;
992 var l = myUsers.length;
993 var matches = [];
993 var matches = [];
994
994
995 // Match against each name of each contact
995 // Match against each name of each contact
996 for (; i < l; i++) {
996 for (; i < l; i++) {
997 var contact = myUsers[i];
997 var contact = myUsers[i];
998 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
998 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
999 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
999 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1000 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1000 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1001 matches[matches.length] = contact;
1001 matches[matches.length] = contact;
1002 }
1002 }
1003 }
1003 }
1004 return matches;
1004 return matches;
1005 };
1005 };
1006
1006
1007 // Custom search function for the DataSource of userGroups
1007 // Custom search function for the DataSource of userGroups
1008 var autocompleteMatchGroups = function (sQuery, myGroups) {
1008 var autocompleteMatchGroups = function (sQuery, myGroups) {
1009 // Case insensitive matching
1009 // Case insensitive matching
1010 var query = sQuery.toLowerCase();
1010 var query = sQuery.toLowerCase();
1011 var i = 0;
1011 var i = 0;
1012 var l = myGroups.length;
1012 var l = myGroups.length;
1013 var matches = [];
1013 var matches = [];
1014
1014
1015 // Match against each name of each group
1015 // Match against each name of each group
1016 for (; i < l; i++) {
1016 for (; i < l; i++) {
1017 var matched_group = myGroups[i];
1017 var matched_group = myGroups[i];
1018 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1018 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1019 matches[matches.length] = matched_group;
1019 matches[matches.length] = matched_group;
1020 }
1020 }
1021 }
1021 }
1022 return matches;
1022 return matches;
1023 };
1023 };
1024
1024
1025 // Highlight the snippet if it is found in the full text.
1025 // Highlight the snippet if it is found in the full text.
1026 // Snippet must be lowercased already.
1026 // Snippet must be lowercased already.
1027 var autocompleteHighlightMatch = function (full, snippet) {
1027 var autocompleteHighlightMatch = function (full, snippet) {
1028 var matchindex = full.toLowerCase().indexOf(snippet);
1028 var matchindex = full.toLowerCase().indexOf(snippet);
1029 if (matchindex <0)
1029 if (matchindex <0)
1030 return full;
1030 return full;
1031 return full.substring(0, matchindex)
1031 return full.substring(0, matchindex)
1032 + '<span class="select2-match">'
1032 + '<span class="select2-match">'
1033 + full.substr(matchindex, snippet.length)
1033 + full.substr(matchindex, snippet.length)
1034 + '</span>' + full.substring(matchindex + snippet.length);
1034 + '</span>' + full.substring(matchindex + snippet.length);
1035 };
1035 };
1036
1036
1037 // Return html snippet for showing the provided gravatar url
1037 // Return html snippet for showing the provided gravatar url
1038 var gravatar = function(gravatar_lnk, size, cssclass) {
1038 var gravatar = function(gravatar_lnk, size, cssclass) {
1039 if (!gravatar_lnk) {
1039 if (!gravatar_lnk) {
1040 return '';
1040 return '';
1041 }
1041 }
1042 if (gravatar_lnk == 'default') {
1042 if (gravatar_lnk == 'default') {
1043 return '<i class="icon-user {1}" style="font-size: {0}px;"></i>'.format(size, cssclass);
1043 return '<i class="icon-user {1}" style="font-size: {0}px;"></i>'.format(size, cssclass);
1044 }
1044 }
1045 return ('<i class="icon-gravatar {2}"' +
1045 return ('<i class="icon-gravatar {2}"' +
1046 ' style="font-size: {0}px;background-image: url(\'{1}\'); background-size: {0}px"' +
1046 ' style="font-size: {0}px;background-image: url(\'{1}\'); background-size: {0}px"' +
1047 '></i>').format(size, gravatar_lnk, cssclass);
1047 '></i>').format(size, gravatar_lnk, cssclass);
1048 }
1048 }
1049
1049
1050 var autocompleteGravatar = function(res, gravatar_lnk, size, group) {
1050 var autocompleteGravatar = function(res, gravatar_lnk, size, group) {
1051 var elem;
1051 var elem;
1052 if (group !== undefined) {
1052 if (group !== undefined) {
1053 elem = '<i class="perm-gravatar-ac icon-users"></i>';
1053 elem = '<i class="perm-gravatar-ac icon-users"></i>';
1054 } else {
1054 } else {
1055 elem = gravatar(gravatar_lnk, size, "perm-gravatar-ac");
1055 elem = gravatar(gravatar_lnk, size, "perm-gravatar-ac");
1056 }
1056 }
1057 return '<div class="ac-container-wrap">{0}{1}</div>'.format(elem, res);
1057 return '<div class="ac-container-wrap">{0}{1}</div>'.format(elem, res);
1058 }
1058 }
1059
1059
1060 // Custom formatter to highlight the matching letters
1060 // Custom formatter to highlight the matching letters
1061 var autocompleteFormatter = function (oResultData, sQuery, sResultMatch) {
1061 var autocompleteFormatter = function (oResultData, sQuery, sResultMatch) {
1062 var query;
1062 var query;
1063 if (sQuery && sQuery.toLowerCase) // YAHOO AutoComplete
1063 if (sQuery && sQuery.toLowerCase) // YAHOO AutoComplete
1064 query = sQuery.toLowerCase();
1064 query = sQuery.toLowerCase();
1065 else if (sResultMatch && sResultMatch.term) // select2 - parameter names doesn't match
1065 else if (sResultMatch && sResultMatch.term) // select2 - parameter names doesn't match
1066 query = sResultMatch.term.toLowerCase();
1066 query = sResultMatch.term.toLowerCase();
1067
1067
1068 // group
1068 // group
1069 if (oResultData.type == "group") {
1069 if (oResultData.type == "group") {
1070 return autocompleteGravatar(
1070 return autocompleteGravatar(
1071 "{0}: {1}".format(
1071 "{0}: {1}".format(
1072 _TM['Group'],
1072 _TM['Group'],
1073 autocompleteHighlightMatch(oResultData.grname, query)),
1073 autocompleteHighlightMatch(oResultData.grname, query)),
1074 null, null, true);
1074 null, null, true);
1075 }
1075 }
1076
1076
1077 // users
1077 // users
1078 if (oResultData.nname) {
1078 if (oResultData.nname) {
1079 var displayname = autocompleteHighlightMatch(oResultData.nname, query);
1079 var displayname = autocompleteHighlightMatch(oResultData.nname, query);
1080 if (oResultData.fname && oResultData.lname) {
1080 if (oResultData.fname && oResultData.lname) {
1081 displayname = "{0} {1} ({2})".format(
1081 displayname = "{0} {1} ({2})".format(
1082 autocompleteHighlightMatch(oResultData.fname, query),
1082 autocompleteHighlightMatch(oResultData.fname, query),
1083 autocompleteHighlightMatch(oResultData.lname, query),
1083 autocompleteHighlightMatch(oResultData.lname, query),
1084 displayname);
1084 displayname);
1085 }
1085 }
1086
1086
1087 return autocompleteGravatar(displayname, oResultData.gravatar_lnk, oResultData.gravatar_size);
1087 return autocompleteGravatar(displayname, oResultData.gravatar_lnk, oResultData.gravatar_size);
1088 }
1088 }
1089
1089
1090 return '';
1090 return '';
1091 };
1091 };
1092
1092
1093 var SimpleUserAutoComplete = function ($inputElement) {
1093 var SimpleUserAutoComplete = function ($inputElement) {
1094 $inputElement.select2({
1094 $inputElement.select2({
1095 formatInputTooShort: $inputElement.attr('placeholder'),
1095 formatInputTooShort: $inputElement.attr('placeholder'),
1096 initSelection : function (element, callback) {
1096 initSelection : function (element, callback) {
1097 $.ajax({
1097 $.ajax({
1098 url: pyroutes.url('users_and_groups_data'),
1098 url: pyroutes.url('users_and_groups_data'),
1099 dataType: 'json',
1099 dataType: 'json',
1100 data: {
1100 data: {
1101 key: element.val()
1101 key: element.val()
1102 },
1102 },
1103 success: function(data){
1103 success: function(data){
1104 callback(data.results[0]);
1104 callback(data.results[0]);
1105 }
1105 }
1106 });
1106 });
1107 },
1107 },
1108 minimumInputLength: 1,
1108 minimumInputLength: 1,
1109 ajax: {
1109 ajax: {
1110 url: pyroutes.url('users_and_groups_data'),
1110 url: pyroutes.url('users_and_groups_data'),
1111 dataType: 'json',
1111 dataType: 'json',
1112 data: function(term, page){
1112 data: function(term, page){
1113 return {
1113 return {
1114 query: term
1114 query: term
1115 };
1115 };
1116 },
1116 },
1117 results: function (data, page){
1117 results: function (data, page){
1118 return data;
1118 return data;
1119 },
1119 },
1120 cache: true
1120 cache: true
1121 },
1121 },
1122 formatSelection: autocompleteFormatter,
1122 formatSelection: autocompleteFormatter,
1123 formatResult: autocompleteFormatter,
1123 formatResult: autocompleteFormatter,
1124 escapeMarkup: function(m) { return m; },
1124 escapeMarkup: function(m) { return m; },
1125 id: function(item) { return item.nname; },
1125 id: function(item) { return item.nname; },
1126 });
1126 });
1127 }
1127 }
1128
1128
1129 var MembersAutoComplete = function ($inputElement, $typeElement) {
1129 var MembersAutoComplete = function ($inputElement, $typeElement) {
1130
1130
1131 $inputElement.select2({
1131 $inputElement.select2({
1132 placeholder: $inputElement.attr('placeholder'),
1132 placeholder: $inputElement.attr('placeholder'),
1133 minimumInputLength: 1,
1133 minimumInputLength: 1,
1134 ajax: {
1134 ajax: {
1135 url: pyroutes.url('users_and_groups_data'),
1135 url: pyroutes.url('users_and_groups_data'),
1136 dataType: 'json',
1136 dataType: 'json',
1137 data: function(term, page){
1137 data: function(term, page){
1138 return {
1138 return {
1139 query: term,
1139 query: term,
1140 types: 'users,groups'
1140 types: 'users,groups'
1141 };
1141 };
1142 },
1142 },
1143 results: function (data, page){
1143 results: function (data, page){
1144 return data;
1144 return data;
1145 },
1145 },
1146 cache: true
1146 cache: true
1147 },
1147 },
1148 formatSelection: autocompleteFormatter,
1148 formatSelection: autocompleteFormatter,
1149 formatResult: autocompleteFormatter,
1149 formatResult: autocompleteFormatter,
1150 escapeMarkup: function(m) { return m; },
1150 escapeMarkup: function(m) { return m; },
1151 id: function(item) { return item.type == 'user' ? item.nname : item.grname },
1151 id: function(item) { return item.type == 'user' ? item.nname : item.grname },
1152 }).on("select2-selecting", function(e) {
1152 }).on("select2-selecting", function(e) {
1153 // e.choice.id is automatically used as selection value - just set the type of the selection
1153 // e.choice.id is automatically used as selection value - just set the type of the selection
1154 $typeElement.val(e.choice.type);
1154 $typeElement.val(e.choice.type);
1155 });
1155 });
1156 }
1156 }
1157
1157
1158 var MentionsAutoComplete = function ($inputElement, users_list) {
1158 var MentionsAutoComplete = function ($inputElement) {
1159 var $container = $('<div/>').insertAfter($inputElement);
1159 $inputElement.atwho({
1160
1160 at: "@",
1161 var matchUsers = function (sQuery) {
1161 callbacks: {
1162 // use the search string from $inputElement instead of sQuery
1162 remoteFilter: function(query, callback) {
1163 if(!$container.data('search')){
1163 $.getJSON(
1164 // return empty list so the input list isn't shown
1164 pyroutes.url('users_and_groups_data'),
1165 return []
1165 {
1166 }
1166 query: query,
1167 return autocompleteMatchUsers($container.data('search'), users_list);
1167 types: 'users'
1168 }
1168 },
1169
1169 function(data) {
1170 var datasource = new YAHOO.util.FunctionDataSource(matchUsers);
1170 callback(data.results)
1171 var mentionsAC = new YAHOO.widget.AutoComplete($inputElement[0], $container[0], datasource);
1172 mentionsAC.useShadow = false;
1173 mentionsAC.resultTypeList = false;
1174 mentionsAC.animVert = false;
1175 mentionsAC.animHoriz = false;
1176 mentionsAC.animSpeed = 0.1;
1177 mentionsAC.suppressInputUpdate = true;
1178 mentionsAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1179 // use the search string from $inputElement instead of sQuery
1180 return autocompleteFormatter(oResultData, $container.data('search'), sResultMatch);
1181 }
1171 }
1182
1172 );
1183 // Handler for selection of an entry
1173 },
1184 if(mentionsAC.itemSelectEvent){
1174 sorter: function(query, items, searchKey) {
1185 mentionsAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1175 return items;
1186 var myAC = aArgs[0]; // reference back to the AC instance
1187 var elLI = aArgs[1]; // reference to the selected LI element
1188 var oData = aArgs[2]; // object literal of selected item's result data
1189 myAC.getInputEl().value = $container.data('before') + oData.nname + ' ' + $container.data('after');
1190 _setCaretPosition($(myAC.getInputEl()), myAC.dataSource.before.length + oData.nname.length + 1);
1191 });
1192 }
1176 }
1193
1177 },
1194 // Must match utils2.py MENTIONS_REGEX.
1178 displayTpl: "<li>" + autocompleteGravatar('${fname} ${lname} (${nname})', '${gravatar_lnk}', 16) + "</li>",
1195 // Operates on a string from char before @ up to cursor.
1179 insertTpl: "${atwho-at}${nname}"
1196 // Check that the char before @ doesn't look like an email address, and match to end of string.
1197 var mentionRe = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$');
1198
1199 $inputElement.keyup(function(e){
1200 var currentMessage = $inputElement.val();
1201 var currentCaretPosition = $inputElement[0].selectionStart;
1202
1203 $container.data('search', '');
1204 var messageBeforeCaret = currentMessage.substr(0, currentCaretPosition);
1205 var lastAtPos = messageBeforeCaret.lastIndexOf('@');
1206 if(lastAtPos >= 0){
1207 // Search from one char before last @ ... if possible
1208 var m = mentionRe.exec(messageBeforeCaret.substr(Math.max(0, lastAtPos - 1)));
1209 if(m){
1210 $container.data('before', currentMessage.substr(0, lastAtPos + 1));
1211 $container.data('search', currentMessage.substr(lastAtPos + 1, currentCaretPosition - lastAtPos - 1));
1212 $container.data('after', currentMessage.substr(currentCaretPosition));
1213 }
1214 }
1215 });
1180 });
1216 }
1181 };
1217
1182
1218
1183
1219 // Set caret at the given position in the input element
1184 // Set caret at the given position in the input element
1220 function _setCaretPosition($inputElement, caretPos) {
1185 function _setCaretPosition($inputElement, caretPos) {
1221 $inputElement.each(function(){
1186 $inputElement.each(function(){
1222 if(this.createTextRange) { // IE
1187 if(this.createTextRange) { // IE
1223 var range = this.createTextRange();
1188 var range = this.createTextRange();
1224 range.move('character', caretPos);
1189 range.move('character', caretPos);
1225 range.select();
1190 range.select();
1226 }
1191 }
1227 else if(this.selectionStart) { // other recent browsers
1192 else if(this.selectionStart) { // other recent browsers
1228 this.focus();
1193 this.focus();
1229 this.setSelectionRange(caretPos, caretPos);
1194 this.setSelectionRange(caretPos, caretPos);
1230 }
1195 }
1231 else // last resort - very old browser
1196 else // last resort - very old browser
1232 this.focus();
1197 this.focus();
1233 });
1198 });
1234 }
1199 }
1235
1200
1236
1201
1237 var addReviewMember = function(id,fname,lname,nname,gravatar_link,gravatar_size){
1202 var addReviewMember = function(id,fname,lname,nname,gravatar_link,gravatar_size){
1238 var displayname = nname;
1203 var displayname = nname;
1239 if ((fname != "") && (lname != "")) {
1204 if ((fname != "") && (lname != "")) {
1240 displayname = "{0} {1} ({2})".format(fname, lname, nname);
1205 displayname = "{0} {1} ({2})".format(fname, lname, nname);
1241 }
1206 }
1242 var gravatarelm = gravatar(gravatar_link, gravatar_size, "");
1207 var gravatarelm = gravatar(gravatar_link, gravatar_size, "");
1243 // WARNING: the HTML below is duplicate with
1208 // WARNING: the HTML below is duplicate with
1244 // kallithea/templates/pullrequests/pullrequest_show.html
1209 // kallithea/templates/pullrequests/pullrequest_show.html
1245 // If you change something here it should be reflected in the template too.
1210 // If you change something here it should be reflected in the template too.
1246 var element = (
1211 var element = (
1247 ' <li id="reviewer_{2}">\n'+
1212 ' <li id="reviewer_{2}">\n'+
1248 ' <span class="reviewers_member">\n'+
1213 ' <span class="reviewers_member">\n'+
1249 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1214 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1250 ' <span class="reviewer_status" data-toggle="tooltip" title="not_reviewed">\n'+
1215 ' <span class="reviewer_status" data-toggle="tooltip" title="not_reviewed">\n'+
1251 ' <i class="icon-circle changeset-status-not_reviewed"></i>\n'+
1216 ' <i class="icon-circle changeset-status-not_reviewed"></i>\n'+
1252 ' </span>\n'+
1217 ' </span>\n'+
1253 (gravatarelm ?
1218 (gravatarelm ?
1254 ' {0}\n' :
1219 ' {0}\n' :
1255 '')+
1220 '')+
1256 ' <span>{1}</span>\n'+
1221 ' <span>{1}</span>\n'+
1257 ' <a href="#" class="reviewer_member_remove" onclick="removeReviewMember({2})">\n'+
1222 ' <a href="#" class="reviewer_member_remove" onclick="removeReviewMember({2})">\n'+
1258 ' <i class="icon-minus-circled"></i>\n'+
1223 ' <i class="icon-minus-circled"></i>\n'+
1259 ' </a> (add not saved)\n'+
1224 ' </a> (add not saved)\n'+
1260 ' </span>\n'+
1225 ' </span>\n'+
1261 ' </li>\n'
1226 ' </li>\n'
1262 ).format(gravatarelm, displayname, id);
1227 ).format(gravatarelm, displayname, id);
1263 // check if we don't have this ID already in
1228 // check if we don't have this ID already in
1264 var ids = [];
1229 var ids = [];
1265 $('#review_members').find('li').each(function() {
1230 $('#review_members').find('li').each(function() {
1266 ids.push(this.id);
1231 ids.push(this.id);
1267 });
1232 });
1268 if(ids.indexOf('reviewer_'+id) == -1){
1233 if(ids.indexOf('reviewer_'+id) == -1){
1269 //only add if it's not there
1234 //only add if it's not there
1270 $('#review_members').append(element);
1235 $('#review_members').append(element);
1271 }
1236 }
1272 }
1237 }
1273
1238
1274 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1239 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1275 var $li = $('#reviewer_{0}'.format(reviewer_id));
1240 var $li = $('#reviewer_{0}'.format(reviewer_id));
1276 $li.find('div div').css("text-decoration", "line-through");
1241 $li.find('div div').css("text-decoration", "line-through");
1277 $li.find('input').prop('name', 'review_members_removed');
1242 $li.find('input').prop('name', 'review_members_removed');
1278 $li.find('.reviewer_member_remove').replaceWith('&nbsp;(remove not saved)');
1243 $li.find('.reviewer_member_remove').replaceWith('&nbsp;(remove not saved)');
1279 }
1244 }
1280
1245
1281 /* activate auto completion of users as PR reviewers */
1246 /* activate auto completion of users as PR reviewers */
1282 var PullRequestAutoComplete = function ($inputElement) {
1247 var PullRequestAutoComplete = function ($inputElement) {
1283 $inputElement.select2(
1248 $inputElement.select2(
1284 {
1249 {
1285 placeholder: $inputElement.attr('placeholder'),
1250 placeholder: $inputElement.attr('placeholder'),
1286 minimumInputLength: 1,
1251 minimumInputLength: 1,
1287 ajax: {
1252 ajax: {
1288 url: pyroutes.url('users_and_groups_data'),
1253 url: pyroutes.url('users_and_groups_data'),
1289 dataType: 'json',
1254 dataType: 'json',
1290 data: function(term, page){
1255 data: function(term, page){
1291 return {
1256 return {
1292 query: term
1257 query: term
1293 };
1258 };
1294 },
1259 },
1295 results: function (data, page){
1260 results: function (data, page){
1296 return data;
1261 return data;
1297 },
1262 },
1298 cache: true
1263 cache: true
1299 },
1264 },
1300 formatSelection: autocompleteFormatter,
1265 formatSelection: autocompleteFormatter,
1301 formatResult: autocompleteFormatter,
1266 formatResult: autocompleteFormatter,
1302 escapeMarkup: function(m) { return m; },
1267 escapeMarkup: function(m) { return m; },
1303 }).on("select2-selecting", function(e) {
1268 }).on("select2-selecting", function(e) {
1304 addReviewMember(e.choice.id, e.choice.fname, e.choice.lname, e.choice.nname,
1269 addReviewMember(e.choice.id, e.choice.fname, e.choice.lname, e.choice.nname,
1305 e.choice.gravatar_lnk, e.choice.gravatar_size);
1270 e.choice.gravatar_lnk, e.choice.gravatar_size);
1306 $inputElement.select2("close");
1271 $inputElement.select2("close");
1307 e.preventDefault();
1272 e.preventDefault();
1308 });
1273 });
1309 }
1274 }
1310
1275
1311
1276
1312 function addPermAction(perm_type) {
1277 function addPermAction(perm_type) {
1313 var template =
1278 var template =
1314 '<td><input type="radio" value="{1}.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1279 '<td><input type="radio" value="{1}.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1315 '<td><input type="radio" value="{1}.read" checked="checked" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1280 '<td><input type="radio" value="{1}.read" checked="checked" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1316 '<td><input type="radio" value="{1}.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1281 '<td><input type="radio" value="{1}.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1317 '<td><input type="radio" value="{1}.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1282 '<td><input type="radio" value="{1}.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1318 '<td>' +
1283 '<td>' +
1319 '<input class="form-control" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text" placeholder="{2}">' +
1284 '<input class="form-control" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text" placeholder="{2}">' +
1320 '<input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden">' +
1285 '<input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden">' +
1321 '</td>' +
1286 '</td>' +
1322 '<td></td>';
1287 '<td></td>';
1323 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1288 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1324 var next_id = $('.new_members').length;
1289 var next_id = $('.new_members').length;
1325 $last_node.before($('<tr class="new_members">').append(template.format(next_id, perm_type, _TM['Type name of user or member to grant permission'])));
1290 $last_node.before($('<tr class="new_members">').append(template.format(next_id, perm_type, _TM['Type name of user or member to grant permission'])));
1326 MembersAutoComplete($("#perm_new_member_name_"+next_id), $("#perm_new_member_type_"+next_id));
1291 MembersAutoComplete($("#perm_new_member_name_"+next_id), $("#perm_new_member_type_"+next_id));
1327 }
1292 }
1328
1293
1329 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1294 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1330 var success = function (o) {
1295 var success = function (o) {
1331 $('#' + field_id).remove();
1296 $('#' + field_id).remove();
1332 };
1297 };
1333 var failure = function (o) {
1298 var failure = function (o) {
1334 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1299 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1335 };
1300 };
1336 var query_params = {};
1301 var query_params = {};
1337 // put extra data into POST
1302 // put extra data into POST
1338 if (extra_data !== undefined && (typeof extra_data === 'object')){
1303 if (extra_data !== undefined && (typeof extra_data === 'object')){
1339 for(var k in extra_data){
1304 for(var k in extra_data){
1340 query_params[k] = extra_data[k];
1305 query_params[k] = extra_data[k];
1341 }
1306 }
1342 }
1307 }
1343
1308
1344 if (obj_type=='user'){
1309 if (obj_type=='user'){
1345 query_params['user_id'] = obj_id;
1310 query_params['user_id'] = obj_id;
1346 query_params['obj_type'] = 'user';
1311 query_params['obj_type'] = 'user';
1347 }
1312 }
1348 else if (obj_type=='user_group'){
1313 else if (obj_type=='user_group'){
1349 query_params['user_group_id'] = obj_id;
1314 query_params['user_group_id'] = obj_id;
1350 query_params['obj_type'] = 'user_group';
1315 query_params['obj_type'] = 'user_group';
1351 }
1316 }
1352
1317
1353 ajaxPOST(url, query_params, success, failure);
1318 ajaxPOST(url, query_params, success, failure);
1354 };
1319 };
1355
1320
1356 /* Multi selectors */
1321 /* Multi selectors */
1357
1322
1358 var MultiSelectWidget = function(selected_id, available_id, form_id){
1323 var MultiSelectWidget = function(selected_id, available_id, form_id){
1359 var $availableselect = $('#' + available_id);
1324 var $availableselect = $('#' + available_id);
1360 var $selectedselect = $('#' + selected_id);
1325 var $selectedselect = $('#' + selected_id);
1361
1326
1362 //fill available only with those not in selected
1327 //fill available only with those not in selected
1363 var $selectedoptions = $selectedselect.children('option');
1328 var $selectedoptions = $selectedselect.children('option');
1364 $availableselect.children('option').filter(function(i, e){
1329 $availableselect.children('option').filter(function(i, e){
1365 for(var j = 0, node; node = $selectedoptions[j]; j++){
1330 for(var j = 0, node; node = $selectedoptions[j]; j++){
1366 if(node.value == e.value){
1331 if(node.value == e.value){
1367 return true;
1332 return true;
1368 }
1333 }
1369 }
1334 }
1370 return false;
1335 return false;
1371 }).remove();
1336 }).remove();
1372
1337
1373 $('#add_element').click(function(e){
1338 $('#add_element').click(function(e){
1374 $selectedselect.append($availableselect.children('option:selected'));
1339 $selectedselect.append($availableselect.children('option:selected'));
1375 });
1340 });
1376 $('#remove_element').click(function(e){
1341 $('#remove_element').click(function(e){
1377 $availableselect.append($selectedselect.children('option:selected'));
1342 $availableselect.append($selectedselect.children('option:selected'));
1378 });
1343 });
1379
1344
1380 $('#'+form_id).submit(function(){
1345 $('#'+form_id).submit(function(){
1381 $selectedselect.children('option').each(function(i, e){
1346 $selectedselect.children('option').each(function(i, e){
1382 e.selected = 'selected';
1347 e.selected = 'selected';
1383 });
1348 });
1384 });
1349 });
1385 }
1350 }
1386
1351
1387
1352
1388 /**
1353 /**
1389 Branch Sorting callback for select2, modifying the filtered result so prefix
1354 Branch Sorting callback for select2, modifying the filtered result so prefix
1390 matches come before matches in the line.
1355 matches come before matches in the line.
1391 **/
1356 **/
1392 var branchSort = function(results, container, query) {
1357 var branchSort = function(results, container, query) {
1393 if (query.term) {
1358 if (query.term) {
1394 return results.sort(function (a, b) {
1359 return results.sort(function (a, b) {
1395 // Put closed branches after open ones (a bit of a hack ...)
1360 // Put closed branches after open ones (a bit of a hack ...)
1396 var aClosed = a.text.indexOf("(closed)") > -1,
1361 var aClosed = a.text.indexOf("(closed)") > -1,
1397 bClosed = b.text.indexOf("(closed)") > -1;
1362 bClosed = b.text.indexOf("(closed)") > -1;
1398 if (aClosed && !bClosed) {
1363 if (aClosed && !bClosed) {
1399 return 1;
1364 return 1;
1400 }
1365 }
1401 if (bClosed && !aClosed) {
1366 if (bClosed && !aClosed) {
1402 return -1;
1367 return -1;
1403 }
1368 }
1404
1369
1405 // Put early (especially prefix) matches before later matches
1370 // Put early (especially prefix) matches before later matches
1406 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1371 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1407 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1372 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1408 if (aPos < bPos) {
1373 if (aPos < bPos) {
1409 return -1;
1374 return -1;
1410 }
1375 }
1411 if (bPos < aPos) {
1376 if (bPos < aPos) {
1412 return 1;
1377 return 1;
1413 }
1378 }
1414
1379
1415 // Default sorting
1380 // Default sorting
1416 if (a.text > b.text) {
1381 if (a.text > b.text) {
1417 return 1;
1382 return 1;
1418 }
1383 }
1419 if (a.text < b.text) {
1384 if (a.text < b.text) {
1420 return -1;
1385 return -1;
1421 }
1386 }
1422 return 0;
1387 return 0;
1423 });
1388 });
1424 }
1389 }
1425 return results;
1390 return results;
1426 };
1391 };
1427
1392
1428 var prefixFirstSort = function(results, container, query) {
1393 var prefixFirstSort = function(results, container, query) {
1429 if (query.term) {
1394 if (query.term) {
1430 return results.sort(function (a, b) {
1395 return results.sort(function (a, b) {
1431 // if parent node, no sorting
1396 // if parent node, no sorting
1432 if (a.children != undefined || b.children != undefined) {
1397 if (a.children != undefined || b.children != undefined) {
1433 return 0;
1398 return 0;
1434 }
1399 }
1435
1400
1436 // Put prefix matches before matches in the line
1401 // Put prefix matches before matches in the line
1437 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1402 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1438 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1403 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1439 if (aPos === 0 && bPos !== 0) {
1404 if (aPos === 0 && bPos !== 0) {
1440 return -1;
1405 return -1;
1441 }
1406 }
1442 if (bPos === 0 && aPos !== 0) {
1407 if (bPos === 0 && aPos !== 0) {
1443 return 1;
1408 return 1;
1444 }
1409 }
1445
1410
1446 // Default sorting
1411 // Default sorting
1447 if (a.text > b.text) {
1412 if (a.text > b.text) {
1448 return 1;
1413 return 1;
1449 }
1414 }
1450 if (a.text < b.text) {
1415 if (a.text < b.text) {
1451 return -1;
1416 return -1;
1452 }
1417 }
1453 return 0;
1418 return 0;
1454 });
1419 });
1455 }
1420 }
1456 return results;
1421 return results;
1457 };
1422 };
1458
1423
1459 /* Helper for jQuery DataTables */
1424 /* Helper for jQuery DataTables */
1460
1425
1461 var updateRowCountCallback = function updateRowCountCallback($elem, onlyDisplayed) {
1426 var updateRowCountCallback = function updateRowCountCallback($elem, onlyDisplayed) {
1462 return function drawCallback() {
1427 return function drawCallback() {
1463 var info = this.api().page.info(),
1428 var info = this.api().page.info(),
1464 count = onlyDisplayed === true ? info.recordsDisplay : info.recordsTotal;
1429 count = onlyDisplayed === true ? info.recordsDisplay : info.recordsTotal;
1465 $elem.html(count);
1430 $elem.html(count);
1466 }
1431 }
1467 };
1432 };
1468
1433
1469
1434
1470 /**
1435 /**
1471 * activate changeset parent/child navigation links
1436 * activate changeset parent/child navigation links
1472 */
1437 */
1473 var activate_parent_child_links = function(){
1438 var activate_parent_child_links = function(){
1474
1439
1475 $('.parent-child-link').on('click', function(e){
1440 $('.parent-child-link').on('click', function(e){
1476 var $this = $(this);
1441 var $this = $(this);
1477 //fetch via ajax what is going to be the next link, if we have
1442 //fetch via ajax what is going to be the next link, if we have
1478 //>1 links show them to user to choose
1443 //>1 links show them to user to choose
1479 if(!$this.hasClass('disabled')){
1444 if(!$this.hasClass('disabled')){
1480 $.ajax({
1445 $.ajax({
1481 url: $this.data('ajax-url'),
1446 url: $this.data('ajax-url'),
1482 success: function(data) {
1447 success: function(data) {
1483 var repo_name = $this.data('reponame');
1448 var repo_name = $this.data('reponame');
1484 if(data.results.length === 0){
1449 if(data.results.length === 0){
1485 $this.addClass('disabled');
1450 $this.addClass('disabled');
1486 $this.text(_TM['No revisions']);
1451 $this.text(_TM['No revisions']);
1487 }
1452 }
1488 if(data.results.length === 1){
1453 if(data.results.length === 1){
1489 var commit = data.results[0];
1454 var commit = data.results[0];
1490 window.location = pyroutes.url('changeset_home', {'repo_name': repo_name, 'revision': commit.raw_id});
1455 window.location = pyroutes.url('changeset_home', {'repo_name': repo_name, 'revision': commit.raw_id});
1491 }
1456 }
1492 else if(data.results.length > 1){
1457 else if(data.results.length > 1){
1493 $this.addClass('disabled');
1458 $this.addClass('disabled');
1494 $this.addClass('double');
1459 $this.addClass('double');
1495 var template =
1460 var template =
1496 ($this.data('linktype') == 'parent' ? '<i class="icon-left-open"/> ' : '') +
1461 ($this.data('linktype') == 'parent' ? '<i class="icon-left-open"/> ' : '') +
1497 '<a title="__title__" href="__url__">__rev__</a>' +
1462 '<a title="__title__" href="__url__">__rev__</a>' +
1498 ($this.data('linktype') == 'child' ? ' <i class="icon-right-open"/>' : '');
1463 ($this.data('linktype') == 'child' ? ' <i class="icon-right-open"/>' : '');
1499 var _html = [];
1464 var _html = [];
1500 for(var i = 0; i < data.results.length; i++){
1465 for(var i = 0; i < data.results.length; i++){
1501 _html.push(template
1466 _html.push(template
1502 .replace('__rev__', 'r{0}:{1}'.format(data.results[i].revision, data.results[i].raw_id.substr(0, 6)))
1467 .replace('__rev__', 'r{0}:{1}'.format(data.results[i].revision, data.results[i].raw_id.substr(0, 6)))
1503 .replace('__title__', data.results[i].message)
1468 .replace('__title__', data.results[i].message)
1504 .replace('__url__', pyroutes.url('changeset_home', {
1469 .replace('__url__', pyroutes.url('changeset_home', {
1505 'repo_name': repo_name,
1470 'repo_name': repo_name,
1506 'revision': data.results[i].raw_id}))
1471 'revision': data.results[i].raw_id}))
1507 );
1472 );
1508 }
1473 }
1509 $this.html(_html.join('<br/>'));
1474 $this.html(_html.join('<br/>'));
1510 }
1475 }
1511 }
1476 }
1512 });
1477 });
1513 e.preventDefault();
1478 e.preventDefault();
1514 }
1479 }
1515 });
1480 });
1516 }
1481 }
@@ -1,28 +1,29 b''
1 /*!
1 /*!
2 * Don't edit the css file directly.
2 * Don't edit the css file directly.
3 *
3 *
4 * Instead, edit the less file(s) and regenerate the css:
4 * Instead, edit the less file(s) and regenerate the css:
5 *
5 *
6 * npm install
6 * npm install
7 * npm run less
7 * npm run less
8 *
8 *
9 */
9 */
10
10
11 /* 3rd party styles */
11 /* 3rd party styles */
12 @import "node_modules/bootstrap/less/bootstrap.less";
12 @import "node_modules/bootstrap/less/bootstrap.less";
13 @import (inline) "node_modules/datatables.net-bs/css/dataTables.bootstrap.css";
13 @import (inline) "node_modules/datatables.net-bs/css/dataTables.bootstrap.css";
14 @import (inline) "node_modules/at.js/dist/css/jquery.atwho.css";
14 @import (less) "node_modules/select2/select2.css";
15 @import (less) "node_modules/select2/select2.css";
15 @import (less) "node_modules/select2-bootstrap-css/select2-bootstrap.css";
16 @import (less) "node_modules/select2-bootstrap-css/select2-bootstrap.css";
16 @import (less) "tmp/pygments.css";
17 @import (less) "tmp/pygments.css";
17 @import (less) "../fontello/css/kallithea.css";
18 @import (less) "../fontello/css/kallithea.css";
18
19
19 /* kallithea styles */
20 /* kallithea styles */
20 @import "kallithea-variables.less";
21 @import "kallithea-variables.less";
21 @import "kallithea-labels.less";
22 @import "kallithea-labels.less";
22 @import "yui-ac.less";
23 @import "yui-ac.less";
23 @import "kallithea-select2.less";
24 @import "kallithea-select2.less";
24 @import "kallithea-diff.less";
25 @import "kallithea-diff.less";
25 @import "style.less";
26 @import "style.less";
26
27
27 /* finally, import the optional theme file with local customizations */
28 /* finally, import the optional theme file with local customizations */
28 @import (optional) "theme.less";
29 @import (optional) "theme.less";
@@ -1,931 +1,937 b''
1 body {
1 body {
2 background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
2 background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
3 }
3 }
4
4
5 /* pseude content that should not be selected or copied by the user */
5 /* pseude content that should not be selected or copied by the user */
6 [data-pseudo-content]:before {
6 [data-pseudo-content]:before {
7 content: attr(data-pseudo-content);
7 content: attr(data-pseudo-content);
8 }
8 }
9
9
10 /* class for texts where newlines should be preserved, for very light-weight ascii art markup (like pull request descriptions) */
10 /* class for texts where newlines should be preserved, for very light-weight ascii art markup (like pull request descriptions) */
11 .formatted-fixed {
11 .formatted-fixed {
12 font-family: @font-family-monospace;
12 font-family: @font-family-monospace;
13 white-space: pre-wrap;
13 white-space: pre-wrap;
14 }
14 }
15
15
16 /* use monospace for changeset hashes */
16 /* use monospace for changeset hashes */
17 .changeset_hash {
17 .changeset_hash {
18 font-family: @font-family-monospace;
18 font-family: @font-family-monospace;
19 }
19 }
20
20
21 /* Note: class 'icon-empty' or 'icon-gravatar' can be used to get icon-ish styling without an actual glyph */
21 /* Note: class 'icon-empty' or 'icon-gravatar' can be used to get icon-ish styling without an actual glyph */
22 i[class^='icon-empty'],
22 i[class^='icon-empty'],
23 i[class^='icon-gravatar'] {
23 i[class^='icon-gravatar'] {
24 background-repeat: no-repeat;
24 background-repeat: no-repeat;
25 background-position: center;
25 background-position: center;
26 display: inline-block;
26 display: inline-block;
27 min-width: 16px;
27 min-width: 16px;
28 min-height: 16px;
28 min-height: 16px;
29 margin: -2px 0 -4px 0;
29 margin: -2px 0 -4px 0;
30 }
30 }
31
31
32 .inline-comments-general.show-general-status .hidden.general-only {
32 .inline-comments-general.show-general-status .hidden.general-only {
33 display: block !important;
33 display: block !important;
34 }
34 }
35 .truncate {
35 .truncate {
36 white-space: nowrap;
36 white-space: nowrap;
37 overflow: hidden;
37 overflow: hidden;
38 text-overflow: ellipsis;
38 text-overflow: ellipsis;
39 -o-text-overflow: ellipsis;
39 -o-text-overflow: ellipsis;
40 -ms-text-overflow: ellipsis;
40 -ms-text-overflow: ellipsis;
41 }
41 }
42 .truncate.autoexpand:hover {
42 .truncate.autoexpand:hover {
43 overflow: visible;
43 overflow: visible;
44 }
44 }
45
45
46 /* show comment anchors when hovering over panel-heading */
46 /* show comment anchors when hovering over panel-heading */
47 a.permalink {
47 a.permalink {
48 visibility: hidden;
48 visibility: hidden;
49 }
49 }
50 .panel-heading:hover .permalink {
50 .panel-heading:hover .permalink {
51 visibility: visible;
51 visibility: visible;
52 }
52 }
53
53
54 .navbar-inverse {
54 .navbar-inverse {
55 border: none;
55 border: none;
56 }
56 }
57
57
58 /* logo */
58 /* logo */
59 nav.navbar.mainmenu > .navbar-header > .navbar-brand {
59 nav.navbar.mainmenu > .navbar-header > .navbar-brand {
60 font-size: 20px;
60 font-size: 20px;
61 padding-top: 12px;
61 padding-top: 12px;
62 > .branding:before {
62 > .branding:before {
63 content: "";
63 content: "";
64 display: inline-block;
64 display: inline-block;
65 margin-right: .2em;
65 margin-right: .2em;
66 background-image: url(@kallithea-logo-url);
66 background-image: url(@kallithea-logo-url);
67 width: @kallithea-logo-width;
67 width: @kallithea-logo-width;
68 height: @kallithea-logo-height;
68 height: @kallithea-logo-height;
69 margin-bottom: -@kallithea-logo-bottom;
69 margin-bottom: -@kallithea-logo-bottom;
70 margin-top: -12px;
70 margin-top: -12px;
71 }
71 }
72 }
72 }
73
73
74 /* code highlighting */
74 /* code highlighting */
75 /* don't use bootstrap style for code blocks */
75 /* don't use bootstrap style for code blocks */
76 .code-highlighttable pre {
76 .code-highlighttable pre {
77 background: inherit;
77 background: inherit;
78 border: 0;
78 border: 0;
79 }
79 }
80
80
81 /* every direct child of a panel, that is not .panel-heading, should auto
81 /* every direct child of a panel, that is not .panel-heading, should auto
82 * overflow to prevent overflowing of elements like text boxes and tables */
82 * overflow to prevent overflowing of elements like text boxes and tables */
83 .panel > :not(.panel-heading){
83 .panel > :not(.panel-heading){
84 overflow-x: auto;
84 overflow-x: auto;
85 min-height: 0.01%;
85 min-height: 0.01%;
86 }
86 }
87
87
88 /* allow other exceptions to automatic overflow-x */
88 /* allow other exceptions to automatic overflow-x */
89 .panel > .overflow-x-visible {
89 .panel > .overflow-x-visible {
90 overflow-x: visible;
90 overflow-x: visible;
91 }
91 }
92
92
93 /* margin below top level panels */
93 /* margin below top level panels */
94 #main > .panel {
94 #main > .panel {
95 margin-bottom: @kallithea-panel-margin;
95 margin-bottom: @kallithea-panel-margin;
96 }
96 }
97
97
98 /* search highlighting */
98 /* search highlighting */
99 div.search-code-body pre .match {
99 div.search-code-body pre .match {
100 background-color: @highlight-color;
100 background-color: @highlight-color;
101 }
101 }
102 div.search-code-body pre .break {
102 div.search-code-body pre .break {
103 background-color: @highlight-line-color;
103 background-color: @highlight-line-color;
104 width: 100%;
104 width: 100%;
105 display: block;
105 display: block;
106 }
106 }
107
107
108 /* use @alert-danger-text for form error messages and .alert-danger for the input element */
108 /* use @alert-danger-text for form error messages and .alert-danger for the input element */
109 .form-group .error-message {
109 .form-group .error-message {
110 color: @alert-danger-text;
110 color: @alert-danger-text;
111 display: inline-block;
111 display: inline-block;
112 padding-top: 5px;
112 padding-top: 5px;
113 &:empty{
113 &:empty{
114 display: none;
114 display: none;
115 }
115 }
116 }
116 }
117 input.error {
117 input.error {
118 .alert-danger;
118 .alert-danger;
119 }
119 }
120
120
121 /* datatable */
121 /* datatable */
122 .dataTables_left {
122 .dataTables_left {
123 .pull-left;
123 .pull-left;
124 }
124 }
125 .dataTables_right {
125 .dataTables_right {
126 .pull-right;
126 .pull-right;
127 }
127 }
128
128
129 /* make all datatable paginations small */
129 /* make all datatable paginations small */
130 .dataTables_paginate .pagination {
130 .dataTables_paginate .pagination {
131 .pagination-sm;
131 .pagination-sm;
132 }
132 }
133
133
134 /* show column sort icons in our font ... and before column header */
134 /* show column sort icons in our font ... and before column header */
135 table.dataTable {
135 table.dataTable {
136 .sorting_asc:before {
136 .sorting_asc:before {
137 font-family: "kallithea";
137 font-family: "kallithea";
138 content: "\23f6";
138 content: "\23f6";
139 padding-right: 8px;
139 padding-right: 8px;
140 }
140 }
141 .sorting_desc:before {
141 .sorting_desc:before {
142 font-family: "kallithea";
142 font-family: "kallithea";
143 content: "\23f7";
143 content: "\23f7";
144 padding-right: 8px;
144 padding-right: 8px;
145 }
145 }
146 .sorting:before {
146 .sorting:before {
147 font-family: "kallithea";
147 font-family: "kallithea";
148 content: "\2195";
148 content: "\2195";
149 padding-right: 8px;
149 padding-right: 8px;
150 opacity: 0.5;
150 opacity: 0.5;
151 }
151 }
152 .sorting_asc:after,
152 .sorting_asc:after,
153 .sorting_desc:after,
153 .sorting_desc:after,
154 .sorting:after {
154 .sorting:after {
155 content: "" !important;
155 content: "" !important;
156 }
156 }
157 }
157 }
158
158
159 /* _dt_elements.html styling - some submit buttons have their own form but should still be shown inline */
159 /* _dt_elements.html styling - some submit buttons have their own form but should still be shown inline */
160 table.dataTable td > .btn + form {
160 table.dataTable td > .btn + form {
161 display: inline;
161 display: inline;
162 }
162 }
163
163
164 table.dataTable .dt_repo_pending {
164 table.dataTable .dt_repo_pending {
165 opacity: 0.5;
165 opacity: 0.5;
166 }
166 }
167
167
168 /* language bars (summary page) */
168 /* language bars (summary page) */
169 #lang_stats {
169 #lang_stats {
170 .progress-bar {
170 .progress-bar {
171 min-width: 15px;
171 min-width: 15px;
172 border-top-right-radius: 8px;
172 border-top-right-radius: 8px;
173 border-bottom-right-radius: 8px;
173 border-bottom-right-radius: 8px;
174 }
174 }
175 td {
175 td {
176 padding: 1px 0 !important;
176 padding: 1px 0 !important;
177 }
177 }
178 }
178 }
179
179
180 /* use pointer cursor for expand_commit */
180 /* use pointer cursor for expand_commit */
181 .expand_commit .icon-align-left {
181 .expand_commit .icon-align-left {
182 cursor: pointer;
182 cursor: pointer;
183 color: #999;
183 color: #999;
184 }
184 }
185
185
186 /* don't break author, date and comment cells into multiple lines in changeset table */
186 /* don't break author, date and comment cells into multiple lines in changeset table */
187 table.changesets {
187 table.changesets {
188 .author,
188 .author,
189 .date,
189 .date,
190 .comments {
190 .comments {
191 white-space: nowrap;
191 white-space: nowrap;
192 }
192 }
193 }
193 }
194
194
195 /* textareas should be at least 100px high and 400px wide */
195 /* textareas should be at least 100px high and 400px wide */
196 textarea.form-control {
196 textarea.form-control {
197 font-family: @font-family-monospace;
197 font-family: @font-family-monospace;
198 min-height: 100px;
198 min-height: 100px;
199 min-width: 400px;
199 min-width: 400px;
200 }
200 }
201
201
202 /* add some space between the code-browser icons and the file names */
202 /* add some space between the code-browser icons and the file names */
203 .browser-dir > i[class^='icon-'],
203 .browser-dir > i[class^='icon-'],
204 .submodule-dir > i[class^='icon-'],
204 .submodule-dir > i[class^='icon-'],
205 .browser-file > i[class^='icon-'] {
205 .browser-file > i[class^='icon-'] {
206 padding-right: 0.3em;
206 padding-right: 0.3em;
207 }
207 }
208
208
209 div.panel-primary {
209 div.panel-primary {
210 border: none;
210 border: none;
211 }
211 }
212
212
213 /* no extra vertical margin */
213 /* no extra vertical margin */
214 #content div.panel ul.pagination {
214 #content div.panel ul.pagination {
215 margin: 0;
215 margin: 0;
216 }
216 }
217
217
218 /* remove margin below footer */
218 /* remove margin below footer */
219 .navbar.footer {
219 .navbar.footer {
220 margin-bottom: 0;
220 margin-bottom: 0;
221 }
221 }
222
222
223 .user-menu {
223 .user-menu {
224 padding: 0 !important;
224 padding: 0 !important;
225 }
225 }
226 #quick_login {
226 #quick_login {
227 width: 360px;
227 width: 360px;
228 margin-top: 15px;
228 margin-top: 15px;
229 min-height: 110px;
229 min-height: 110px;
230 }
230 }
231 #quick_login input#username,
231 #quick_login input#username,
232 #quick_login input#password {
232 #quick_login input#password {
233 display: block;
233 display: block;
234 margin: 5px 0 10px;
234 margin: 5px 0 10px;
235 }
235 }
236 #quick_login .password_forgotten a,
236 #quick_login .password_forgotten a,
237 #quick_login .register a {
237 #quick_login .register a {
238 padding: 0 !important;
238 padding: 0 !important;
239 line-height: 25px !important;
239 line-height: 25px !important;
240 float: left;
240 float: left;
241 clear: both;
241 clear: both;
242 }
242 }
243 #quick_login .submit {
243 #quick_login .submit {
244 float: right;
244 float: right;
245 }
245 }
246 #quick_login .submit input#sign_in {
246 #quick_login .submit input#sign_in {
247 margin-top: 5px;
247 margin-top: 5px;
248 }
248 }
249 #quick_login > .pull-left {
249 #quick_login > .pull-left {
250 width: 170px;
250 width: 170px;
251 }
251 }
252 #quick_login > .pull-right {
252 #quick_login > .pull-right {
253 width: 140px;
253 width: 140px;
254 }
254 }
255 #quick_login .full_name {
255 #quick_login .full_name {
256 font-weight: bold;
256 font-weight: bold;
257 padding: 3px;
257 padding: 3px;
258 }
258 }
259 #quick_login .email {
259 #quick_login .email {
260 padding: 3px 3px 3px 0;
260 padding: 3px 3px 3px 0;
261 }
261 }
262 #quick_login :not(input) {
262 #quick_login :not(input) {
263 color: @kallithea-theme-inverse-color;
263 color: @kallithea-theme-inverse-color;
264 padding-bottom: 3px;
264 padding-bottom: 3px;
265 }
265 }
266
266
267 #journal .journal_user {
267 #journal .journal_user {
268 color: #747474;
268 color: #747474;
269 font-size: 14px;
269 font-size: 14px;
270 font-weight: bold;
270 font-weight: bold;
271 height: 30px;
271 height: 30px;
272 }
272 }
273 #journal .journal_user.deleted {
273 #journal .journal_user.deleted {
274 color: #747474;
274 color: #747474;
275 font-size: 14px;
275 font-size: 14px;
276 font-weight: normal;
276 font-weight: normal;
277 height: 30px;
277 height: 30px;
278 font-style: italic;
278 font-style: italic;
279 }
279 }
280 #journal .journal_icon {
280 #journal .journal_icon {
281 clear: both;
281 clear: both;
282 float: left;
282 float: left;
283 padding-right: 4px;
283 padding-right: 4px;
284 padding-top: 3px;
284 padding-top: 3px;
285 }
285 }
286 #journal .journal_action {
286 #journal .journal_action {
287 padding-top: 4px;
287 padding-top: 4px;
288 min-height: 2px;
288 min-height: 2px;
289 float: left;
289 float: left;
290 }
290 }
291 #journal .journal_action_params {
291 #journal .journal_action_params {
292 clear: left;
292 clear: left;
293 padding-left: 22px;
293 padding-left: 22px;
294 }
294 }
295 #journal .date {
295 #journal .date {
296 clear: both;
296 clear: both;
297 color: #777777;
297 color: #777777;
298 font-size: 11px;
298 font-size: 11px;
299 padding-left: 22px;
299 padding-left: 22px;
300 }
300 }
301 #journal .journal_repo_name {
301 #journal .journal_repo_name {
302 font-weight: bold;
302 font-weight: bold;
303 font-size: 1.1em;
303 font-size: 1.1em;
304 }
304 }
305 #journal .compare_view {
305 #journal .compare_view {
306 padding: 5px 0px 5px 0px;
306 padding: 5px 0px 5px 0px;
307 width: 95px;
307 width: 95px;
308 }
308 }
309 .trending_language_tbl,
309 .trending_language_tbl,
310 .trending_language_tbl td {
310 .trending_language_tbl td {
311 border: 0 !important;
311 border: 0 !important;
312 margin: 0 !important;
312 margin: 0 !important;
313 padding: 0 !important;
313 padding: 0 !important;
314 }
314 }
315 .trending_language_tbl,
315 .trending_language_tbl,
316 .trending_language_tbl tr {
316 .trending_language_tbl tr {
317 border-spacing: 1px;
317 border-spacing: 1px;
318 }
318 }
319 h3.files_location {
319 h3.files_location {
320 font-size: 1.8em;
320 font-size: 1.8em;
321 font-weight: 700;
321 font-weight: 700;
322 border-bottom: none !important;
322 border-bottom: none !important;
323 margin: 10px 0 !important;
323 margin: 10px 0 !important;
324 }
324 }
325 .file_history {
325 .file_history {
326 padding-top: 10px;
326 padding-top: 10px;
327 font-size: 16px;
327 font-size: 16px;
328 }
328 }
329 .file_author {
329 .file_author {
330 float: left;
330 float: left;
331 }
331 }
332 .file_author .item {
332 .file_author .item {
333 float: left;
333 float: left;
334 padding: 5px;
334 padding: 5px;
335 color: #888;
335 color: #888;
336 }
336 }
337 table#updaterevs-table tr.mergerow,
337 table#updaterevs-table tr.mergerow,
338 table#updaterevs-table tr.out-of-range,
338 table#updaterevs-table tr.out-of-range,
339 table#changesets tr.mergerow,
339 table#changesets tr.mergerow,
340 table#changesets tr.out-of-range {
340 table#changesets tr.out-of-range {
341 opacity: 0.6;
341 opacity: 0.6;
342 }
342 }
343 .issue-tracker-link {
343 .issue-tracker-link {
344 color: #3F6F9F;
344 color: #3F6F9F;
345 font-weight: bold !important;
345 font-weight: bold !important;
346 }
346 }
347 /* changeset statuses (must be the same name as the status) */
347 /* changeset statuses (must be the same name as the status) */
348 .changeset-status-not_reviewed {
348 .changeset-status-not_reviewed {
349 color: #bababa;
349 color: #bababa;
350 }
350 }
351 .changeset-status-approved {
351 .changeset-status-approved {
352 color: #81ba51;
352 color: #81ba51;
353 }
353 }
354 .changeset-status-rejected {
354 .changeset-status-rejected {
355 color: #d06060;
355 color: #d06060;
356 }
356 }
357 .changeset-status-under_review {
357 .changeset-status-under_review {
358 color: #ffc71e;
358 color: #ffc71e;
359 }
359 }
360
360
361 #repo_size {
361 #repo_size {
362 display: block;
362 display: block;
363 margin-top: 4px;
363 margin-top: 4px;
364 color: #666;
364 color: #666;
365 float: right;
365 float: right;
366 }
366 }
367 .currently_following {
367 .currently_following {
368 padding-left: 10px;
368 padding-left: 10px;
369 padding-bottom: 5px;
369 padding-bottom: 5px;
370 }
370 }
371 #switch_repos {
371 #switch_repos {
372 position: absolute;
372 position: absolute;
373 height: 25px;
373 height: 25px;
374 z-index: 1;
374 z-index: 1;
375 }
375 }
376 #switch_repos select {
376 #switch_repos select {
377 min-width: 150px;
377 min-width: 150px;
378 max-height: 250px;
378 max-height: 250px;
379 z-index: 1;
379 z-index: 1;
380 }
380 }
381 table#permissions_manage span.private_repo_msg {
381 table#permissions_manage span.private_repo_msg {
382 font-size: 0.8em;
382 font-size: 0.8em;
383 opacity: 0.6;
383 opacity: 0.6;
384 }
384 }
385 table#permissions_manage td.private_repo_msg {
385 table#permissions_manage td.private_repo_msg {
386 font-size: 0.8em;
386 font-size: 0.8em;
387 }
387 }
388 table#permissions_manage tr#add_perm_input td {
388 table#permissions_manage tr#add_perm_input td {
389 vertical-align: middle;
389 vertical-align: middle;
390 }
390 }
391 div.gravatar {
391 div.gravatar {
392 float: left;
392 float: left;
393 background-color: #FFF;
393 background-color: #FFF;
394 margin-right: 0.7em;
394 margin-right: 0.7em;
395 padding: 1px 1px 1px 1px;
395 padding: 1px 1px 1px 1px;
396 line-height: 0;
396 line-height: 0;
397 border-radius: 3px;
397 border-radius: 3px;
398 }
398 }
399 div.gravatar img {
399 div.gravatar img {
400 border-radius: 2px;
400 border-radius: 2px;
401 }
401 }
402 .panel-body.settings .nav-pills > :not(.active) > a {
402 .panel-body.settings .nav-pills > :not(.active) > a {
403 color: inherit;
403 color: inherit;
404 }
404 }
405 .panel-body.no-padding {
405 .panel-body.no-padding {
406 padding: 0;
406 padding: 0;
407 }
407 }
408 .panel-body ~ .panel-body {
408 .panel-body ~ .panel-body {
409 padding-top: 0;
409 padding-top: 0;
410 }
410 }
411 .panel-body.no-padding ~ .panel-body {
411 .panel-body.no-padding ~ .panel-body {
412 padding-top: 15px;
412 padding-top: 15px;
413 }
413 }
414 .panel-body > :last-child {
414 .panel-body > :last-child {
415 margin-bottom: 0;
415 margin-bottom: 0;
416 }
416 }
417 .panel-body.settings .text-muted {
417 .panel-body.settings .text-muted {
418 margin: 5px 0;
418 margin: 5px 0;
419 }
419 }
420 ins,
420 ins,
421 div.options a:hover {
421 div.options a:hover {
422 text-decoration: none;
422 text-decoration: none;
423 }
423 }
424 img,
424 img,
425 nav.navbar #quick li a:hover span.normal,
425 nav.navbar #quick li a:hover span.normal,
426 #clone_url,
426 #clone_url,
427 #clone_url_id {
427 #clone_url_id {
428 border: none;
428 border: none;
429 }
429 }
430 img.icon,
430 img.icon,
431 .right .merge img {
431 .right .merge img {
432 vertical-align: bottom;
432 vertical-align: bottom;
433 }
433 }
434 #content div.panel div.panel-heading ul.links,
434 #content div.panel div.panel-heading ul.links,
435 #content div.panel div.message div.dismiss {
435 #content div.panel div.message div.dismiss {
436 float: right;
436 float: right;
437 margin: 0;
437 margin: 0;
438 padding: 0;
438 padding: 0;
439 }
439 }
440 nav.navbar #home,
440 nav.navbar #home,
441 #content div.panel ul.left,
441 #content div.panel ul.left,
442 #content div.panel ol.left,
442 #content div.panel ol.left,
443 div#commit_history,
443 div#commit_history,
444 div#legend_data,
444 div#legend_data,
445 div#legend_container,
445 div#legend_container,
446 div#legend_choices {
446 div#legend_choices {
447 float: left;
447 float: left;
448 }
448 }
449
449
450 /* set size for statistics charts */
450 /* set size for statistics charts */
451 #commit_history {
451 #commit_history {
452 width: 450px;
452 width: 450px;
453 height: 300px;
453 height: 300px;
454 }
454 }
455 #overview {
455 #overview {
456 clear: both;
456 clear: both;
457 width: 450px;
457 width: 450px;
458 height: 100px;
458 height: 100px;
459 }
459 }
460
460
461 #content #left #menu ul.closed,
461 #content #left #menu ul.closed,
462 #content #left #menu li ul.collapsed,
462 #content #left #menu li ul.collapsed,
463 .yui-tt-shadow {
463 .yui-tt-shadow {
464 display: none;
464 display: none;
465 }
465 }
466 #content #left #menu ul.opened,
466 #content #left #menu ul.opened,
467 #content #left #menu li ul.expanded {
467 #content #left #menu li ul.expanded {
468 display: block !important;
468 display: block !important;
469 }
469 }
470
470
471 #content div.panel ol.lower-roman,
471 #content div.panel ol.lower-roman,
472 #content div.panel ol.upper-roman,
472 #content div.panel ol.upper-roman,
473 #content div.panel ol.lower-alpha,
473 #content div.panel ol.lower-alpha,
474 #content div.panel ol.upper-alpha,
474 #content div.panel ol.upper-alpha,
475 #content div.panel ol.decimal {
475 #content div.panel ol.decimal {
476 margin: 10px 24px 10px 44px;
476 margin: 10px 24px 10px 44px;
477 }
477 }
478
478
479 div.form div.form-group div.button input,
479 div.form div.form-group div.button input,
480 #content div.panel div.form div.buttons input,
480 #content div.panel div.form div.buttons input,
481 div.form div.buttons input,
481 div.form div.buttons input,
482 #content div.panel div.action div.button input {
482 #content div.panel div.action div.button input {
483 font-size: 11px;
483 font-size: 11px;
484 font-weight: 700;
484 font-weight: 700;
485 margin: 0;
485 margin: 0;
486 }
486 }
487 div.form div.form-group div.highlight,
487 div.form div.form-group div.highlight,
488 #content div.panel div.form div.buttons div.highlight {
488 #content div.panel div.form div.buttons div.highlight {
489 display: inline;
489 display: inline;
490 }
490 }
491 #content div.panel table td.user,
491 #content div.panel table td.user,
492 #content div.panel table td.address {
492 #content div.panel table td.address {
493 width: 10%;
493 width: 10%;
494 text-align: center;
494 text-align: center;
495 }
495 }
496 #content div.panel div.action div.button {
496 #content div.panel div.action div.button {
497 text-align: right;
497 text-align: right;
498 margin: 6px 0 0;
498 margin: 6px 0 0;
499 padding: 0;
499 padding: 0;
500 }
500 }
501 .ac .match {
501 .ac .match {
502 font-weight: 700;
502 font-weight: 700;
503 padding-top: 5px;
503 padding-top: 5px;
504 padding-bottom: 5px;
504 padding-bottom: 5px;
505 }
505 }
506 .q_filter_box {
506 .q_filter_box {
507 border-radius: 4px;
507 border-radius: 4px;
508 border: 0 none;
508 border: 0 none;
509 margin-bottom: -4px;
509 margin-bottom: -4px;
510 margin-top: -4px;
510 margin-top: -4px;
511 padding-left: 3px;
511 padding-left: 3px;
512 }
512 }
513 #node_filter {
513 #node_filter {
514 border: 0px solid #545454;
514 border: 0px solid #545454;
515 color: #AAAAAA;
515 color: #AAAAAA;
516 padding-left: 3px;
516 padding-left: 3px;
517 }
517 }
518 /** comment main **/
518 /** comment main **/
519 .comment .panel,
519 .comment .panel,
520 .comment-inline-form {
520 .comment-inline-form {
521 max-width: 978px;
521 max-width: 978px;
522 }
522 }
523 .comment .panel-body {
523 .comment .panel-body {
524 background-color: #FAFAFA;
524 background-color: #FAFAFA;
525 }
525 }
526 .comments-number {
526 .comments-number {
527 padding: 10px 0;
527 padding: 10px 0;
528 color: #666;
528 color: #666;
529 }
529 }
530 .automatic-comment {
530 .automatic-comment {
531 font-style: italic;
531 font-style: italic;
532 }
532 }
533 /** comment form **/
533 /** comment form **/
534 .status-block {
534 .status-block {
535 margin: 5px;
535 margin: 5px;
536 clear: both;
536 clear: both;
537 }
537 }
538 .panel-heading .pull-left input[type=checkbox],
538 .panel-heading .pull-left input[type=checkbox],
539 .panel-heading .pull-right input[type=checkbox] {
539 .panel-heading .pull-right input[type=checkbox] {
540 position: relative;
540 position: relative;
541 top: 4px;
541 top: 4px;
542 margin: -10px 2px 0;
542 margin: -10px 2px 0;
543 }
543 }
544 /** comment inline **/
544 /** comment inline **/
545 .inline-comments {
545 .inline-comments {
546 padding: 5px;
546 padding: 5px;
547 }
547 }
548 .inline-comments .comments-number {
548 .inline-comments .comments-number {
549 padding: 0px 0px 10px 0px;
549 padding: 0px 0px 10px 0px;
550 }
550 }
551 input.status_change_checkbox,
551 input.status_change_checkbox,
552 input.status_change_radio {
552 input.status_change_radio {
553 margin: 0 0 5px 15px;
553 margin: 0 0 5px 15px;
554 }
554 }
555 @keyframes animated-comment-background {
555 @keyframes animated-comment-background {
556 0% {
556 0% {
557 background-position: 0 0;
557 background-position: 0 0;
558 }
558 }
559 100% {
559 100% {
560 background-position: 20px 0;
560 background-position: 20px 0;
561 }
561 }
562 }
562 }
563 .comment-preview.failed .user,
563 .comment-preview.failed .user,
564 .comment-preview.failed .panel-body {
564 .comment-preview.failed .panel-body {
565 color: #666;
565 color: #666;
566 }
566 }
567 .comment-preview .comment-submission-status {
567 .comment-preview .comment-submission-status {
568 float: right;
568 float: right;
569 }
569 }
570 .comment-preview .comment-submission-status .btn-group {
570 .comment-preview .comment-submission-status .btn-group {
571 margin-left: 10px;
571 margin-left: 10px;
572 }
572 }
573 .comment-preview.submitting .panel-body {
573 .comment-preview.submitting .panel-body {
574 background-image: linear-gradient(-45deg, #FAFAFA, #FAFAFA 25%, #FFF 25%, #FFF 50%, #FAFAFA 50%, #FAFAFA 75%, #FFF 75%, #FFF 100%);
574 background-image: linear-gradient(-45deg, #FAFAFA, #FAFAFA 25%, #FFF 25%, #FFF 50%, #FAFAFA 50%, #FAFAFA 75%, #FFF 75%, #FFF 100%);
575 background-size: 20px 20px;
575 background-size: 20px 20px;
576 animation: animated-comment-background 0.4s linear infinite;
576 animation: animated-comment-background 0.4s linear infinite;
577 }
577 }
578 /****
578 /****
579 PULL REQUESTS
579 PULL REQUESTS
580 *****/
580 *****/
581 div.pr-details-title.closed {
581 div.pr-details-title.closed {
582 color: #555;
582 color: #555;
583 background: #eee;
583 background: #eee;
584 }
584 }
585 div.pr {
585 div.pr {
586 margin: 0px 15px;
586 margin: 0px 15px;
587 padding: 4px 4px;
587 padding: 4px 4px;
588 }
588 }
589 tr.pr-closed td {
589 tr.pr-closed td {
590 background-color: #eee !important;
590 background-color: #eee !important;
591 color: #555 !important;
591 color: #555 !important;
592 }
592 }
593 span.pr-closed-tag {
593 span.pr-closed-tag {
594 margin-bottom: 1px;
594 margin-bottom: 1px;
595 margin-right: 1px;
595 margin-right: 1px;
596 padding: 1px 3px;
596 padding: 1px 3px;
597 font-size: 10px;
597 font-size: 10px;
598 color: @kallithea-theme-main-color;
598 color: @kallithea-theme-main-color;
599 white-space: nowrap;
599 white-space: nowrap;
600 border-radius: 4px;
600 border-radius: 4px;
601 border: 1px solid #d9e8f8;
601 border: 1px solid #d9e8f8;
602 line-height: 1.5em;
602 line-height: 1.5em;
603 }
603 }
604 #s2id_org_ref,
604 #s2id_org_ref,
605 #s2id_other_ref,
605 #s2id_other_ref,
606 #s2id_org_repo,
606 #s2id_org_repo,
607 #s2id_other_repo {
607 #s2id_other_repo {
608 min-width: 150px;
608 min-width: 150px;
609 margin: 5px;
609 margin: 5px;
610 }
610 }
611 #pr-summary > .pr-not-edit {
611 #pr-summary > .pr-not-edit {
612 min-height: 50px !important;
612 min-height: 50px !important;
613 }
613 }
614 /* make 'next iteration' changeset table smaller and scrollable */
614 /* make 'next iteration' changeset table smaller and scrollable */
615 #pr-summary #updaterevs {
615 #pr-summary #updaterevs {
616 max-height: 200px;
616 max-height: 200px;
617 overflow-y: auto;
617 overflow-y: auto;
618 overflow-x: hidden;
618 overflow-x: hidden;
619 }
619 }
620
620
621 /****
621 /****
622 PERMS
622 PERMS
623 *****/
623 *****/
624 .perm-gravatar-ac {
624 .perm-gravatar-ac {
625 vertical-align: middle;
625 vertical-align: middle;
626 padding: 2px;
626 padding: 2px;
627 width: 14px;
627 width: 14px;
628 height: 14px;
628 height: 14px;
629 }
629 }
630
630
631 /* avoid gaps between the navbar and browser */
631 /* avoid gaps between the navbar and browser */
632 .navbar.mainmenu {
632 .navbar.mainmenu {
633 border-top-left-radius: 0;
633 border-top-left-radius: 0;
634 border-top-right-radius: 0;
634 border-top-right-radius: 0;
635 }
635 }
636 .navbar.footer {
636 .navbar.footer {
637 border-bottom-left-radius: 0;
637 border-bottom-left-radius: 0;
638 border-bottom-right-radius: 0;
638 border-bottom-right-radius: 0;
639 }
639 }
640
640
641 /* show some context of link targets - but only works when the link target
641 /* show some context of link targets - but only works when the link target
642 can be extended with any visual difference */
642 can be extended with any visual difference */
643 div.comment:target:before {
643 div.comment:target:before {
644 display: block;
644 display: block;
645 height: 100px;
645 height: 100px;
646 margin: -100px 0 0;
646 margin: -100px 0 0;
647 content: "";
647 content: "";
648 }
648 }
649 div.comment:target > .panel {
649 div.comment:target > .panel {
650 border: solid 2px #ee0 !important;
650 border: solid 2px #ee0 !important;
651 }
651 }
652 .lineno:target a {
652 .lineno:target a {
653 border: solid 2px #ee0 !important;
653 border: solid 2px #ee0 !important;
654 margin: -2px;
654 margin: -2px;
655 }
655 }
656 .btn-image-diff-show,
656 .btn-image-diff-show,
657 .btn-image-diff-swap {
657 .btn-image-diff-swap {
658 margin: 5px;
658 margin: 5px;
659 }
659 }
660 .img-diff {
660 .img-diff {
661 max-width: 45%;
661 max-width: 45%;
662 height: auto;
662 height: auto;
663 margin: 5px;
663 margin: 5px;
664 /* http://lea.verou.me/demos/css3-patterns.html */
664 /* http://lea.verou.me/demos/css3-patterns.html */
665 background-image: linear-gradient(45deg, #888 25%, transparent 25%, transparent), linear-gradient(-45deg, #888 25%, transparent 25%, transparent), linear-gradient(45deg, transparent 75%, #888 75%), linear-gradient(-45deg, transparent 75%, #888 75%);
665 background-image: linear-gradient(45deg, #888 25%, transparent 25%, transparent), linear-gradient(-45deg, #888 25%, transparent 25%, transparent), linear-gradient(45deg, transparent 75%, #888 75%), linear-gradient(-45deg, transparent 75%, #888 75%);
666 background-size: 10px 10px;
666 background-size: 10px 10px;
667 background-color: #999;
667 background-color: #999;
668 }
668 }
669 .img-preview {
669 .img-preview {
670 max-width: 100%;
670 max-width: 100%;
671 height: auto;
671 height: auto;
672 margin: 5px;
672 margin: 5px;
673 }
673 }
674 div.comment-prev-next-links div.prev-comment,
674 div.comment-prev-next-links div.prev-comment,
675 div.comment-prev-next-links div.next-comment {
675 div.comment-prev-next-links div.next-comment {
676 display: inline-block;
676 display: inline-block;
677 min-width: 150px;
677 min-width: 150px;
678 margin: 3px 6px;
678 margin: 3px 6px;
679 }
679 }
680 #comments-general-comments div.comment-prev-next-links div.prev-comment,
680 #comments-general-comments div.comment-prev-next-links div.prev-comment,
681 #comments-general-comments div.comment-prev-next-links div.next-comment {
681 #comments-general-comments div.comment-prev-next-links div.next-comment {
682 margin-left: 0;
682 margin-left: 0;
683 }
683 }
684
684
685 /* changelog graph */
685 /* changelog graph */
686 #graph_nodes,
686 #graph_nodes,
687 #updaterevs-graph {
687 #updaterevs-graph {
688 .make-xs-column(1);
688 .make-xs-column(1);
689 height: 0;
689 height: 0;
690 }
690 }
691 #graph_content,
691 #graph_content,
692 #graph_content_pr,
692 #graph_content_pr,
693 #updaterevs-table {
693 #updaterevs-table {
694 .make-xs-column-offset(1);
694 .make-xs-column-offset(1);
695 .make-xs-column(11);
695 .make-xs-column(11);
696 }
696 }
697
697
698 /* use bootstrap grid columns for centered columns */
698 /* use bootstrap grid columns for centered columns */
699 .centered-column {
699 .centered-column {
700 .make-sm-column-offset(3);
700 .make-sm-column-offset(3);
701 .make-sm-column(6);
701 .make-sm-column(6);
702 .form {
702 .form {
703 .form-horizontal;
703 .form-horizontal;
704 .form-group > label {
704 .form-group > label {
705 .make-sm-column(4);
705 .make-sm-column(4);
706 }
706 }
707 .form-group > div {
707 .form-group > div {
708 .make-sm-column(8);
708 .make-sm-column(8);
709 }
709 }
710 .form-group > div:first-child { /* in case there is no label */
710 .form-group > div:first-child { /* in case there is no label */
711 .make-sm-column-offset(4);
711 .make-sm-column-offset(4);
712 .make-sm-column(8);
712 .make-sm-column(8);
713 }
713 }
714 }
714 }
715 }
715 }
716
716
717 /* use columns and form-horizontal for settings pages ... on wide screens */
717 /* use columns and form-horizontal for settings pages ... on wide screens */
718 @media (min-width: @screen-sm-min) {
718 @media (min-width: @screen-sm-min) {
719 .settings {
719 .settings {
720 max-width: @container-md;
720 max-width: @container-md;
721 > ul.nav-stacked {
721 > ul.nav-stacked {
722 .make-sm-column(2);
722 .make-sm-column(2);
723 max-width: (@container-md/12)*2;
723 max-width: (@container-md/12)*2;
724 }
724 }
725 > div {
725 > div {
726 .make-sm-column(10);
726 .make-sm-column(10);
727 max-width: (@container-md/12)*10;
727 max-width: (@container-md/12)*10;
728 }
728 }
729 .form {
729 .form {
730 .form-horizontal;
730 .form-horizontal;
731 }
731 }
732 .form-group {
732 .form-group {
733 .clearfix;
733 .clearfix;
734 > label {
734 > label {
735 .make-xs-column(3);
735 .make-xs-column(3);
736 overflow: hidden;
736 overflow: hidden;
737 text-overflow: ellipsis;
737 text-overflow: ellipsis;
738 input {
738 input {
739 width: 100%;
739 width: 100%;
740 }
740 }
741 }
741 }
742 > div {
742 > div {
743 .make-xs-column(9);
743 .make-xs-column(9);
744 }
744 }
745 .buttons {
745 .buttons {
746 .make-xs-column-offset(3);
746 .make-xs-column-offset(3);
747 }
747 }
748 }
748 }
749 }
749 }
750 }
750 }
751
751
752 /* use columns and form-horizontal for summary page */
752 /* use columns and form-horizontal for summary page */
753 #summary {
753 #summary {
754 max-width: @container-md;
754 max-width: @container-md;
755 .form-horizontal;
755 .form-horizontal;
756 .make-sm-column(10);
756 .make-sm-column(10);
757 .form-group > label {
757 .form-group > label {
758 .make-sm-column(2);
758 .make-sm-column(2);
759 }
759 }
760 .form-group > div {
760 .form-group > div {
761 .make-sm-column(10);
761 .make-sm-column(10);
762 }
762 }
763 }
763 }
764 #summary-menu-stats {
764 #summary-menu-stats {
765 .make-sm-column(2);
765 .make-sm-column(2);
766 }
766 }
767
767
768 /* use columns and form-horizontal for pull request page */
768 /* use columns and form-horizontal for pull request page */
769 .pr-box {
769 .pr-box {
770 .make-sm-column(9);
770 .make-sm-column(9);
771 max-width: @container-md;
771 max-width: @container-md;
772 #pr-summary {
772 #pr-summary {
773 .form-horizontal;
773 .form-horizontal;
774 .form-group > label {
774 .form-group > label {
775 .make-sm-column(3);
775 .make-sm-column(3);
776 }
776 }
777 .form-group > div {
777 .form-group > div {
778 .make-sm-column(9);
778 .make-sm-column(9);
779 }
779 }
780 .form-group > .buttons {
780 .form-group > .buttons {
781 .make-sm-column-offset(3);
781 .make-sm-column-offset(3);
782 .make-sm-column(9);
782 .make-sm-column(9);
783 }
783 }
784 }
784 }
785 }
785 }
786 .pr-reviewers-box {
786 .pr-reviewers-box {
787 .make-sm-column(3);
787 .make-sm-column(3);
788 }
788 }
789
789
790 /* repo table icons */
790 /* repo table icons */
791 #repos_list_wrap_wrapper {
791 #repos_list_wrap_wrapper {
792 /* make icon-folder and repotag the same width */
792 /* make icon-folder and repotag the same width */
793 .icon-folder:before {
793 .icon-folder:before {
794 margin: 0; // default margin would otherwise add to the total width
794 margin: 0; // default margin would otherwise add to the total width
795 width: 24px;
795 width: 24px;
796 text-align: left;
796 text-align: left;
797 }
797 }
798 .label-repo {
798 .label-repo {
799 display: inline-block;
799 display: inline-block;
800 width: 24px;
800 width: 24px;
801 }
801 }
802 }
802 }
803
803
804 /* changelog table columns */
804 /* changelog table columns */
805 .table#changesets {
805 .table#changesets {
806 table-layout: fixed;
806 table-layout: fixed;
807 td {
807 td {
808 overflow: hidden;
808 overflow: hidden;
809 text-overflow: ellipsis;
809 text-overflow: ellipsis;
810 white-space: nowrap;
810 white-space: nowrap;
811 vertical-align: baseline;
811 vertical-align: baseline;
812 }
812 }
813 .checkbox-column {
813 .checkbox-column {
814 width: 24px;
814 width: 24px;
815 /* the optional second checkbox will be inline-block but should wrap to a new line */
815 /* the optional second checkbox will be inline-block but should wrap to a new line */
816 white-space: normal;
816 white-space: normal;
817 > input[type=checkbox] {
817 > input[type=checkbox] {
818 margin-top: inherit;
818 margin-top: inherit;
819 vertical-align: text-bottom;
819 vertical-align: text-bottom;
820 }
820 }
821 }
821 }
822 .changeset-logical-index {
822 .changeset-logical-index {
823 color: @gray-light;
823 color: @gray-light;
824 font-style: italic;
824 font-style: italic;
825 font-size: 85%;
825 font-size: 85%;
826 text-align: right;
826 text-align: right;
827 overflow: visible;
827 overflow: visible;
828 }
828 }
829 .changeset-logical-index,
829 .changeset-logical-index,
830 .expand_commit,
830 .expand_commit,
831 .status {
831 .status {
832 width: 28px;
832 width: 28px;
833 }
833 }
834 .author {
834 .author {
835 width: 200px;
835 width: 200px;
836 @media (max-width: @screen-sm-max) {
836 @media (max-width: @screen-sm-max) {
837 width: 120px;
837 width: 120px;
838 }
838 }
839 @media (max-width: @screen-xs-max) {
839 @media (max-width: @screen-xs-max) {
840 width: 20px;
840 width: 20px;
841 /* keep gravatar but hide name on tiny screens to give important columns more room */
841 /* keep gravatar but hide name on tiny screens to give important columns more room */
842 span {
842 span {
843 .hidden;
843 .hidden;
844 }
844 }
845 }
845 }
846 }
846 }
847 .hash {
847 .hash {
848 .small;
848 .small;
849 width: 110px;
849 width: 110px;
850 @media (max-width: @screen-xs-max) {
850 @media (max-width: @screen-xs-max) {
851 width: 48px;
851 width: 48px;
852 }
852 }
853 }
853 }
854 .date {
854 .date {
855 .small;
855 .small;
856 width: 100px;
856 width: 100px;
857 }
857 }
858 /* hide on small screens to give important columns more room */
858 /* hide on small screens to give important columns more room */
859 .status,
859 .status,
860 .expand_commit,
860 .expand_commit,
861 .comments,
861 .comments,
862 .extra-container {
862 .extra-container {
863 .hidden-xs;
863 .hidden-xs;
864 }
864 }
865 .mid > .log-container {
865 .mid > .log-container {
866 position: relative;
866 position: relative;
867 overflow: hidden;
867 overflow: hidden;
868 > .extra-container {
868 > .extra-container {
869 position: absolute;
869 position: absolute;
870 top: 0;
870 top: 0;
871 right: 0;
871 right: 0;
872 background: white;
872 background: white;
873 box-shadow: -10px 0px 10px 0px white;
873 box-shadow: -10px 0px 10px 0px white;
874 }
874 }
875 }
875 }
876 }
876 }
877
877
878 /* undo Bootstrap chrome/webkit blue outline on focus in navbar */
878 /* undo Bootstrap chrome/webkit blue outline on focus in navbar */
879 .navbar-inverse .navbar-nav > li > a:focus {
879 .navbar-inverse .navbar-nav > li > a:focus {
880 outline: 0;
880 outline: 0;
881 }
881 }
882
882
883 /* use same badge coloring in navbar inverse as in panel-heading */
883 /* use same badge coloring in navbar inverse as in panel-heading */
884 .navbar-inverse {
884 .navbar-inverse {
885 .badge {
885 .badge {
886 color: @navbar-inverse-bg;
886 color: @navbar-inverse-bg;
887 background-color: @navbar-inverse-color;
887 background-color: @navbar-inverse-color;
888 }
888 }
889 }
889 }
890
890
891 /* pygments style */
891 /* pygments style */
892 div.search-code-body pre .match {
892 div.search-code-body pre .match {
893 background-color: @highlight-color;
893 background-color: @highlight-color;
894 }
894 }
895 div.search-code-body pre .break {
895 div.search-code-body pre .break {
896 background-color: @highlight-line-color;
896 background-color: @highlight-line-color;
897 width: 100%;
897 width: 100%;
898 color: #747474;
898 color: #747474;
899 display: block;
899 display: block;
900 }
900 }
901 div.annotatediv {
901 div.annotatediv {
902 margin-left: 2px;
902 margin-left: 2px;
903 margin-right: 4px;
903 margin-right: 4px;
904 }
904 }
905 .code-highlight {
905 .code-highlight {
906 border-left: 1px solid #ccc;
906 border-left: 1px solid #ccc;
907 }
907 }
908 .code-highlight pre,
908 .code-highlight pre,
909 .linenodiv pre {
909 .linenodiv pre {
910 padding: 5px 2px 0px 5px;
910 padding: 5px 2px 0px 5px;
911 margin: 0;
911 margin: 0;
912 }
912 }
913 .code-highlight pre div:target {
913 .code-highlight pre div:target {
914 background-color: #FFFFBE !important;
914 background-color: #FFFFBE !important;
915 }
915 }
916 .linenos a { text-decoration: none; }
916 .linenos a { text-decoration: none; }
917
917
918 /* Stylesheets for the context bar */
918 /* Stylesheets for the context bar */
919 #quick_login > .pull-right .list-group-item {
919 #quick_login > .pull-right .list-group-item {
920 background-color: @kallithea-theme-main-color;
920 background-color: @kallithea-theme-main-color;
921 border: 0;
921 border: 0;
922 }
922 }
923 #content #context-pages .follow .show-following,
923 #content #context-pages .follow .show-following,
924 #content #context-pages .following .show-follow {
924 #content #context-pages .following .show-follow {
925 display: none;
925 display: none;
926 }
926 }
927
927
928 nav.navbar #quick > li > a,
928 nav.navbar #quick > li > a,
929 #context-pages > ul > li > a {
929 #context-pages > ul > li > a {
930 height: @navbar-height;
930 height: @navbar-height;
931 }
931 }
932
933 /* at.js */
934 .atwho-view strong {
935 /* the blue color doesn't look good, use normal color */
936 color: inherit;
937 }
@@ -1,141 +1,143 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html>
2 <!DOCTYPE html>
3 <html xmlns="http://www.w3.org/1999/xhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
4 <head>
5 <title><%block name="title"/><%block name="branding_title"/></title>
5 <title><%block name="title"/><%block name="branding_title"/></title>
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 <meta http-equiv="X-UA-Compatible" content="IE=10"/>
7 <meta http-equiv="X-UA-Compatible" content="IE=10"/>
8 <meta name="robots" content="index, nofollow"/>
8 <meta name="robots" content="index, nofollow"/>
9 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
9 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
10 <link rel="shortcut icon" href="${h.url('/images/favicon.ico')}" type="image/x-icon" />
10 <link rel="shortcut icon" href="${h.url('/images/favicon.ico')}" type="image/x-icon" />
11 <link rel="icon" type="image/png" href="${h.url('/images/favicon-32x32.png')}" sizes="32x32">
11 <link rel="icon" type="image/png" href="${h.url('/images/favicon-32x32.png')}" sizes="32x32">
12 <link rel="icon" type="image/png" href="${h.url('/images/favicon-16x16.png')}" sizes="16x16">
12 <link rel="icon" type="image/png" href="${h.url('/images/favicon-16x16.png')}" sizes="16x16">
13 <link rel="apple-touch-icon" sizes="180x180" href="${h.url('/images/apple-touch-icon.png')}">
13 <link rel="apple-touch-icon" sizes="180x180" href="${h.url('/images/apple-touch-icon.png')}">
14 <link rel="manifest" href="${h.url('/images/manifest.json')}">
14 <link rel="manifest" href="${h.url('/images/manifest.json')}">
15 <link rel="mask-icon" href="${h.url('/images/safari-pinned-tab.svg')}" color="#b1d579">
15 <link rel="mask-icon" href="${h.url('/images/safari-pinned-tab.svg')}" color="#b1d579">
16 <meta name="msapplication-config" content="${h.url('/images/browserconfig.xml')}">
16 <meta name="msapplication-config" content="${h.url('/images/browserconfig.xml')}">
17 <meta name="theme-color" content="#ffffff">
17 <meta name="theme-color" content="#ffffff">
18
18
19 ## CSS ###
19 ## CSS ###
20 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css', ver=c.kallithea_version)}" media="screen"/>
20 <link rel="stylesheet" type="text/css" href="${h.url('/css/style.css', ver=c.kallithea_version)}" media="screen"/>
21 <%block name="css_extra"/>
21 <%block name="css_extra"/>
22
22
23 ## JAVASCRIPT ##
23 ## JAVASCRIPT ##
24 <script type="text/javascript">
24 <script type="text/javascript">
25 ## JS translations map
25 ## JS translations map
26 var TRANSLATION_MAP = {
26 var TRANSLATION_MAP = {
27 'Cancel': ${h.jshtml(_("Cancel"))},
27 'Cancel': ${h.jshtml(_("Cancel"))},
28 'Retry': ${h.jshtml(_("Retry"))},
28 'Retry': ${h.jshtml(_("Retry"))},
29 'Submitting ...': ${h.jshtml(_("Submitting ..."))},
29 'Submitting ...': ${h.jshtml(_("Submitting ..."))},
30 'Unable to post': ${h.jshtml(_("Unable to post"))},
30 'Unable to post': ${h.jshtml(_("Unable to post"))},
31 'Add Another Comment': ${h.jshtml(_("Add Another Comment"))},
31 'Add Another Comment': ${h.jshtml(_("Add Another Comment"))},
32 'Stop following this repository': ${h.jshtml(_('Stop following this repository'))},
32 'Stop following this repository': ${h.jshtml(_('Stop following this repository'))},
33 'Start following this repository': ${h.jshtml(_('Start following this repository'))},
33 'Start following this repository': ${h.jshtml(_('Start following this repository'))},
34 'Group': ${h.jshtml(_('Group'))},
34 'Group': ${h.jshtml(_('Group'))},
35 'Loading ...': ${h.jshtml(_('Loading ...'))},
35 'Loading ...': ${h.jshtml(_('Loading ...'))},
36 'loading ...': ${h.jshtml(_('loading ...'))},
36 'loading ...': ${h.jshtml(_('loading ...'))},
37 'Search truncated': ${h.jshtml(_('Search truncated'))},
37 'Search truncated': ${h.jshtml(_('Search truncated'))},
38 'No matching files': ${h.jshtml(_('No matching files'))},
38 'No matching files': ${h.jshtml(_('No matching files'))},
39 'Open New Pull Request from {0}': ${h.jshtml(_('Open New Pull Request from {0}'))},
39 'Open New Pull Request from {0}': ${h.jshtml(_('Open New Pull Request from {0}'))},
40 'Open New Pull Request for {0} &rarr; {1}': ${h.js(_('Open New Pull Request for {0} &rarr; {1}'))},
40 'Open New Pull Request for {0} &rarr; {1}': ${h.js(_('Open New Pull Request for {0} &rarr; {1}'))},
41 'Show Selected Changesets {0} &rarr; {1}': ${h.js(_('Show Selected Changesets {0} &rarr; {1}'))},
41 'Show Selected Changesets {0} &rarr; {1}': ${h.js(_('Show Selected Changesets {0} &rarr; {1}'))},
42 'Selection Link': ${h.jshtml(_('Selection Link'))},
42 'Selection Link': ${h.jshtml(_('Selection Link'))},
43 'Collapse Diff': ${h.jshtml(_('Collapse Diff'))},
43 'Collapse Diff': ${h.jshtml(_('Collapse Diff'))},
44 'Expand Diff': ${h.jshtml(_('Expand Diff'))},
44 'Expand Diff': ${h.jshtml(_('Expand Diff'))},
45 'No revisions': ${h.jshtml(_('No revisions'))},
45 'No revisions': ${h.jshtml(_('No revisions'))},
46 'Type name of user or member to grant permission': ${h.jshtml(_('Type name of user or member to grant permission'))},
46 'Type name of user or member to grant permission': ${h.jshtml(_('Type name of user or member to grant permission'))},
47 'Failed to revoke permission': ${h.jshtml(_('Failed to revoke permission'))},
47 'Failed to revoke permission': ${h.jshtml(_('Failed to revoke permission'))},
48 'Confirm to revoke permission for {0}: {1} ?': ${h.jshtml(_('Confirm to revoke permission for {0}: {1} ?'))},
48 'Confirm to revoke permission for {0}: {1} ?': ${h.jshtml(_('Confirm to revoke permission for {0}: {1} ?'))},
49 'Enabled': ${h.jshtml(_('Enabled'))},
49 'Enabled': ${h.jshtml(_('Enabled'))},
50 'Disabled': ${h.jshtml(_('Disabled'))},
50 'Disabled': ${h.jshtml(_('Disabled'))},
51 'Select changeset': ${h.jshtml(_('Select changeset'))},
51 'Select changeset': ${h.jshtml(_('Select changeset'))},
52 'Specify changeset': ${h.jshtml(_('Specify changeset'))},
52 'Specify changeset': ${h.jshtml(_('Specify changeset'))},
53 'MSG_SORTASC': ${h.jshtml(_('Click to sort ascending'))},
53 'MSG_SORTASC': ${h.jshtml(_('Click to sort ascending'))},
54 'MSG_SORTDESC': ${h.jshtml(_('Click to sort descending'))},
54 'MSG_SORTDESC': ${h.jshtml(_('Click to sort descending'))},
55 'MSG_EMPTY': ${h.jshtml(_('No records found.'))},
55 'MSG_EMPTY': ${h.jshtml(_('No records found.'))},
56 'MSG_ERROR': ${h.jshtml(_('Data error.'))},
56 'MSG_ERROR': ${h.jshtml(_('Data error.'))},
57 'MSG_LOADING': ${h.jshtml(_('Loading...'))}
57 'MSG_LOADING': ${h.jshtml(_('Loading...'))}
58 };
58 };
59 var _TM = TRANSLATION_MAP;
59 var _TM = TRANSLATION_MAP;
60
60
61 var TOGGLE_FOLLOW_URL = ${h.js(h.url('toggle_following'))};
61 var TOGGLE_FOLLOW_URL = ${h.js(h.url('toggle_following'))};
62
62
63 var REPO_NAME = "";
63 var REPO_NAME = "";
64 %if hasattr(c, 'repo_name'):
64 %if hasattr(c, 'repo_name'):
65 var REPO_NAME = ${h.js(c.repo_name)};
65 var REPO_NAME = ${h.js(c.repo_name)};
66 %endif
66 %endif
67
67
68 var _authentication_token = ${h.js(h.authentication_token())};
68 var _authentication_token = ${h.js(h.authentication_token())};
69 </script>
69 </script>
70 <script type="text/javascript" src="${h.url('/js/yui.2.9.js', ver=c.kallithea_version)}"></script>
70 <script type="text/javascript" src="${h.url('/js/yui.2.9.js', ver=c.kallithea_version)}"></script>
71 <script type="text/javascript" src="${h.url('/js/jquery.min.js', ver=c.kallithea_version)}"></script>
71 <script type="text/javascript" src="${h.url('/js/jquery.min.js', ver=c.kallithea_version)}"></script>
72 <script type="text/javascript" src="${h.url('/js/jquery.dataTables.js', ver=c.kallithea_version)}"></script>
72 <script type="text/javascript" src="${h.url('/js/jquery.dataTables.js', ver=c.kallithea_version)}"></script>
73 <script type="text/javascript" src="${h.url('/js/dataTables.bootstrap.js', ver=c.kallithea_version)}"></script>
73 <script type="text/javascript" src="${h.url('/js/dataTables.bootstrap.js', ver=c.kallithea_version)}"></script>
74 <script type="text/javascript" src="${h.url('/js/bootstrap.js', ver=c.kallithea_version)}"></script>
74 <script type="text/javascript" src="${h.url('/js/bootstrap.js', ver=c.kallithea_version)}"></script>
75 <script type="text/javascript" src="${h.url('/js/select2.js', ver=c.kallithea_version)}"></script>
75 <script type="text/javascript" src="${h.url('/js/select2.js', ver=c.kallithea_version)}"></script>
76 <script type="text/javascript" src="${h.url('/js/native.history.js', ver=c.kallithea_version)}"></script>
76 <script type="text/javascript" src="${h.url('/js/native.history.js', ver=c.kallithea_version)}"></script>
77 <script type="text/javascript" src="${h.url('/js/jquery.caret.min.js', ver=c.kallithea_version)}"></script>
78 <script type="text/javascript" src="${h.url('/js/jquery.atwho.min.js', ver=c.kallithea_version)}"></script>
77 <script type="text/javascript" src="${h.url('/js/base.js', ver=c.kallithea_version)}"></script>
79 <script type="text/javascript" src="${h.url('/js/base.js', ver=c.kallithea_version)}"></script>
78 ## EXTRA FOR JS
80 ## EXTRA FOR JS
79 <%block name="js_extra"/>
81 <%block name="js_extra"/>
80 <script type="text/javascript">
82 <script type="text/javascript">
81 (function(window,undefined){
83 (function(window,undefined){
82 var History = window.History; // Note: We are using a capital H instead of a lower h
84 var History = window.History; // Note: We are using a capital H instead of a lower h
83 if ( !History.enabled ) {
85 if ( !History.enabled ) {
84 // History.js is disabled for this browser.
86 // History.js is disabled for this browser.
85 // This is because we can optionally choose to support HTML4 browsers or not.
87 // This is because we can optionally choose to support HTML4 browsers or not.
86 return false;
88 return false;
87 }
89 }
88 })(window);
90 })(window);
89
91
90 $(document).ready(function(){
92 $(document).ready(function(){
91 tooltip_activate();
93 tooltip_activate();
92 show_more_event();
94 show_more_event();
93 // routes registration
95 // routes registration
94 pyroutes.register('home', ${h.js(h.url('home'))}, []);
96 pyroutes.register('home', ${h.js(h.url('home'))}, []);
95 pyroutes.register('new_gist', ${h.js(h.url('new_gist'))}, []);
97 pyroutes.register('new_gist', ${h.js(h.url('new_gist'))}, []);
96 pyroutes.register('gists', ${h.js(h.url('gists'))}, []);
98 pyroutes.register('gists', ${h.js(h.url('gists'))}, []);
97 pyroutes.register('new_repo', ${h.js(h.url('new_repo'))}, []);
99 pyroutes.register('new_repo', ${h.js(h.url('new_repo'))}, []);
98
100
99 pyroutes.register('summary_home', ${h.js(h.url('summary_home', repo_name='%(repo_name)s'))}, ['repo_name']);
101 pyroutes.register('summary_home', ${h.js(h.url('summary_home', repo_name='%(repo_name)s'))}, ['repo_name']);
100 pyroutes.register('changelog_home', ${h.js(h.url('changelog_home', repo_name='%(repo_name)s'))}, ['repo_name']);
102 pyroutes.register('changelog_home', ${h.js(h.url('changelog_home', repo_name='%(repo_name)s'))}, ['repo_name']);
101 pyroutes.register('files_home', ${h.js(h.url('files_home', repo_name='%(repo_name)s',revision='%(revision)s',f_path='%(f_path)s'))}, ['repo_name', 'revision', 'f_path']);
103 pyroutes.register('files_home', ${h.js(h.url('files_home', repo_name='%(repo_name)s',revision='%(revision)s',f_path='%(f_path)s'))}, ['repo_name', 'revision', 'f_path']);
102 pyroutes.register('edit_repo', ${h.js(h.url('edit_repo', repo_name='%(repo_name)s'))}, ['repo_name']);
104 pyroutes.register('edit_repo', ${h.js(h.url('edit_repo', repo_name='%(repo_name)s'))}, ['repo_name']);
103 pyroutes.register('edit_repo_perms', ${h.js(h.url('edit_repo_perms', repo_name='%(repo_name)s'))}, ['repo_name']);
105 pyroutes.register('edit_repo_perms', ${h.js(h.url('edit_repo_perms', repo_name='%(repo_name)s'))}, ['repo_name']);
104 pyroutes.register('pullrequest_home', ${h.js(h.url('pullrequest_home', repo_name='%(repo_name)s'))}, ['repo_name']);
106 pyroutes.register('pullrequest_home', ${h.js(h.url('pullrequest_home', repo_name='%(repo_name)s'))}, ['repo_name']);
105
107
106 pyroutes.register('toggle_following', ${h.js(h.url('toggle_following'))});
108 pyroutes.register('toggle_following', ${h.js(h.url('toggle_following'))});
107 pyroutes.register('changeset_info', ${h.js(h.url('changeset_info', repo_name='%(repo_name)s', revision='%(revision)s'))}, ['repo_name', 'revision']);
109 pyroutes.register('changeset_info', ${h.js(h.url('changeset_info', repo_name='%(repo_name)s', revision='%(revision)s'))}, ['repo_name', 'revision']);
108 pyroutes.register('changeset_home', ${h.js(h.url('changeset_home', repo_name='%(repo_name)s', revision='%(revision)s'))}, ['repo_name', 'revision']);
110 pyroutes.register('changeset_home', ${h.js(h.url('changeset_home', repo_name='%(repo_name)s', revision='%(revision)s'))}, ['repo_name', 'revision']);
109 pyroutes.register('repo_size', ${h.js(h.url('repo_size', repo_name='%(repo_name)s'))}, ['repo_name']);
111 pyroutes.register('repo_size', ${h.js(h.url('repo_size', repo_name='%(repo_name)s'))}, ['repo_name']);
110 pyroutes.register('repo_refs_data', ${h.js(h.url('repo_refs_data', repo_name='%(repo_name)s'))}, ['repo_name']);
112 pyroutes.register('repo_refs_data', ${h.js(h.url('repo_refs_data', repo_name='%(repo_name)s'))}, ['repo_name']);
111 pyroutes.register('users_and_groups_data', ${h.js(h.url('users_and_groups_data'))}, []);
113 pyroutes.register('users_and_groups_data', ${h.js(h.url('users_and_groups_data'))}, []);
112 });
114 });
113 </script>
115 </script>
114
116
115 <%block name="head_extra"/>
117 <%block name="head_extra"/>
116 </head>
118 </head>
117 <body>
119 <body>
118 <nav class="navbar navbar-inverse mainmenu">
120 <nav class="navbar navbar-inverse mainmenu">
119 <div class="navbar-header" id="logo">
121 <div class="navbar-header" id="logo">
120 <a class="navbar-brand" href="${h.url('home')}">
122 <a class="navbar-brand" href="${h.url('home')}">
121 <span class="branding">${c.site_name}</span>
123 <span class="branding">${c.site_name}</span>
122 </a>
124 </a>
123 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false">
125 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false">
124 <span class="sr-only">Toggle navigation</span>
126 <span class="sr-only">Toggle navigation</span>
125 <span class="icon-bar"></span>
127 <span class="icon-bar"></span>
126 <span class="icon-bar"></span>
128 <span class="icon-bar"></span>
127 <span class="icon-bar"></span>
129 <span class="icon-bar"></span>
128 </button>
130 </button>
129 </div>
131 </div>
130 <div id="navbar" class="navbar-collapse collapse">
132 <div id="navbar" class="navbar-collapse collapse">
131 <%block name="header_menu"/>
133 <%block name="header_menu"/>
132 </div>
134 </div>
133 </nav>
135 </nav>
134
136
135 ${next.body()}
137 ${next.body()}
136
138
137 %if c.ga_code:
139 %if c.ga_code:
138 ${h.literal(c.ga_code)}
140 ${h.literal(c.ga_code)}
139 %endif
141 %endif
140 </body>
142 </body>
141 </html>
143 </html>
General Comments 0
You need to be logged in to leave comments. Login now