##// END OF EJS Templates
release: merge back stable branch into default
marcink -
r334:8c44af47 merge default
parent child Browse files
Show More
@@ -0,0 +1,14 b''
1 |RCE| 4.2.1 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2016-07-04
8
9 Fixes
10 ^^^^^
11
12 - ui: fixed empty labels caused by missing translation of JS components
13 - login: fixed bad routing URL in comments when user is not logged in.
14 - celery: make sure to run tasks in sync mode if connection to celery is lost.
@@ -0,0 +1,36 b''
1 // # Copyright (C) 2016-2016 RhodeCode GmbH
2 // #
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
6 // #
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
19 i18nLog = Logger.get('i18n');
20
21 var _gettext = function (s) {
22 if (_TM.hasOwnProperty(s)) {
23 return _TM[s];
24 }
25 i18nLog.error(
26 'String `' + s + '` was requested but cannot be ' +
27 'found in translation table');
28 return s
29 };
30
31 var _ngettext = function (singular, plural, n) {
32 if (n === 1) {
33 return _gettext(singular)
34 }
35 return _gettext(plural)
36 };
@@ -1,6 +1,7 b''
1 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0
1 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0
2 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1
2 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1
3 c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0
3 c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0
4 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1
4 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1
5 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2
5 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2
6 baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0
6 baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0
7 32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1
@@ -1,138 +1,139 b''
1 module.exports = function(grunt) {
1 module.exports = function(grunt) {
2 grunt.initConfig({
2 grunt.initConfig({
3
3
4 dirs: {
4 dirs: {
5 css: "rhodecode/public/css",
5 css: "rhodecode/public/css",
6 js: {
6 js: {
7 "src": "rhodecode/public/js/src",
7 "src": "rhodecode/public/js/src",
8 "dest": "rhodecode/public/js"
8 "dest": "rhodecode/public/js"
9 }
9 }
10 },
10 },
11
11
12 concat: {
12 concat: {
13 dist: {
13 dist: {
14 src: [
14 src: [
15 // Base libraries
15 // Base libraries
16 '<%= dirs.js.src %>/jquery-1.11.1.min.js',
16 '<%= dirs.js.src %>/jquery-1.11.1.min.js',
17 '<%= dirs.js.src %>/logging.js',
17 '<%= dirs.js.src %>/logging.js',
18 '<%= dirs.js.src %>/bootstrap.js',
18 '<%= dirs.js.src %>/bootstrap.js',
19 '<%= dirs.js.src %>/mousetrap.js',
19 '<%= dirs.js.src %>/mousetrap.js',
20 '<%= dirs.js.src %>/moment.js',
20 '<%= dirs.js.src %>/moment.js',
21 '<%= dirs.js.src %>/appenlight-client-0.4.1.min.js',
21 '<%= dirs.js.src %>/appenlight-client-0.4.1.min.js',
22 '<%= dirs.js.src %>/i18n_utils.js',
22
23
23 // Plugins
24 // Plugins
24 '<%= dirs.js.src %>/plugins/jquery.pjax.js',
25 '<%= dirs.js.src %>/plugins/jquery.pjax.js',
25 '<%= dirs.js.src %>/plugins/jquery.dataTables.js',
26 '<%= dirs.js.src %>/plugins/jquery.dataTables.js',
26 '<%= dirs.js.src %>/plugins/flavoured_checkbox.js',
27 '<%= dirs.js.src %>/plugins/flavoured_checkbox.js',
27 '<%= dirs.js.src %>/plugins/jquery.auto-grow-input.js',
28 '<%= dirs.js.src %>/plugins/jquery.auto-grow-input.js',
28 '<%= dirs.js.src %>/plugins/jquery.autocomplete.js',
29 '<%= dirs.js.src %>/plugins/jquery.autocomplete.js',
29 '<%= dirs.js.src %>/plugins/jquery.debounce.js',
30 '<%= dirs.js.src %>/plugins/jquery.debounce.js',
30 '<%= dirs.js.src %>/plugins/jquery.mark.js',
31 '<%= dirs.js.src %>/plugins/jquery.mark.js',
31 '<%= dirs.js.src %>/plugins/jquery.timeago.js',
32 '<%= dirs.js.src %>/plugins/jquery.timeago.js',
32 '<%= dirs.js.src %>/plugins/jquery.timeago-extension.js',
33 '<%= dirs.js.src %>/plugins/jquery.timeago-extension.js',
33
34
34 // Select2
35 // Select2
35 '<%= dirs.js.src %>/select2/select2.js',
36 '<%= dirs.js.src %>/select2/select2.js',
36
37
37 // Code-mirror
38 // Code-mirror
38 '<%= dirs.js.src %>/codemirror/codemirror.js',
39 '<%= dirs.js.src %>/codemirror/codemirror.js',
39 '<%= dirs.js.src %>/codemirror/codemirror_loadmode.js',
40 '<%= dirs.js.src %>/codemirror/codemirror_loadmode.js',
40 '<%= dirs.js.src %>/codemirror/codemirror_hint.js',
41 '<%= dirs.js.src %>/codemirror/codemirror_hint.js',
41 '<%= dirs.js.src %>/codemirror/codemirror_overlay.js',
42 '<%= dirs.js.src %>/codemirror/codemirror_overlay.js',
42 '<%= dirs.js.src %>/codemirror/codemirror_placeholder.js',
43 '<%= dirs.js.src %>/codemirror/codemirror_placeholder.js',
43 // TODO: mikhail: this is an exception. Since the code mirror modes
44 // TODO: mikhail: this is an exception. Since the code mirror modes
44 // are loaded "on the fly", we need to keep them in a public folder
45 // are loaded "on the fly", we need to keep them in a public folder
45 '<%= dirs.js.dest %>/mode/meta.js',
46 '<%= dirs.js.dest %>/mode/meta.js',
46 '<%= dirs.js.dest %>/mode/meta_ext.js',
47 '<%= dirs.js.dest %>/mode/meta_ext.js',
47 '<%= dirs.js.dest %>/rhodecode/i18n/select2/translations.js',
48 '<%= dirs.js.dest %>/rhodecode/i18n/select2/translations.js',
48
49
49 // Rhodecode utilities
50 // Rhodecode utilities
50 '<%= dirs.js.src %>/rhodecode/utils/array.js',
51 '<%= dirs.js.src %>/rhodecode/utils/array.js',
51 '<%= dirs.js.src %>/rhodecode/utils/string.js',
52 '<%= dirs.js.src %>/rhodecode/utils/string.js',
52 '<%= dirs.js.src %>/rhodecode/utils/pyroutes.js',
53 '<%= dirs.js.src %>/rhodecode/utils/pyroutes.js',
53 '<%= dirs.js.src %>/rhodecode/utils/ajax.js',
54 '<%= dirs.js.src %>/rhodecode/utils/ajax.js',
54 '<%= dirs.js.src %>/rhodecode/utils/autocomplete.js',
55 '<%= dirs.js.src %>/rhodecode/utils/autocomplete.js',
55 '<%= dirs.js.src %>/rhodecode/utils/colorgenerator.js',
56 '<%= dirs.js.src %>/rhodecode/utils/colorgenerator.js',
56 '<%= dirs.js.src %>/rhodecode/utils/ie.js',
57 '<%= dirs.js.src %>/rhodecode/utils/ie.js',
57 '<%= dirs.js.src %>/rhodecode/utils/os.js',
58 '<%= dirs.js.src %>/rhodecode/utils/os.js',
58
59
59 // Rhodecode widgets
60 // Rhodecode widgets
60 '<%= dirs.js.src %>/rhodecode/widgets/multiselect.js',
61 '<%= dirs.js.src %>/rhodecode/widgets/multiselect.js',
61
62
62 // Rhodecode components
63 // Rhodecode components
63 '<%= dirs.js.src %>/rhodecode/init.js',
64 '<%= dirs.js.src %>/rhodecode/init.js',
64 '<%= dirs.js.src %>/rhodecode/codemirror.js',
65 '<%= dirs.js.src %>/rhodecode/codemirror.js',
65 '<%= dirs.js.src %>/rhodecode/comments.js',
66 '<%= dirs.js.src %>/rhodecode/comments.js',
66 '<%= dirs.js.src %>/rhodecode/constants.js',
67 '<%= dirs.js.src %>/rhodecode/constants.js',
67 '<%= dirs.js.src %>/rhodecode/files.js',
68 '<%= dirs.js.src %>/rhodecode/files.js',
68 '<%= dirs.js.src %>/rhodecode/followers.js',
69 '<%= dirs.js.src %>/rhodecode/followers.js',
69 '<%= dirs.js.src %>/rhodecode/menus.js',
70 '<%= dirs.js.src %>/rhodecode/menus.js',
70 '<%= dirs.js.src %>/rhodecode/notifications.js',
71 '<%= dirs.js.src %>/rhodecode/notifications.js',
71 '<%= dirs.js.src %>/rhodecode/permissions.js',
72 '<%= dirs.js.src %>/rhodecode/permissions.js',
72 '<%= dirs.js.src %>/rhodecode/pjax.js',
73 '<%= dirs.js.src %>/rhodecode/pjax.js',
73 '<%= dirs.js.src %>/rhodecode/pullrequests.js',
74 '<%= dirs.js.src %>/rhodecode/pullrequests.js',
74 '<%= dirs.js.src %>/rhodecode/settings.js',
75 '<%= dirs.js.src %>/rhodecode/settings.js',
75 '<%= dirs.js.src %>/rhodecode/select2_widgets.js',
76 '<%= dirs.js.src %>/rhodecode/select2_widgets.js',
76 '<%= dirs.js.src %>/rhodecode/tooltips.js',
77 '<%= dirs.js.src %>/rhodecode/tooltips.js',
77 '<%= dirs.js.src %>/rhodecode/users.js',
78 '<%= dirs.js.src %>/rhodecode/users.js',
78 '<%= dirs.js.src %>/rhodecode/appenlight.js',
79 '<%= dirs.js.src %>/rhodecode/appenlight.js',
79
80
80 // Rhodecode main module
81 // Rhodecode main module
81 '<%= dirs.js.src %>/rhodecode.js'
82 '<%= dirs.js.src %>/rhodecode.js'
82 ],
83 ],
83 dest: '<%= dirs.js.dest %>/scripts.js',
84 dest: '<%= dirs.js.dest %>/scripts.js',
84 nonull: true
85 nonull: true
85 }
86 }
86 },
87 },
87
88
88 less: {
89 less: {
89 development: {
90 development: {
90 options: {
91 options: {
91 compress: false,
92 compress: false,
92 yuicompress: false,
93 yuicompress: false,
93 optimization: 0
94 optimization: 0
94 },
95 },
95 files: {
96 files: {
96 "<%= dirs.css %>/style.css": "<%= dirs.css %>/main.less"
97 "<%= dirs.css %>/style.css": "<%= dirs.css %>/main.less"
97 }
98 }
98 },
99 },
99 production: {
100 production: {
100 options: {
101 options: {
101 compress: true,
102 compress: true,
102 yuicompress: true,
103 yuicompress: true,
103 optimization: 2
104 optimization: 2
104 },
105 },
105 files: {
106 files: {
106 "<%= dirs.css %>/style.css": "<%= dirs.css %>/main.less"
107 "<%= dirs.css %>/style.css": "<%= dirs.css %>/main.less"
107 }
108 }
108 }
109 }
109 },
110 },
110
111
111 watch: {
112 watch: {
112 less: {
113 less: {
113 files: ["<%= dirs.css %>/*.less"],
114 files: ["<%= dirs.css %>/*.less"],
114 tasks: ["less:production"]
115 tasks: ["less:production"]
115 },
116 },
116 js: {
117 js: {
117 files: ["<%= dirs.js.src %>/**/*.js"],
118 files: ["<%= dirs.js.src %>/**/*.js"],
118 tasks: ["concat:dist"]
119 tasks: ["concat:dist"]
119 }
120 }
120 },
121 },
121
122
122 jshint: {
123 jshint: {
123 rhodecode: {
124 rhodecode: {
124 src: '<%= dirs.js.src %>/rhodecode/**/*.js',
125 src: '<%= dirs.js.src %>/rhodecode/**/*.js',
125 options: {
126 options: {
126 jshintrc: '.jshintrc'
127 jshintrc: '.jshintrc'
127 }
128 }
128 }
129 }
129 }
130 }
130 });
131 });
131
132
132 grunt.loadNpmTasks('grunt-contrib-less');
133 grunt.loadNpmTasks('grunt-contrib-less');
133 grunt.loadNpmTasks('grunt-contrib-concat');
134 grunt.loadNpmTasks('grunt-contrib-concat');
134 grunt.loadNpmTasks('grunt-contrib-watch');
135 grunt.loadNpmTasks('grunt-contrib-watch');
135 grunt.loadNpmTasks('grunt-contrib-jshint');
136 grunt.loadNpmTasks('grunt-contrib-jshint');
136
137
137 grunt.registerTask('default', ['less:production', 'concat:dist']);
138 grunt.registerTask('default', ['less:production', 'concat:dist']);
138 };
139 };
@@ -1,83 +1,84 b''
1 .. _rhodecode-release-notes-ref:
1 .. _rhodecode-release-notes-ref:
2
2
3 Release Notes
3 Release Notes
4 =============
4 =============
5
5
6 |RCE| 4.x Versions
6 |RCE| 4.x Versions
7 ------------------
7 ------------------
8
8
9 .. toctree::
9 .. toctree::
10 :maxdepth: 1
10 :maxdepth: 1
11
11
12 release-notes-4.2.1.rst
12 release-notes-4.2.0.rst
13 release-notes-4.2.0.rst
13 release-notes-4.1.2.rst
14 release-notes-4.1.2.rst
14 release-notes-4.1.1.rst
15 release-notes-4.1.1.rst
15 release-notes-4.1.0.rst
16 release-notes-4.1.0.rst
16 release-notes-4.0.1.rst
17 release-notes-4.0.1.rst
17 release-notes-4.0.0.rst
18 release-notes-4.0.0.rst
18
19
19 |RCE| 3.x Versions
20 |RCE| 3.x Versions
20 ------------------
21 ------------------
21
22
22 .. toctree::
23 .. toctree::
23 :maxdepth: 1
24 :maxdepth: 1
24
25
25 release-notes-3.8.4.rst
26 release-notes-3.8.4.rst
26 release-notes-3.8.3.rst
27 release-notes-3.8.3.rst
27 release-notes-3.8.2.rst
28 release-notes-3.8.2.rst
28 release-notes-3.8.1.rst
29 release-notes-3.8.1.rst
29 release-notes-3.8.0.rst
30 release-notes-3.8.0.rst
30 release-notes-3.7.1.rst
31 release-notes-3.7.1.rst
31 release-notes-3.7.0.rst
32 release-notes-3.7.0.rst
32 release-notes-3.6.1.rst
33 release-notes-3.6.1.rst
33 release-notes-3.6.0.rst
34 release-notes-3.6.0.rst
34 release-notes-3.5.2.rst
35 release-notes-3.5.2.rst
35 release-notes-3.5.1.rst
36 release-notes-3.5.1.rst
36 release-notes-3.5.0.rst
37 release-notes-3.5.0.rst
37 release-notes-3.4.1.rst
38 release-notes-3.4.1.rst
38 release-notes-3.4.0.rst
39 release-notes-3.4.0.rst
39 release-notes-3.3.4.rst
40 release-notes-3.3.4.rst
40 release-notes-3.3.3.rst
41 release-notes-3.3.3.rst
41 release-notes-3.3.2.rst
42 release-notes-3.3.2.rst
42 release-notes-3.3.1.rst
43 release-notes-3.3.1.rst
43 release-notes-3.3.0.rst
44 release-notes-3.3.0.rst
44 release-notes-3.2.3.rst
45 release-notes-3.2.3.rst
45 release-notes-3.2.2.rst
46 release-notes-3.2.2.rst
46 release-notes-3.2.1.rst
47 release-notes-3.2.1.rst
47 release-notes-3.2.0.rst
48 release-notes-3.2.0.rst
48 release-notes-3.1.1.rst
49 release-notes-3.1.1.rst
49 release-notes-3.1.0.rst
50 release-notes-3.1.0.rst
50 release-notes-3.0.2.rst
51 release-notes-3.0.2.rst
51 release-notes-3.0.1.rst
52 release-notes-3.0.1.rst
52 release-notes-3.0.0.rst
53 release-notes-3.0.0.rst
53
54
54 |RCE| 2.x Versions
55 |RCE| 2.x Versions
55 ------------------
56 ------------------
56
57
57 .. toctree::
58 .. toctree::
58 :maxdepth: 1
59 :maxdepth: 1
59
60
60 release-notes-2.2.8.rst
61 release-notes-2.2.8.rst
61 release-notes-2.2.7.rst
62 release-notes-2.2.7.rst
62 release-notes-2.2.6.rst
63 release-notes-2.2.6.rst
63 release-notes-2.2.5.rst
64 release-notes-2.2.5.rst
64 release-notes-2.2.4.rst
65 release-notes-2.2.4.rst
65 release-notes-2.2.3.rst
66 release-notes-2.2.3.rst
66 release-notes-2.2.2.rst
67 release-notes-2.2.2.rst
67 release-notes-2.2.1.rst
68 release-notes-2.2.1.rst
68 release-notes-2.2.0.rst
69 release-notes-2.2.0.rst
69 release-notes-2.1.0.rst
70 release-notes-2.1.0.rst
70 release-notes-2.0.2.rst
71 release-notes-2.0.2.rst
71 release-notes-2.0.1.rst
72 release-notes-2.0.1.rst
72 release-notes-2.0.0.rst
73 release-notes-2.0.0.rst
73
74
74 |RCE| 1.x Versions
75 |RCE| 1.x Versions
75 ------------------
76 ------------------
76
77
77 .. toctree::
78 .. toctree::
78 :maxdepth: 1
79 :maxdepth: 1
79
80
80 release-notes-1.7.2.rst
81 release-notes-1.7.2.rst
81 release-notes-1.7.1.rst
82 release-notes-1.7.1.rst
82 release-notes-1.7.0.rst
83 release-notes-1.7.0.rst
83 release-notes-1.6.0.rst
84 release-notes-1.6.0.rst
@@ -1,34 +1,43 b''
1 // translate select2 components
1 // translate select2 components
2 select2Locales = {
2 select2Locales = {
3 formatLoadMore: function(pageNumber) {
3 formatLoadMore: function(pageNumber) {
4 return _TM["Loading more results..."];
4 return _gettext("Loading more results...");
5 },
5 },
6 formatSearching: function() {
6 formatSearching: function() {
7 return _TM["Searching..."];
7 return _gettext("Searching...");
8 },
8 },
9 formatNoMatches: function() {
9 formatNoMatches: function() {
10 return _TM["No matches found"];
10 return _gettext("No matches found");
11 },
11 },
12 formatAjaxError: function(jqXHR, textStatus, errorThrown) {
12 formatAjaxError: function(jqXHR, textStatus, errorThrown) {
13 return _TM["Loading failed"];
13 return _gettext("Loading failed");
14 },
14 },
15 formatMatches: function(matches) {
15 formatMatches: function(matches) {
16 if (matches === 1) {
16 if (matches === 1) {
17 return _TM["One result is available, press enter to select it."];
17 return _gettext("One result is available, press enter to select it.");
18 }
18 }
19 return _TM["{0} results are available, use up and down arrow keys to navigate."].format(matches);
19 return _gettext("{0} results are available, use up and down arrow keys to navigate.").format(matches);
20 },
20 },
21 formatInputTooShort: function(input, min) {
21 formatInputTooShort: function(input, min) {
22 var n = min - input.length;
22 var n = min - input.length;
23 return "Please enter {0} or more character".format(n) + (n === 1 ? "" : "s");
23 if (n === 1) {
24 return _gettext("Please enter {0} or more character").format(n);
25 }
26 return _gettext("Please enter {0} or more characters").format(n);
24 },
27 },
25 formatInputTooLong: function(input, max) {
28 formatInputTooLong: function(input, max) {
26 var n = input.length - max;
29 var n = input.length - max;
27 return "Please delete {0} character".format(n) + (n === 1 ? "" : "s");
30 if (n === 1) {
31 return _gettext("Please delete {0} character").format(n);
32 }
33 return _gettext("Please delete {0} characters").format(n);
28 },
34 },
29 formatSelectionTooBig: function(limit) {
35 formatSelectionTooBig: function(limit) {
30 return "You can only select {0} item".format(limit) + (limit === 1 ? "" : "s");
36 if (limit === 1) {
37 return _gettext("You can only select {0} item").format(limit);
38 }
39 return _gettext("You can only select {0} items").format(limit);
31 }
40 }
32 };
41 };
33
42
34 $.extend($.fn.select2.defaults, select2Locales);
43 $.extend($.fn.select2.defaults, select2Locales);
@@ -1,936 +1,936 b''
1 /**
1 /**
2 * Ajax Autocomplete for jQuery, version dev
2 * Ajax Autocomplete for jQuery, version dev
3 * RhodeCode additions
3 * RhodeCode additions
4 * (c) 2014 Tomas Kirda
4 * (c) 2014 Tomas Kirda
5 * (c) 2014 Marcin Kuzminski
5 * (c) 2014 Marcin Kuzminski
6 *
6 *
7 * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
7 * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
8 * For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
8 * For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
9 */
9 */
10 // Expose plugin as an AMD module if AMD loader is present:
10 // Expose plugin as an AMD module if AMD loader is present:
11 (function (factory) {
11 (function (factory) {
12 'use strict';
12 'use strict';
13 if (typeof define === 'function' && define.amd) {
13 if (typeof define === 'function' && define.amd) {
14 // AMD. Register as an anonymous module.
14 // AMD. Register as an anonymous module.
15 define(['jquery'], factory);
15 define(['jquery'], factory);
16 } else if (typeof exports === 'object' && typeof require === 'function') {
16 } else if (typeof exports === 'object' && typeof require === 'function') {
17 // Browserify
17 // Browserify
18 factory(require('jquery'));
18 factory(require('jquery'));
19 } else {
19 } else {
20 // Browser globals
20 // Browser globals
21 factory(jQuery);
21 factory(jQuery);
22 }
22 }
23 }(function ($) {
23 }(function ($) {
24 'use strict';
24 'use strict';
25
25
26 var
26 var
27 utils = (function () {
27 utils = (function () {
28 return {
28 return {
29 escapeRegExChars: function (value) {
29 escapeRegExChars: function (value) {
30 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
30 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
31 },
31 },
32 createNode: function (containerClass) {
32 createNode: function (containerClass) {
33 var div = document.createElement('div');
33 var div = document.createElement('div');
34 div.className = containerClass;
34 div.className = containerClass;
35 div.style.position = 'absolute';
35 div.style.position = 'absolute';
36 div.style.display = 'none';
36 div.style.display = 'none';
37 return div;
37 return div;
38 }
38 }
39 };
39 };
40 }()),
40 }()),
41
41
42 keys = {
42 keys = {
43 ESC: 27,
43 ESC: 27,
44 TAB: 9,
44 TAB: 9,
45 RETURN: 13,
45 RETURN: 13,
46 LEFT: 37,
46 LEFT: 37,
47 UP: 38,
47 UP: 38,
48 RIGHT: 39,
48 RIGHT: 39,
49 DOWN: 40
49 DOWN: 40
50 };
50 };
51
51
52 function Autocomplete(el, options) {
52 function Autocomplete(el, options) {
53 var noop = function () { },
53 var noop = function () { },
54 that = this,
54 that = this,
55 defaults = {
55 defaults = {
56 ajaxSettings: {},
56 ajaxSettings: {},
57 autoSelectFirst: false,
57 autoSelectFirst: false,
58 appendTo: document.body,
58 appendTo: document.body,
59 serviceUrl: null,
59 serviceUrl: null,
60 lookup: null,
60 lookup: null,
61 width: 'auto',
61 width: 'auto',
62 minChars: 1,
62 minChars: 1,
63 maxHeight: 300,
63 maxHeight: 300,
64 deferRequestBy: 0,
64 deferRequestBy: 0,
65 params: {},
65 params: {},
66 formatResult: Autocomplete.formatResult,
66 formatResult: Autocomplete.formatResult,
67 lookupFilter: Autocomplete.lookupFilter,
67 lookupFilter: Autocomplete.lookupFilter,
68 delimiter: null,
68 delimiter: null,
69 zIndex: 9999,
69 zIndex: 9999,
70 type: 'GET',
70 type: 'GET',
71 noCache: false,
71 noCache: false,
72 onSelect: noop,
72 onSelect: noop,
73 onSearchStart: noop,
73 onSearchStart: noop,
74 onSearchComplete: noop,
74 onSearchComplete: noop,
75 onSearchError: noop,
75 onSearchError: noop,
76 containerClass: 'autocomplete-suggestions',
76 containerClass: 'autocomplete-suggestions',
77 tabDisabled: false,
77 tabDisabled: false,
78 dataType: 'text',
78 dataType: 'text',
79 currentRequest: null,
79 currentRequest: null,
80 triggerSelectOnValidInput: false,
80 triggerSelectOnValidInput: false,
81 preventBadQueries: true,
81 preventBadQueries: true,
82 paramName: 'query',
82 paramName: 'query',
83 transformResult: function (response) {
83 transformResult: function (response) {
84 return typeof response === 'string' ? $.parseJSON(response) : response;
84 return typeof response === 'string' ? $.parseJSON(response) : response;
85 },
85 },
86 showNoSuggestionNotice: false,
86 showNoSuggestionNotice: false,
87 noSuggestionNotice: _TM['No results'],
87 noSuggestionNotice: _gettext('No results'),
88 orientation: 'bottom',
88 orientation: 'bottom',
89 forceFixPosition: false,
89 forceFixPosition: false,
90 replaceOnArrowKey: true
90 replaceOnArrowKey: true
91 };
91 };
92
92
93 // Shared variables:
93 // Shared variables:
94 that.element = el;
94 that.element = el;
95 that.el = $(el);
95 that.el = $(el);
96 that.suggestions = [];
96 that.suggestions = [];
97 that.badQueries = [];
97 that.badQueries = [];
98 that.selectedIndex = -1;
98 that.selectedIndex = -1;
99 that.currentValue = that.element.value;
99 that.currentValue = that.element.value;
100 that.intervalId = 0;
100 that.intervalId = 0;
101 that.cachedResponse = {};
101 that.cachedResponse = {};
102 that.onChangeInterval = null;
102 that.onChangeInterval = null;
103 that.onChange = null;
103 that.onChange = null;
104 that.isLocal = false;
104 that.isLocal = false;
105 that.suggestionsContainer = null;
105 that.suggestionsContainer = null;
106 that.noSuggestionsContainer = null;
106 that.noSuggestionsContainer = null;
107 that.options = $.extend({}, defaults, options);
107 that.options = $.extend({}, defaults, options);
108 that.classes = {
108 that.classes = {
109 selected: 'autocomplete-selected',
109 selected: 'autocomplete-selected',
110 suggestion: 'autocomplete-suggestion'
110 suggestion: 'autocomplete-suggestion'
111 };
111 };
112 that.hint = null;
112 that.hint = null;
113 that.hintValue = '';
113 that.hintValue = '';
114 that.selection = null;
114 that.selection = null;
115
115
116 // Initialize and set options:
116 // Initialize and set options:
117 that.initialize();
117 that.initialize();
118 that.setOptions(options);
118 that.setOptions(options);
119 }
119 }
120
120
121 Autocomplete.utils = utils;
121 Autocomplete.utils = utils;
122
122
123 $.Autocomplete = Autocomplete;
123 $.Autocomplete = Autocomplete;
124
124
125 Autocomplete.formatResult = function (suggestion, currentValue) {
125 Autocomplete.formatResult = function (suggestion, currentValue) {
126 var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
126 var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
127 return suggestion.value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
127 return suggestion.value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
128 };
128 };
129 Autocomplete.lookupFilter = function (suggestion, originalQuery, queryLowerCase) {
129 Autocomplete.lookupFilter = function (suggestion, originalQuery, queryLowerCase) {
130 return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
130 return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
131 };
131 };
132
132
133 Autocomplete.prototype = {
133 Autocomplete.prototype = {
134
134
135 killerFn: null,
135 killerFn: null,
136
136
137 initialize: function () {
137 initialize: function () {
138 var that = this,
138 var that = this,
139 suggestionSelector = '.' + that.classes.suggestion,
139 suggestionSelector = '.' + that.classes.suggestion,
140 selected = that.classes.selected,
140 selected = that.classes.selected,
141 options = that.options,
141 options = that.options,
142 container;
142 container;
143
143
144 // Remove autocomplete attribute to prevent native suggestions:
144 // Remove autocomplete attribute to prevent native suggestions:
145 that.element.setAttribute('autocomplete', 'off');
145 that.element.setAttribute('autocomplete', 'off');
146
146
147 that.killerFn = function (e) {
147 that.killerFn = function (e) {
148 if ($(e.target).closest('.' + that.options.containerClass).length === 0) {
148 if ($(e.target).closest('.' + that.options.containerClass).length === 0) {
149 that.killSuggestions();
149 that.killSuggestions();
150 that.disableKillerFn();
150 that.disableKillerFn();
151 }
151 }
152 };
152 };
153
153
154 // html() deals with many types: htmlString or Element or Array or jQuery
154 // html() deals with many types: htmlString or Element or Array or jQuery
155 that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
155 that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
156 .html(this.options.noSuggestionNotice).get(0);
156 .html(this.options.noSuggestionNotice).get(0);
157
157
158 that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
158 that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
159
159
160 container = $(that.suggestionsContainer);
160 container = $(that.suggestionsContainer);
161
161
162 container.appendTo(options.appendTo);
162 container.appendTo(options.appendTo);
163
163
164 // Only set width if it was provided:
164 // Only set width if it was provided:
165 if (options.width !== 'auto') {
165 if (options.width !== 'auto') {
166 container.width(options.width);
166 container.width(options.width);
167 }
167 }
168
168
169 // Listen for mouse over event on suggestions list:
169 // Listen for mouse over event on suggestions list:
170 container.on('mouseover.autocomplete', suggestionSelector, function () {
170 container.on('mouseover.autocomplete', suggestionSelector, function () {
171 that.activate($(this).data('index'));
171 that.activate($(this).data('index'));
172 });
172 });
173
173
174 // Deselect active element when mouse leaves suggestions container:
174 // Deselect active element when mouse leaves suggestions container:
175 container.on('mouseout.autocomplete', function () {
175 container.on('mouseout.autocomplete', function () {
176 that.selectedIndex = -1;
176 that.selectedIndex = -1;
177 container.children('.' + selected).removeClass(selected);
177 container.children('.' + selected).removeClass(selected);
178 });
178 });
179
179
180 // Listen for click event on suggestions list:
180 // Listen for click event on suggestions list:
181 container.on('click.autocomplete', suggestionSelector, function () {
181 container.on('click.autocomplete', suggestionSelector, function () {
182 that.select($(this).data('index'));
182 that.select($(this).data('index'));
183 });
183 });
184
184
185 that.fixPositionCapture = function () {
185 that.fixPositionCapture = function () {
186 if (that.visible) {
186 if (that.visible) {
187 that.fixPosition();
187 that.fixPosition();
188 }
188 }
189 };
189 };
190
190
191 $(window).on('resize.autocomplete', that.fixPositionCapture);
191 $(window).on('resize.autocomplete', that.fixPositionCapture);
192
192
193 that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
193 that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
194 that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
194 that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
195 that.el.on('blur.autocomplete', function () { that.onBlur(); });
195 that.el.on('blur.autocomplete', function () { that.onBlur(); });
196 that.el.on('focus.autocomplete', function () { that.onFocus(); });
196 that.el.on('focus.autocomplete', function () { that.onFocus(); });
197 that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
197 that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
198 },
198 },
199
199
200 onFocus: function () {
200 onFocus: function () {
201 var that = this;
201 var that = this;
202 that.fixPosition();
202 that.fixPosition();
203 if (that.options.minChars <= that.el.val().length) {
203 if (that.options.minChars <= that.el.val().length) {
204 that.onValueChange();
204 that.onValueChange();
205 }
205 }
206 },
206 },
207
207
208 onBlur: function () {
208 onBlur: function () {
209 this.enableKillerFn();
209 this.enableKillerFn();
210 },
210 },
211
211
212 setOptions: function (suppliedOptions) {
212 setOptions: function (suppliedOptions) {
213 var that = this,
213 var that = this,
214 options = that.options;
214 options = that.options;
215
215
216 $.extend(options, suppliedOptions);
216 $.extend(options, suppliedOptions);
217
217
218 that.isLocal = $.isArray(options.lookup);
218 that.isLocal = $.isArray(options.lookup);
219
219
220 if (that.isLocal) {
220 if (that.isLocal) {
221 options.lookup = that.verifySuggestionsFormat(options.lookup);
221 options.lookup = that.verifySuggestionsFormat(options.lookup);
222 }
222 }
223
223
224 options.orientation = that.validateOrientation(options.orientation, 'bottom');
224 options.orientation = that.validateOrientation(options.orientation, 'bottom');
225
225
226 // Adjust height, width and z-index:
226 // Adjust height, width and z-index:
227 $(that.suggestionsContainer).css({
227 $(that.suggestionsContainer).css({
228 'max-height': options.maxHeight + 'px',
228 'max-height': options.maxHeight + 'px',
229 'width': options.width + 'px',
229 'width': options.width + 'px',
230 'z-index': options.zIndex
230 'z-index': options.zIndex
231 });
231 });
232 },
232 },
233
233
234 clearCache: function () {
234 clearCache: function () {
235 this.cachedResponse = {};
235 this.cachedResponse = {};
236 this.badQueries = [];
236 this.badQueries = [];
237 },
237 },
238
238
239 clear: function () {
239 clear: function () {
240 this.clearCache();
240 this.clearCache();
241 this.currentValue = '';
241 this.currentValue = '';
242 this.suggestions = [];
242 this.suggestions = [];
243 },
243 },
244
244
245 disable: function () {
245 disable: function () {
246 var that = this;
246 var that = this;
247 that.disabled = true;
247 that.disabled = true;
248 if (that.currentRequest) {
248 if (that.currentRequest) {
249 that.currentRequest.abort();
249 that.currentRequest.abort();
250 }
250 }
251 },
251 },
252
252
253 enable: function () {
253 enable: function () {
254 this.disabled = false;
254 this.disabled = false;
255 },
255 },
256
256
257 fixPosition: function () {
257 fixPosition: function () {
258 // Use only when container has already its content
258 // Use only when container has already its content
259
259
260 var that = this,
260 var that = this,
261 $container = $(that.suggestionsContainer),
261 $container = $(that.suggestionsContainer),
262 containerParent = $container.parent().get(0);
262 containerParent = $container.parent().get(0);
263 // Fix position automatically when appended to body.
263 // Fix position automatically when appended to body.
264 // In other cases force parameter must be given.
264 // In other cases force parameter must be given.
265 if (containerParent !== document.body && !that.options.forceFixPosition)
265 if (containerParent !== document.body && !that.options.forceFixPosition)
266 return;
266 return;
267
267
268 // Choose orientation
268 // Choose orientation
269 var orientation = that.options.orientation,
269 var orientation = that.options.orientation,
270 containerHeight = $container.outerHeight(),
270 containerHeight = $container.outerHeight(),
271 height = that.el.outerHeight(),
271 height = that.el.outerHeight(),
272 offset = that.el.offset(),
272 offset = that.el.offset(),
273 styles = { 'top': offset.top, 'left': offset.left };
273 styles = { 'top': offset.top, 'left': offset.left };
274
274
275 if (orientation == 'auto') {
275 if (orientation == 'auto') {
276 var viewPortHeight = $(window).height(),
276 var viewPortHeight = $(window).height(),
277 scrollTop = $(window).scrollTop(),
277 scrollTop = $(window).scrollTop(),
278 topOverflow = -scrollTop + offset.top - containerHeight,
278 topOverflow = -scrollTop + offset.top - containerHeight,
279 bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight);
279 bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight);
280
280
281 orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow)
281 orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow)
282 ? 'top'
282 ? 'top'
283 : 'bottom';
283 : 'bottom';
284 }
284 }
285
285
286 if (orientation === 'top') {
286 if (orientation === 'top') {
287 styles.top += -containerHeight;
287 styles.top += -containerHeight;
288 } else {
288 } else {
289 styles.top += height;
289 styles.top += height;
290 }
290 }
291
291
292 // If container is not positioned to body,
292 // If container is not positioned to body,
293 // correct its position using offset parent offset
293 // correct its position using offset parent offset
294 if(containerParent !== document.body) {
294 if(containerParent !== document.body) {
295 var opacity = $container.css('opacity'),
295 var opacity = $container.css('opacity'),
296 parentOffsetDiff;
296 parentOffsetDiff;
297
297
298 if (!that.visible){
298 if (!that.visible){
299 $container.css('opacity', 0).show();
299 $container.css('opacity', 0).show();
300 }
300 }
301
301
302 parentOffsetDiff = $container.offsetParent().offset();
302 parentOffsetDiff = $container.offsetParent().offset();
303 styles.top -= parentOffsetDiff.top;
303 styles.top -= parentOffsetDiff.top;
304 styles.left -= parentOffsetDiff.left;
304 styles.left -= parentOffsetDiff.left;
305
305
306 if (!that.visible){
306 if (!that.visible){
307 $container.css('opacity', opacity).hide();
307 $container.css('opacity', opacity).hide();
308 }
308 }
309 }
309 }
310
310
311 // -2px to account for suggestions border.
311 // -2px to account for suggestions border.
312 if (that.options.width === 'auto') {
312 if (that.options.width === 'auto') {
313 styles.width = (that.el.outerWidth() - 2) + 'px';
313 styles.width = (that.el.outerWidth() - 2) + 'px';
314 }
314 }
315
315
316 $container.css(styles);
316 $container.css(styles);
317 },
317 },
318
318
319 enableKillerFn: function () {
319 enableKillerFn: function () {
320 var that = this;
320 var that = this;
321 $(document).on('click.autocomplete', that.killerFn);
321 $(document).on('click.autocomplete', that.killerFn);
322 },
322 },
323
323
324 disableKillerFn: function () {
324 disableKillerFn: function () {
325 var that = this;
325 var that = this;
326 $(document).off('click.autocomplete', that.killerFn);
326 $(document).off('click.autocomplete', that.killerFn);
327 },
327 },
328
328
329 killSuggestions: function () {
329 killSuggestions: function () {
330 var that = this;
330 var that = this;
331 that.stopKillSuggestions();
331 that.stopKillSuggestions();
332 that.intervalId = window.setInterval(function () {
332 that.intervalId = window.setInterval(function () {
333 that.hide();
333 that.hide();
334 that.stopKillSuggestions();
334 that.stopKillSuggestions();
335 }, 50);
335 }, 50);
336 },
336 },
337
337
338 stopKillSuggestions: function () {
338 stopKillSuggestions: function () {
339 window.clearInterval(this.intervalId);
339 window.clearInterval(this.intervalId);
340 },
340 },
341
341
342 isCursorAtEnd: function () {
342 isCursorAtEnd: function () {
343 var that = this,
343 var that = this,
344 valLength = that.el.val().length,
344 valLength = that.el.val().length,
345 selectionStart = that.element.selectionStart,
345 selectionStart = that.element.selectionStart,
346 range;
346 range;
347
347
348 if (typeof selectionStart === 'number') {
348 if (typeof selectionStart === 'number') {
349 return selectionStart === valLength;
349 return selectionStart === valLength;
350 }
350 }
351 if (document.selection) {
351 if (document.selection) {
352 range = document.selection.createRange();
352 range = document.selection.createRange();
353 range.moveStart('character', -valLength);
353 range.moveStart('character', -valLength);
354 return valLength === range.text.length;
354 return valLength === range.text.length;
355 }
355 }
356 return true;
356 return true;
357 },
357 },
358
358
359 onKeyPress: function (e) {
359 onKeyPress: function (e) {
360 var that = this;
360 var that = this;
361
361
362 // If suggestions are hidden and user presses arrow down, display suggestions:
362 // If suggestions are hidden and user presses arrow down, display suggestions:
363 if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
363 if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
364 that.suggest();
364 that.suggest();
365 return;
365 return;
366 }
366 }
367
367
368 if (that.disabled || !that.visible) {
368 if (that.disabled || !that.visible) {
369 return;
369 return;
370 }
370 }
371
371
372 switch (e.which) {
372 switch (e.which) {
373 case keys.ESC:
373 case keys.ESC:
374 that.el.val(that.currentValue);
374 that.el.val(that.currentValue);
375 that.hide();
375 that.hide();
376 break;
376 break;
377 case keys.RIGHT:
377 case keys.RIGHT:
378 if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
378 if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
379 that.selectHint();
379 that.selectHint();
380 break;
380 break;
381 }
381 }
382 return;
382 return;
383 case keys.TAB:
383 case keys.TAB:
384 if (that.hint && that.options.onHint) {
384 if (that.hint && that.options.onHint) {
385 that.selectHint();
385 that.selectHint();
386 return;
386 return;
387 }
387 }
388 // Fall through to RETURN
388 // Fall through to RETURN
389 case keys.RETURN:
389 case keys.RETURN:
390 if (that.selectedIndex === -1) {
390 if (that.selectedIndex === -1) {
391 that.hide();
391 that.hide();
392 return;
392 return;
393 }
393 }
394 that.select(that.selectedIndex);
394 that.select(that.selectedIndex);
395 if (e.which === keys.TAB && that.options.tabDisabled === false) {
395 if (e.which === keys.TAB && that.options.tabDisabled === false) {
396 return;
396 return;
397 }
397 }
398 break;
398 break;
399 case keys.UP:
399 case keys.UP:
400 that.moveUp();
400 that.moveUp();
401 break;
401 break;
402 case keys.DOWN:
402 case keys.DOWN:
403 that.moveDown();
403 that.moveDown();
404 break;
404 break;
405 default:
405 default:
406 return;
406 return;
407 }
407 }
408
408
409 // Cancel event if function did not return:
409 // Cancel event if function did not return:
410 e.stopImmediatePropagation();
410 e.stopImmediatePropagation();
411 e.preventDefault();
411 e.preventDefault();
412 },
412 },
413
413
414 onKeyUp: function (e) {
414 onKeyUp: function (e) {
415 var that = this;
415 var that = this;
416
416
417 if (that.disabled) {
417 if (that.disabled) {
418 return;
418 return;
419 }
419 }
420
420
421 switch (e.which) {
421 switch (e.which) {
422 case keys.UP:
422 case keys.UP:
423 case keys.DOWN:
423 case keys.DOWN:
424 return;
424 return;
425 }
425 }
426
426
427 clearInterval(that.onChangeInterval);
427 clearInterval(that.onChangeInterval);
428
428
429 if (that.currentValue !== that.el.val()) {
429 if (that.currentValue !== that.el.val()) {
430 that.findBestHint();
430 that.findBestHint();
431 if (that.options.deferRequestBy > 0) {
431 if (that.options.deferRequestBy > 0) {
432 // Defer lookup in case when value changes very quickly:
432 // Defer lookup in case when value changes very quickly:
433 that.onChangeInterval = setInterval(function () {
433 that.onChangeInterval = setInterval(function () {
434 that.onValueChange();
434 that.onValueChange();
435 }, that.options.deferRequestBy);
435 }, that.options.deferRequestBy);
436 } else {
436 } else {
437 that.onValueChange();
437 that.onValueChange();
438 }
438 }
439 }
439 }
440 },
440 },
441
441
442 onValueChange: function () {
442 onValueChange: function () {
443 var that = this,
443 var that = this,
444 options = that.options,
444 options = that.options,
445 value = that.el.val(),
445 value = that.el.val(),
446 query = that.getQuery(value),
446 query = that.getQuery(value),
447 index;
447 index;
448
448
449 if (that.selection && that.currentValue !== query) {
449 if (that.selection && that.currentValue !== query) {
450 that.selection = null;
450 that.selection = null;
451 (options.onInvalidateSelection || $.noop).call(that.element);
451 (options.onInvalidateSelection || $.noop).call(that.element);
452 }
452 }
453
453
454 clearInterval(that.onChangeInterval);
454 clearInterval(that.onChangeInterval);
455 that.currentValue = value;
455 that.currentValue = value;
456 that.selectedIndex = -1;
456 that.selectedIndex = -1;
457
457
458 // Check existing suggestion for the match before proceeding:
458 // Check existing suggestion for the match before proceeding:
459 if (options.triggerSelectOnValidInput) {
459 if (options.triggerSelectOnValidInput) {
460 index = that.findSuggestionIndex(query);
460 index = that.findSuggestionIndex(query);
461 if (index !== -1) {
461 if (index !== -1) {
462 that.select(index);
462 that.select(index);
463 return;
463 return;
464 }
464 }
465 }
465 }
466
466
467 if (query.length < options.minChars) {
467 if (query.length < options.minChars) {
468 that.hide();
468 that.hide();
469 } else {
469 } else {
470 that.getSuggestions(query);
470 that.getSuggestions(query);
471 }
471 }
472 },
472 },
473
473
474 findSuggestionIndex: function (query) {
474 findSuggestionIndex: function (query) {
475 var that = this,
475 var that = this,
476 index = -1,
476 index = -1,
477 queryLowerCase = query.toLowerCase();
477 queryLowerCase = query.toLowerCase();
478
478
479 $.each(that.suggestions, function (i, suggestion) {
479 $.each(that.suggestions, function (i, suggestion) {
480 if (suggestion.value.toLowerCase() === queryLowerCase) {
480 if (suggestion.value.toLowerCase() === queryLowerCase) {
481 index = i;
481 index = i;
482 return false;
482 return false;
483 }
483 }
484 });
484 });
485
485
486 return index;
486 return index;
487 },
487 },
488
488
489 getQuery: function (value) {
489 getQuery: function (value) {
490 var delimiter = this.options.delimiter,
490 var delimiter = this.options.delimiter,
491 parts;
491 parts;
492
492
493 if (!delimiter) {
493 if (!delimiter) {
494 return value;
494 return value;
495 }
495 }
496 parts = value.split(delimiter);
496 parts = value.split(delimiter);
497 return $.trim(parts[parts.length - 1]);
497 return $.trim(parts[parts.length - 1]);
498 },
498 },
499
499
500 getSuggestionsLocal: function (query) {
500 getSuggestionsLocal: function (query) {
501 var that = this,
501 var that = this,
502 options = that.options,
502 options = that.options,
503 queryLowerCase = query.toLowerCase(),
503 queryLowerCase = query.toLowerCase(),
504 data;
504 data;
505
505
506 // re-pack the data as it was comming from AJAX
506 // re-pack the data as it was comming from AJAX
507 data = {
507 data = {
508 suggestions: data
508 suggestions: data
509 };
509 };
510 return data;
510 return data;
511 },
511 },
512
512
513 getSuggestions: function (query) {
513 getSuggestions: function (query) {
514 var response,
514 var response,
515 that = this,
515 that = this,
516 options = that.options,
516 options = that.options,
517 serviceUrl = options.serviceUrl,
517 serviceUrl = options.serviceUrl,
518 params,
518 params,
519 cacheKey,
519 cacheKey,
520 ajaxSettings;
520 ajaxSettings;
521
521
522 options.params[options.paramName] = query;
522 options.params[options.paramName] = query;
523 params = options.ignoreParams ? null : options.params;
523 params = options.ignoreParams ? null : options.params;
524
524
525 if (that.isLocal) {
525 if (that.isLocal) {
526 response = that.getSuggestionsLocal(query);
526 response = that.getSuggestionsLocal(query);
527 } else {
527 } else {
528 if ($.isFunction(serviceUrl)) {
528 if ($.isFunction(serviceUrl)) {
529 serviceUrl = serviceUrl.call(that.element, query);
529 serviceUrl = serviceUrl.call(that.element, query);
530 }
530 }
531 cacheKey = serviceUrl + '?' + $.param(params || {});
531 cacheKey = serviceUrl + '?' + $.param(params || {});
532 response = that.cachedResponse[cacheKey];
532 response = that.cachedResponse[cacheKey];
533 }
533 }
534
534
535 if (response && $.isArray(response.suggestions)) {
535 if (response && $.isArray(response.suggestions)) {
536 that.suggestions = response.suggestions;
536 that.suggestions = response.suggestions;
537 that.suggest();
537 that.suggest();
538 } else if (!that.isBadQuery(query)) {
538 } else if (!that.isBadQuery(query)) {
539 if (options.onSearchStart.call(that.element, options.params) === false) {
539 if (options.onSearchStart.call(that.element, options.params) === false) {
540 return;
540 return;
541 }
541 }
542 if (that.currentRequest) {
542 if (that.currentRequest) {
543 that.currentRequest.abort();
543 that.currentRequest.abort();
544 }
544 }
545
545
546 ajaxSettings = {
546 ajaxSettings = {
547 url: serviceUrl,
547 url: serviceUrl,
548 data: params,
548 data: params,
549 type: options.type,
549 type: options.type,
550 dataType: options.dataType
550 dataType: options.dataType
551 };
551 };
552
552
553 $.extend(ajaxSettings, options.ajaxSettings);
553 $.extend(ajaxSettings, options.ajaxSettings);
554
554
555 that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
555 that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
556 var result;
556 var result;
557 that.currentRequest = null;
557 that.currentRequest = null;
558 result = options.transformResult(data);
558 result = options.transformResult(data);
559 that.processResponse(result, query, cacheKey);
559 that.processResponse(result, query, cacheKey);
560 options.onSearchComplete.call(that.element, query, result.suggestions);
560 options.onSearchComplete.call(that.element, query, result.suggestions);
561 }).fail(function (jqXHR, textStatus, errorThrown) {
561 }).fail(function (jqXHR, textStatus, errorThrown) {
562 options.onSearchError.call(that.element, query, jqXHR, textStatus, errorThrown);
562 options.onSearchError.call(that.element, query, jqXHR, textStatus, errorThrown);
563 });
563 });
564 }
564 }
565 },
565 },
566
566
567 isBadQuery: function (q) {
567 isBadQuery: function (q) {
568 if (!this.options.preventBadQueries){
568 if (!this.options.preventBadQueries){
569 return false;
569 return false;
570 }
570 }
571
571
572 var badQueries = this.badQueries,
572 var badQueries = this.badQueries,
573 i = badQueries.length;
573 i = badQueries.length;
574
574
575 while (i--) {
575 while (i--) {
576 if (q.indexOf(badQueries[i]) === 0) {
576 if (q.indexOf(badQueries[i]) === 0) {
577 return true;
577 return true;
578 }
578 }
579 }
579 }
580
580
581 return false;
581 return false;
582 },
582 },
583
583
584 hide: function () {
584 hide: function () {
585 var that = this;
585 var that = this;
586 that.visible = false;
586 that.visible = false;
587 that.selectedIndex = -1;
587 that.selectedIndex = -1;
588 $(that.suggestionsContainer).hide();
588 $(that.suggestionsContainer).hide();
589 that.signalHint(null);
589 that.signalHint(null);
590 },
590 },
591
591
592 suggest: function () {
592 suggest: function () {
593
593
594 var that = this,
594 var that = this,
595 options = that.options,
595 options = that.options,
596 formatResult = options.formatResult,
596 formatResult = options.formatResult,
597 filterResult = options.lookupFilter,
597 filterResult = options.lookupFilter,
598 value = that.getQuery(that.currentValue),
598 value = that.getQuery(that.currentValue),
599 className = that.classes.suggestion,
599 className = that.classes.suggestion,
600 classSelected = that.classes.selected,
600 classSelected = that.classes.selected,
601 container = $(that.suggestionsContainer),
601 container = $(that.suggestionsContainer),
602 noSuggestionsContainer = $(that.noSuggestionsContainer),
602 noSuggestionsContainer = $(that.noSuggestionsContainer),
603 beforeRender = options.beforeRender,
603 beforeRender = options.beforeRender,
604 limit = parseInt(that.options.lookupLimit, 10),
604 limit = parseInt(that.options.lookupLimit, 10),
605 html = '',
605 html = '',
606 index;
606 index;
607
607
608 // filter and limit given results
608 // filter and limit given results
609 var filtered_suggestions = $.grep(that.suggestions, function (suggestion) {
609 var filtered_suggestions = $.grep(that.suggestions, function (suggestion) {
610 return filterResult(suggestion, value, value.toLowerCase(), that.element);
610 return filterResult(suggestion, value, value.toLowerCase(), that.element);
611 });
611 });
612
612
613 if (limit && filtered_suggestions.length > limit) {
613 if (limit && filtered_suggestions.length > limit) {
614 filtered_suggestions = filtered_suggestions.slice(0, limit);
614 filtered_suggestions = filtered_suggestions.slice(0, limit);
615 }
615 }
616
616
617 if (filtered_suggestions.length === 0) {
617 if (filtered_suggestions.length === 0) {
618 this.options.showNoSuggestionNotice ? this.noSuggestions() : this.hide();
618 this.options.showNoSuggestionNotice ? this.noSuggestions() : this.hide();
619 return;
619 return;
620 }
620 }
621
621
622 if (options.triggerSelectOnValidInput) {
622 if (options.triggerSelectOnValidInput) {
623 index = that.findSuggestionIndex(value);
623 index = that.findSuggestionIndex(value);
624 if (index !== -1) {
624 if (index !== -1) {
625 that.select(index);
625 that.select(index);
626 return;
626 return;
627 }
627 }
628 }
628 }
629
629
630 // Build suggestions inner HTML:
630 // Build suggestions inner HTML:
631 $.each(filtered_suggestions, function (i, suggestion) {
631 $.each(filtered_suggestions, function (i, suggestion) {
632 html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value, Autocomplete.formatResult, that.element) + '</div>';
632 html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value, Autocomplete.formatResult, that.element) + '</div>';
633 });
633 });
634 // set internal suggestion for INDEX pick to work correctly
634 // set internal suggestion for INDEX pick to work correctly
635 that.suggestions = filtered_suggestions;
635 that.suggestions = filtered_suggestions;
636 this.adjustContainerWidth();
636 this.adjustContainerWidth();
637
637
638 noSuggestionsContainer.detach();
638 noSuggestionsContainer.detach();
639 container.html(html);
639 container.html(html);
640
640
641 // Select first value by default:
641 // Select first value by default:
642 if (options.autoSelectFirst) {
642 if (options.autoSelectFirst) {
643 that.selectedIndex = 0;
643 that.selectedIndex = 0;
644 container.children().first().addClass(classSelected);
644 container.children().first().addClass(classSelected);
645 }
645 }
646
646
647 if ($.isFunction(beforeRender)) {
647 if ($.isFunction(beforeRender)) {
648 beforeRender.call(that.element, container);
648 beforeRender.call(that.element, container);
649 }
649 }
650
650
651 that.fixPosition();
651 that.fixPosition();
652
652
653 container.show();
653 container.show();
654 that.visible = true;
654 that.visible = true;
655
655
656 that.findBestHint();
656 that.findBestHint();
657 },
657 },
658
658
659 noSuggestions: function() {
659 noSuggestions: function() {
660 var that = this,
660 var that = this,
661 container = $(that.suggestionsContainer),
661 container = $(that.suggestionsContainer),
662 noSuggestionsContainer = $(that.noSuggestionsContainer);
662 noSuggestionsContainer = $(that.noSuggestionsContainer);
663
663
664 this.adjustContainerWidth();
664 this.adjustContainerWidth();
665
665
666 // Some explicit steps. Be careful here as it easy to get
666 // Some explicit steps. Be careful here as it easy to get
667 // noSuggestionsContainer removed from DOM if not detached properly.
667 // noSuggestionsContainer removed from DOM if not detached properly.
668 noSuggestionsContainer.detach();
668 noSuggestionsContainer.detach();
669 container.empty(); // clean suggestions if any
669 container.empty(); // clean suggestions if any
670 container.append(noSuggestionsContainer);
670 container.append(noSuggestionsContainer);
671
671
672 that.fixPosition();
672 that.fixPosition();
673
673
674 container.show();
674 container.show();
675 that.visible = true;
675 that.visible = true;
676 },
676 },
677
677
678 adjustContainerWidth: function() {
678 adjustContainerWidth: function() {
679 var that = this,
679 var that = this,
680 options = that.options,
680 options = that.options,
681 width,
681 width,
682 container = $(that.suggestionsContainer);
682 container = $(that.suggestionsContainer);
683
683
684 // If width is auto, adjust width before displaying suggestions,
684 // If width is auto, adjust width before displaying suggestions,
685 // because if instance was created before input had width, it will be zero.
685 // because if instance was created before input had width, it will be zero.
686 // Also it adjusts if input width has changed.
686 // Also it adjusts if input width has changed.
687 // -2px to account for suggestions border.
687 // -2px to account for suggestions border.
688 if (options.width === 'auto') {
688 if (options.width === 'auto') {
689 width = that.el.outerWidth() - 2;
689 width = that.el.outerWidth() - 2;
690 container.width(width > 0 ? width : 300);
690 container.width(width > 0 ? width : 300);
691 }
691 }
692 },
692 },
693
693
694 findBestHint: function () {
694 findBestHint: function () {
695 var that = this,
695 var that = this,
696 value = that.el.val().toLowerCase(),
696 value = that.el.val().toLowerCase(),
697 bestMatch = null;
697 bestMatch = null;
698
698
699 if (!value) {
699 if (!value) {
700 return;
700 return;
701 }
701 }
702
702
703 $.each(that.suggestions, function (i, suggestion) {
703 $.each(that.suggestions, function (i, suggestion) {
704 var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
704 var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
705 if (foundMatch) {
705 if (foundMatch) {
706 bestMatch = suggestion;
706 bestMatch = suggestion;
707 }
707 }
708 return !foundMatch;
708 return !foundMatch;
709 });
709 });
710 that.signalHint(bestMatch);
710 that.signalHint(bestMatch);
711 },
711 },
712
712
713 signalHint: function (suggestion) {
713 signalHint: function (suggestion) {
714 var hintValue = '',
714 var hintValue = '',
715 that = this;
715 that = this;
716 if (suggestion) {
716 if (suggestion) {
717 hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
717 hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
718 }
718 }
719 if (that.hintValue !== hintValue) {
719 if (that.hintValue !== hintValue) {
720 that.hintValue = hintValue;
720 that.hintValue = hintValue;
721 that.hint = suggestion;
721 that.hint = suggestion;
722 (this.options.onHint || $.noop)(hintValue);
722 (this.options.onHint || $.noop)(hintValue);
723 }
723 }
724 },
724 },
725
725
726 verifySuggestionsFormat: function (suggestions) {
726 verifySuggestionsFormat: function (suggestions) {
727 // If suggestions is string array, convert them to supported format:
727 // If suggestions is string array, convert them to supported format:
728 if (suggestions.length && typeof suggestions[0] === 'string') {
728 if (suggestions.length && typeof suggestions[0] === 'string') {
729 return $.map(suggestions, function (value) {
729 return $.map(suggestions, function (value) {
730 return { value: value, data: null };
730 return { value: value, data: null };
731 });
731 });
732 }
732 }
733
733
734 return suggestions;
734 return suggestions;
735 },
735 },
736
736
737 validateOrientation: function(orientation, fallback) {
737 validateOrientation: function(orientation, fallback) {
738 orientation = $.trim(orientation || '').toLowerCase();
738 orientation = $.trim(orientation || '').toLowerCase();
739
739
740 if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){
740 if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){
741 orientation = fallback;
741 orientation = fallback;
742 }
742 }
743
743
744 return orientation;
744 return orientation;
745 },
745 },
746
746
747 processResponse: function (result, originalQuery, cacheKey) {
747 processResponse: function (result, originalQuery, cacheKey) {
748 var that = this,
748 var that = this,
749 options = that.options;
749 options = that.options;
750
750
751 result.suggestions = that.verifySuggestionsFormat(result.suggestions);
751 result.suggestions = that.verifySuggestionsFormat(result.suggestions);
752
752
753 // Cache results if cache is not disabled:
753 // Cache results if cache is not disabled:
754 if (!options.noCache) {
754 if (!options.noCache) {
755 that.cachedResponse[cacheKey] = result;
755 that.cachedResponse[cacheKey] = result;
756 if (options.preventBadQueries && result.suggestions.length === 0) {
756 if (options.preventBadQueries && result.suggestions.length === 0) {
757 that.badQueries.push(originalQuery);
757 that.badQueries.push(originalQuery);
758 }
758 }
759 }
759 }
760
760
761 // Return if originalQuery is not matching current query:
761 // Return if originalQuery is not matching current query:
762 if (originalQuery !== that.getQuery(that.currentValue)) {
762 if (originalQuery !== that.getQuery(that.currentValue)) {
763 return;
763 return;
764 }
764 }
765
765
766 that.suggestions = result.suggestions;
766 that.suggestions = result.suggestions;
767 that.suggest();
767 that.suggest();
768 },
768 },
769
769
770 activate: function (index) {
770 activate: function (index) {
771 var that = this,
771 var that = this,
772 activeItem,
772 activeItem,
773 selected = that.classes.selected,
773 selected = that.classes.selected,
774 container = $(that.suggestionsContainer),
774 container = $(that.suggestionsContainer),
775 children = container.find('.' + that.classes.suggestion);
775 children = container.find('.' + that.classes.suggestion);
776
776
777 container.find('.' + selected).removeClass(selected);
777 container.find('.' + selected).removeClass(selected);
778
778
779 that.selectedIndex = index;
779 that.selectedIndex = index;
780
780
781 if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
781 if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
782 activeItem = children.get(that.selectedIndex);
782 activeItem = children.get(that.selectedIndex);
783 $(activeItem).addClass(selected);
783 $(activeItem).addClass(selected);
784 return activeItem;
784 return activeItem;
785 }
785 }
786
786
787 return null;
787 return null;
788 },
788 },
789
789
790 selectHint: function () {
790 selectHint: function () {
791 var that = this,
791 var that = this,
792 i = $.inArray(that.hint, that.suggestions);
792 i = $.inArray(that.hint, that.suggestions);
793 that.select(i);
793 that.select(i);
794 },
794 },
795
795
796 select: function (index) {
796 select: function (index) {
797 var that = this;
797 var that = this;
798 that.hide();
798 that.hide();
799 that.onSelect(index);
799 that.onSelect(index);
800 },
800 },
801
801
802 moveUp: function () {
802 moveUp: function () {
803 var that = this;
803 var that = this;
804
804
805 if (that.selectedIndex === -1) {
805 if (that.selectedIndex === -1) {
806 return;
806 return;
807 }
807 }
808
808
809 if (that.selectedIndex === 0) {
809 if (that.selectedIndex === 0) {
810 $(that.suggestionsContainer).children().first().removeClass(that.classes.selected);
810 $(that.suggestionsContainer).children().first().removeClass(that.classes.selected);
811 that.selectedIndex = -1;
811 that.selectedIndex = -1;
812 that.el.val(that.currentValue);
812 that.el.val(that.currentValue);
813 that.findBestHint();
813 that.findBestHint();
814 return;
814 return;
815 }
815 }
816
816
817 that.adjustScroll(that.selectedIndex - 1);
817 that.adjustScroll(that.selectedIndex - 1);
818 },
818 },
819
819
820 moveDown: function () {
820 moveDown: function () {
821 var that = this;
821 var that = this;
822
822
823 if (that.selectedIndex === (that.suggestions.length - 1)) {
823 if (that.selectedIndex === (that.suggestions.length - 1)) {
824 return;
824 return;
825 }
825 }
826
826
827 that.adjustScroll(that.selectedIndex + 1);
827 that.adjustScroll(that.selectedIndex + 1);
828 },
828 },
829
829
830 adjustScroll: function (index) {
830 adjustScroll: function (index) {
831 var that = this,
831 var that = this,
832 activeItem = that.activate(index),
832 activeItem = that.activate(index),
833 offsetTop,
833 offsetTop,
834 upperBound,
834 upperBound,
835 lowerBound,
835 lowerBound,
836 heightDelta = 25;
836 heightDelta = 25;
837
837
838 if (!activeItem) {
838 if (!activeItem) {
839 return;
839 return;
840 }
840 }
841
841
842 offsetTop = activeItem.offsetTop;
842 offsetTop = activeItem.offsetTop;
843 upperBound = $(that.suggestionsContainer).scrollTop();
843 upperBound = $(that.suggestionsContainer).scrollTop();
844 lowerBound = upperBound + that.options.maxHeight - heightDelta;
844 lowerBound = upperBound + that.options.maxHeight - heightDelta;
845
845
846 if (offsetTop < upperBound) {
846 if (offsetTop < upperBound) {
847 $(that.suggestionsContainer).scrollTop(offsetTop);
847 $(that.suggestionsContainer).scrollTop(offsetTop);
848 } else if (offsetTop > lowerBound) {
848 } else if (offsetTop > lowerBound) {
849 $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
849 $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
850 }
850 }
851
851
852 if (that.options.replaceOnArrowKey) {
852 if (that.options.replaceOnArrowKey) {
853 that.el.val(that.getValue(that.suggestions[index].value));
853 that.el.val(that.getValue(that.suggestions[index].value));
854 }
854 }
855 that.signalHint(null);
855 that.signalHint(null);
856 },
856 },
857
857
858 onSelect: function (index) {
858 onSelect: function (index) {
859 var that = this,
859 var that = this,
860 onSelectCallback = that.options.onSelect,
860 onSelectCallback = that.options.onSelect,
861 suggestion = that.suggestions[index];
861 suggestion = that.suggestions[index];
862
862
863 that.currentValue = that.getValue(suggestion.value);
863 that.currentValue = that.getValue(suggestion.value);
864 var prevElem = {'value': that.el.val(),
864 var prevElem = {'value': that.el.val(),
865 'caret': that.element.selectionStart}
865 'caret': that.element.selectionStart}
866
866
867 if (that.currentValue !== that.el.val()) {
867 if (that.currentValue !== that.el.val()) {
868 that.el.val(that.currentValue);
868 that.el.val(that.currentValue);
869 }
869 }
870
870
871 that.signalHint(null);
871 that.signalHint(null);
872 that.suggestions = [];
872 that.suggestions = [];
873 that.selection = suggestion;
873 that.selection = suggestion;
874
874
875 if ($.isFunction(onSelectCallback)) {
875 if ($.isFunction(onSelectCallback)) {
876 onSelectCallback.call(this, that.element, suggestion, prevElem);
876 onSelectCallback.call(this, that.element, suggestion, prevElem);
877 }
877 }
878 },
878 },
879
879
880 getValue: function (value) {
880 getValue: function (value) {
881 var that = this,
881 var that = this,
882 delimiter = that.options.delimiter,
882 delimiter = that.options.delimiter,
883 currentValue,
883 currentValue,
884 parts;
884 parts;
885
885
886 if (!delimiter) {
886 if (!delimiter) {
887 return value;
887 return value;
888 }
888 }
889
889
890 currentValue = that.currentValue;
890 currentValue = that.currentValue;
891 parts = currentValue.split(delimiter);
891 parts = currentValue.split(delimiter);
892
892
893 if (parts.length === 1) {
893 if (parts.length === 1) {
894 return value;
894 return value;
895 }
895 }
896
896
897 return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
897 return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
898 },
898 },
899
899
900 dispose: function () {
900 dispose: function () {
901 var that = this;
901 var that = this;
902 that.el.off('.autocomplete').removeData('autocomplete');
902 that.el.off('.autocomplete').removeData('autocomplete');
903 that.disableKillerFn();
903 that.disableKillerFn();
904 $(window).off('resize.autocomplete', that.fixPositionCapture);
904 $(window).off('resize.autocomplete', that.fixPositionCapture);
905 $(that.suggestionsContainer).remove();
905 $(that.suggestionsContainer).remove();
906 }
906 }
907 };
907 };
908
908
909 // Create chainable jQuery plugin:
909 // Create chainable jQuery plugin:
910 $.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) {
910 $.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) {
911 var dataKey = 'autocomplete';
911 var dataKey = 'autocomplete';
912 // If function invoked without argument return
912 // If function invoked without argument return
913 // instance of the first matched element:
913 // instance of the first matched element:
914 if (arguments.length === 0) {
914 if (arguments.length === 0) {
915 return this.first().data(dataKey);
915 return this.first().data(dataKey);
916 }
916 }
917
917
918 return this.each(function () {
918 return this.each(function () {
919 var inputElement = $(this),
919 var inputElement = $(this),
920 instance = inputElement.data(dataKey);
920 instance = inputElement.data(dataKey);
921
921
922 if (typeof options === 'string') {
922 if (typeof options === 'string') {
923 if (instance && typeof instance[options] === 'function') {
923 if (instance && typeof instance[options] === 'function') {
924 instance[options](args);
924 instance[options](args);
925 }
925 }
926 } else {
926 } else {
927 // If instance already exists, destroy it:
927 // If instance already exists, destroy it:
928 if (instance && instance.dispose) {
928 if (instance && instance.dispose) {
929 instance.dispose();
929 instance.dispose();
930 }
930 }
931 instance = new Autocomplete(this, options);
931 instance = new Autocomplete(this, options);
932 inputElement.data(dataKey, instance);
932 inputElement.data(dataKey, instance);
933 }
933 }
934 });
934 });
935 };
935 };
936 }));
936 }));
@@ -1,205 +1,190 b''
1 // define module
1 // define module
2 var AgeModule = (function () {
2 var AgeModule = (function () {
3 return {
3 return {
4 age: function(prevdate, now, show_short_version, show_suffix, short_format) {
4 age: function(prevdate, now, show_short_version, show_suffix, short_format) {
5
5
6 var prevdate = moment(prevdate);
6 var prevdate = moment(prevdate);
7 var now = now || moment().utc();
7 var now = now || moment().utc();
8
8
9 var show_short_version = show_short_version || false;
9 var show_short_version = show_short_version || false;
10 var show_suffix = show_suffix || true;
10 var show_suffix = show_suffix || true;
11 var short_format = short_format || false;
11 var short_format = short_format || false;
12
12
13 // alias for backward compat
14 var _ = function(s) {
15 if (_TM.hasOwnProperty(s)) {
16 return _TM[s];
17 }
18 return s
19 };
20
21 var ungettext = function (singular, plural, n) {
22 if (n === 1){
23 return _(singular)
24 }
25 return _(plural)
26 };
27
28 var _get_relative_delta = function(now, prevdate) {
13 var _get_relative_delta = function(now, prevdate) {
29
14
30 var duration = moment.duration(moment(now).diff(prevdate));
15 var duration = moment.duration(moment(now).diff(prevdate));
31 return {
16 return {
32 'year': duration.years(),
17 'year': duration.years(),
33 'month': duration.months(),
18 'month': duration.months(),
34 'day': duration.days(),
19 'day': duration.days(),
35 'hour': duration.hours(),
20 'hour': duration.hours(),
36 'minute': duration.minutes(),
21 'minute': duration.minutes(),
37 'second': duration.seconds()
22 'second': duration.seconds()
38 };
23 };
39
24
40 };
25 };
41
26
42 var _is_leap_year = function(year){
27 var _is_leap_year = function(year){
43 return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
28 return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
44 };
29 };
45
30
46 var get_month = function(prevdate) {
31 var get_month = function(prevdate) {
47 return prevdate.getMonth()
32 return prevdate.getMonth()
48 };
33 };
49
34
50 var get_year = function(prevdate) {
35 var get_year = function(prevdate) {
51 return prevdate.getYear()
36 return prevdate.getYear()
52 };
37 };
53
38
54 var order = ['year', 'month', 'day', 'hour', 'minute', 'second'];
39 var order = ['year', 'month', 'day', 'hour', 'minute', 'second'];
55 var deltas = {};
40 var deltas = {};
56 var future = false;
41 var future = false;
57
42
58 if (prevdate > now) {
43 if (prevdate > now) {
59 var now_old = now;
44 var now_old = now;
60 now = prevdate;
45 now = prevdate;
61 prevdate = now_old;
46 prevdate = now_old;
62 future = true;
47 future = true;
63 }
48 }
64 if (future) {
49 if (future) {
65 // ? remove microseconds, we don't have it in JS
50 // ? remove microseconds, we don't have it in JS
66 }
51 }
67
52
68 // Get date parts deltas
53 // Get date parts deltas
69 for (part in order) {
54 for (part in order) {
70 var part = order[part];
55 var part = order[part];
71 var rel_delta = _get_relative_delta(now, prevdate);
56 var rel_delta = _get_relative_delta(now, prevdate);
72 deltas[part] = rel_delta[part]
57 deltas[part] = rel_delta[part]
73 }
58 }
74
59
75 //# Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
60 //# Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
76 //# not 1 hour, -59 minutes and -59 seconds)
61 //# not 1 hour, -59 minutes and -59 seconds)
77 var offsets = [[5, 60], [4, 60], [3, 24]];
62 var offsets = [[5, 60], [4, 60], [3, 24]];
78 for (element in offsets) { //# seconds, minutes, hours
63 for (element in offsets) { //# seconds, minutes, hours
79 var element = offsets[element];
64 var element = offsets[element];
80 var num = element[0];
65 var num = element[0];
81 var length = element[1];
66 var length = element[1];
82
67
83 var part = order[num];
68 var part = order[num];
84 var carry_part = order[num - 1];
69 var carry_part = order[num - 1];
85
70
86 if (deltas[part] < 0){
71 if (deltas[part] < 0){
87 deltas[part] += length;
72 deltas[part] += length;
88 deltas[carry_part] -= 1
73 deltas[carry_part] -= 1
89 }
74 }
90
75
91 }
76 }
92
77
93 // # Same thing for days except that the increment depends on the (variable)
78 // # Same thing for days except that the increment depends on the (variable)
94 // # number of days in the month
79 // # number of days in the month
95 var month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
80 var month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
96 if (deltas['day'] < 0) {
81 if (deltas['day'] < 0) {
97 if (get_month(prevdate) == 2 && _is_leap_year(get_year(prevdate))) {
82 if (get_month(prevdate) == 2 && _is_leap_year(get_year(prevdate))) {
98 deltas['day'] += 29;
83 deltas['day'] += 29;
99 } else {
84 } else {
100 deltas['day'] += month_lengths[get_month(prevdate) - 1];
85 deltas['day'] += month_lengths[get_month(prevdate) - 1];
101 }
86 }
102
87
103 deltas['month'] -= 1
88 deltas['month'] -= 1
104 }
89 }
105
90
106 if (deltas['month'] < 0) {
91 if (deltas['month'] < 0) {
107 deltas['month'] += 12;
92 deltas['month'] += 12;
108 deltas['year'] -= 1;
93 deltas['year'] -= 1;
109 }
94 }
110
95
111 //# Format the result
96 //# Format the result
112 if (short_format) {
97 if (short_format) {
113 var fmt_funcs = {
98 var fmt_funcs = {
114 'year': function(d) {return '{0}y'.format(d)},
99 'year': function(d) {return '{0}y'.format(d)},
115 'month': function(d) {return '{0}m'.format(d)},
100 'month': function(d) {return '{0}m'.format(d)},
116 'day': function(d) {return '{0}d'.format(d)},
101 'day': function(d) {return '{0}d'.format(d)},
117 'hour': function(d) {return '{0}h'.format(d)},
102 'hour': function(d) {return '{0}h'.format(d)},
118 'minute': function(d) {return '{0}min'.format(d)},
103 'minute': function(d) {return '{0}min'.format(d)},
119 'second': function(d) {return '{0}sec'.format(d)}
104 'second': function(d) {return '{0}sec'.format(d)}
120 }
105 }
121
106
122 } else {
107 } else {
123 var fmt_funcs = {
108 var fmt_funcs = {
124 'year': function(d) {return ungettext('{0} year', '{0} years', d).format(d)},
109 'year': function(d) {return _ngettext('{0} year', '{0} years', d).format(d)},
125 'month': function(d) {return ungettext('{0} month', '{0} months', d).format(d)},
110 'month': function(d) {return _ngettext('{0} month', '{0} months', d).format(d)},
126 'day': function(d) {return ungettext('{0} day', '{0} days', d).format(d)},
111 'day': function(d) {return _ngettext('{0} day', '{0} days', d).format(d)},
127 'hour': function(d) {return ungettext('{0} hour', '{0} hours', d).format(d)},
112 'hour': function(d) {return _ngettext('{0} hour', '{0} hours', d).format(d)},
128 'minute': function(d) {return ungettext('{0} min', '{0} min', d).format(d)},
113 'minute': function(d) {return _ngettext('{0} min', '{0} min', d).format(d)},
129 'second': function(d) {return ungettext('{0} sec', '{0} sec', d).format(d)}
114 'second': function(d) {return _ngettext('{0} sec', '{0} sec', d).format(d)}
130 }
115 }
131
116
132 }
117 }
133 var i = 0;
118 var i = 0;
134 for (part in order){
119 for (part in order){
135 var part = order[part];
120 var part = order[part];
136 var value = deltas[part];
121 var value = deltas[part];
137 if (value !== 0) {
122 if (value !== 0) {
138
123
139 if (i < 5) {
124 if (i < 5) {
140 var sub_part = order[i + 1];
125 var sub_part = order[i + 1];
141 var sub_value = deltas[sub_part]
126 var sub_value = deltas[sub_part]
142 } else {
127 } else {
143 var sub_value = 0
128 var sub_value = 0
144 }
129 }
145 if (sub_value == 0 || show_short_version) {
130 if (sub_value == 0 || show_short_version) {
146 var _val = fmt_funcs[part](value);
131 var _val = fmt_funcs[part](value);
147 if (future) {
132 if (future) {
148 if (show_suffix) {
133 if (show_suffix) {
149 return _('in {0}').format(_val)
134 return _gettext('in {0}').format(_val)
150 } else {
135 } else {
151 return _val
136 return _val
152 }
137 }
153
138
154 }
139 }
155 else {
140 else {
156 if (show_suffix) {
141 if (show_suffix) {
157 return _('{0} ago').format(_val)
142 return _gettext('{0} ago').format(_val)
158 } else {
143 } else {
159 return _val
144 return _val
160 }
145 }
161 }
146 }
162 }
147 }
163
148
164 var val = fmt_funcs[part](value);
149 var val = fmt_funcs[part](value);
165 var val_detail = fmt_funcs[sub_part](sub_value);
150 var val_detail = fmt_funcs[sub_part](sub_value);
166 if (short_format) {
151 if (short_format) {
167 var datetime_tmpl = '{0}, {1}';
152 var datetime_tmpl = '{0}, {1}';
168 if (show_suffix) {
153 if (show_suffix) {
169 datetime_tmpl = _('{0}, {1} ago');
154 datetime_tmpl = _gettext('{0}, {1} ago');
170 if (future) {
155 if (future) {
171 datetime_tmpl = _('in {0}, {1}');
156 datetime_tmpl = _gettext('in {0}, {1}');
172 }
157 }
173 }
158 }
174 } else {
159 } else {
175 var datetime_tmpl = _('{0} and {1}');
160 var datetime_tmpl = _gettext('{0} and {1}');
176 if (show_suffix) {
161 if (show_suffix) {
177 datetime_tmpl = _('{0} and {1} ago');
162 datetime_tmpl = _gettext('{0} and {1} ago');
178 if (future) {
163 if (future) {
179 datetime_tmpl = _('in {0} and {1}')
164 datetime_tmpl = _gettext('in {0} and {1}')
180 }
165 }
181 }
166 }
182 }
167 }
183
168
184 return datetime_tmpl.format(val, val_detail)
169 return datetime_tmpl.format(val, val_detail)
185 }
170 }
186 i += 1;
171 i += 1;
187 }
172 }
188
173
189 return _('just now')
174 return _gettext('just now')
190
175
191 },
176 },
192 createTimeComponent: function(dateTime, text) {
177 createTimeComponent: function(dateTime, text) {
193 return '<time class="timeago tooltip" title="{1}" datetime="{0}+0000">{1}</time>'.format(dateTime, text);
178 return '<time class="timeago tooltip" title="{1}" datetime="{0}+0000">{1}</time>'.format(dateTime, text);
194 }
179 }
195 }
180 }
196 })();
181 })();
197
182
198
183
199 jQuery.timeago.settings.localeTitle = false;
184 jQuery.timeago.settings.localeTitle = false;
200
185
201 // auto refresh the components every Ns
186 // auto refresh the components every Ns
202 jQuery.timeago.settings.refreshMillis = templateContext.timeago.refresh_time;
187 jQuery.timeago.settings.refreshMillis = templateContext.timeago.refresh_time;
203
188
204 // Display original dates older than N days
189 // Display original dates older than N days
205 jQuery.timeago.settings.cutoff = templateContext.timeago.cutoff_limit;
190 jQuery.timeago.settings.cutoff = templateContext.timeago.cutoff_limit;
@@ -1,422 +1,413 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
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 Affero General Public License
12 // # You should have received a copy of the GNU Affero 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 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 RhodeCode JS Files
20 RhodeCode JS Files
21 **/
21 **/
22
22
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
24 console = { log: function() {} }
24 console = { log: function() {} }
25 }
25 }
26
26
27
28 // alias for backward compat
29 var _tm = function(s) {
30 if (_TM.hasOwnProperty(s)) {
31 return _TM[s];
32 }
33 return s
34 };
35
36 // TODO: move the following function to submodules
27 // TODO: move the following function to submodules
37
28
38 /**
29 /**
39 * show more
30 * show more
40 */
31 */
41 var show_more_event = function(){
32 var show_more_event = function(){
42 $('table .show_more').click(function(e) {
33 $('table .show_more').click(function(e) {
43 var cid = e.target.id.substring(1);
34 var cid = e.target.id.substring(1);
44 var button = $(this);
35 var button = $(this);
45 if (button.hasClass('open')) {
36 if (button.hasClass('open')) {
46 $('#'+cid).hide();
37 $('#'+cid).hide();
47 button.removeClass('open');
38 button.removeClass('open');
48 } else {
39 } else {
49 $('#'+cid).show();
40 $('#'+cid).show();
50 button.addClass('open one');
41 button.addClass('open one');
51 }
42 }
52 });
43 });
53 };
44 };
54
45
55 var compare_radio_buttons = function(repo_name, compare_ref_type){
46 var compare_radio_buttons = function(repo_name, compare_ref_type){
56 $('#compare_action').on('click', function(e){
47 $('#compare_action').on('click', function(e){
57 e.preventDefault();
48 e.preventDefault();
58
49
59 var source = $('input[name=compare_source]:checked').val();
50 var source = $('input[name=compare_source]:checked').val();
60 var target = $('input[name=compare_target]:checked').val();
51 var target = $('input[name=compare_target]:checked').val();
61 if(source && target){
52 if(source && target){
62 var url_data = {
53 var url_data = {
63 repo_name: repo_name,
54 repo_name: repo_name,
64 source_ref: source,
55 source_ref: source,
65 source_ref_type: compare_ref_type,
56 source_ref_type: compare_ref_type,
66 target_ref: target,
57 target_ref: target,
67 target_ref_type: compare_ref_type,
58 target_ref_type: compare_ref_type,
68 merge: 1
59 merge: 1
69 };
60 };
70 window.location = pyroutes.url('compare_url', url_data);
61 window.location = pyroutes.url('compare_url', url_data);
71 }
62 }
72 });
63 });
73 $('.compare-radio-button').on('click', function(e){
64 $('.compare-radio-button').on('click', function(e){
74 var source = $('input[name=compare_source]:checked').val();
65 var source = $('input[name=compare_source]:checked').val();
75 var target = $('input[name=compare_target]:checked').val();
66 var target = $('input[name=compare_target]:checked').val();
76 if(source && target){
67 if(source && target){
77 $('#compare_action').removeAttr("disabled");
68 $('#compare_action').removeAttr("disabled");
78 $('#compare_action').removeClass("disabled");
69 $('#compare_action').removeClass("disabled");
79 }
70 }
80 })
71 })
81 };
72 };
82
73
83 var showRepoSize = function(target, repo_name, commit_id, callback) {
74 var showRepoSize = function(target, repo_name, commit_id, callback) {
84 var container = $('#' + target);
75 var container = $('#' + target);
85 var url = pyroutes.url('repo_stats',
76 var url = pyroutes.url('repo_stats',
86 {"repo_name": repo_name, "commit_id": commit_id});
77 {"repo_name": repo_name, "commit_id": commit_id});
87
78
88 if (!container.hasClass('loaded')) {
79 if (!container.hasClass('loaded')) {
89 $.ajax({url: url})
80 $.ajax({url: url})
90 .complete(function (data) {
81 .complete(function (data) {
91 var responseJSON = data.responseJSON;
82 var responseJSON = data.responseJSON;
92 container.addClass('loaded');
83 container.addClass('loaded');
93 container.html(responseJSON.size);
84 container.html(responseJSON.size);
94 callback(responseJSON.code_stats)
85 callback(responseJSON.code_stats)
95 })
86 })
96 .fail(function (data) {
87 .fail(function (data) {
97 console.log('failed to load repo stats');
88 console.log('failed to load repo stats');
98 });
89 });
99 }
90 }
100
91
101 };
92 };
102
93
103 var showRepoStats = function(target, data){
94 var showRepoStats = function(target, data){
104 var container = $('#' + target);
95 var container = $('#' + target);
105
96
106 if (container.hasClass('loaded')) {
97 if (container.hasClass('loaded')) {
107 return
98 return
108 }
99 }
109
100
110 var total = 0;
101 var total = 0;
111 var no_data = true;
102 var no_data = true;
112 var tbl = document.createElement('table');
103 var tbl = document.createElement('table');
113 tbl.setAttribute('class', 'trending_language_tbl');
104 tbl.setAttribute('class', 'trending_language_tbl');
114
105
115 $.each(data, function(key, val){
106 $.each(data, function(key, val){
116 total += val.count;
107 total += val.count;
117 });
108 });
118
109
119 var sortedStats = [];
110 var sortedStats = [];
120 for (var obj in data){
111 for (var obj in data){
121 sortedStats.push([obj, data[obj]])
112 sortedStats.push([obj, data[obj]])
122 }
113 }
123 var sortedData = sortedStats.sort(function (a, b) {
114 var sortedData = sortedStats.sort(function (a, b) {
124 return b[1].count - a[1].count
115 return b[1].count - a[1].count
125 });
116 });
126 var cnt = 0;
117 var cnt = 0;
127 $.each(sortedData, function(idx, val){
118 $.each(sortedData, function(idx, val){
128 cnt += 1;
119 cnt += 1;
129 no_data = false;
120 no_data = false;
130
121
131 var hide = cnt > 2;
122 var hide = cnt > 2;
132 var tr = document.createElement('tr');
123 var tr = document.createElement('tr');
133 if (hide) {
124 if (hide) {
134 tr.setAttribute('style', 'display:none');
125 tr.setAttribute('style', 'display:none');
135 tr.setAttribute('class', 'stats_hidden');
126 tr.setAttribute('class', 'stats_hidden');
136 }
127 }
137
128
138 var key = val[0];
129 var key = val[0];
139 var obj = {"desc": val[1].desc, "count": val[1].count};
130 var obj = {"desc": val[1].desc, "count": val[1].count};
140
131
141 var percentage = Math.round((obj.count / total * 100), 2);
132 var percentage = Math.round((obj.count / total * 100), 2);
142
133
143 var td1 = document.createElement('td');
134 var td1 = document.createElement('td');
144 td1.width = 300;
135 td1.width = 300;
145 var trending_language_label = document.createElement('div');
136 var trending_language_label = document.createElement('div');
146 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
137 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
147 td1.appendChild(trending_language_label);
138 td1.appendChild(trending_language_label);
148
139
149 var td2 = document.createElement('td');
140 var td2 = document.createElement('td');
150 var trending_language = document.createElement('div');
141 var trending_language = document.createElement('div');
151 var nr_files = obj.count +" "+ (obj.count === 1 ? _tm('file'): _tm('files'));
142 var nr_files = obj.count +" "+ _ngettext('file', 'files', obj.count);
152
143
153 trending_language.title = key + " " + nr_files;
144 trending_language.title = key + " " + nr_files;
154
145
155 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
146 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
156 + "</span><b>" + percentage + "% " + nr_files + "</b>";
147 + "</span><b>" + percentage + "% " + nr_files + "</b>";
157
148
158 trending_language.setAttribute("class", 'trending_language');
149 trending_language.setAttribute("class", 'trending_language');
159 $('b', trending_language)[0].style.width = percentage + "%";
150 $('b', trending_language)[0].style.width = percentage + "%";
160 td2.appendChild(trending_language);
151 td2.appendChild(trending_language);
161
152
162 tr.appendChild(td1);
153 tr.appendChild(td1);
163 tr.appendChild(td2);
154 tr.appendChild(td2);
164 tbl.appendChild(tr);
155 tbl.appendChild(tr);
165 if (cnt == 3) {
156 if (cnt == 3) {
166 var show_more = document.createElement('tr');
157 var show_more = document.createElement('tr');
167 var td = document.createElement('td');
158 var td = document.createElement('td');
168 lnk = document.createElement('a');
159 lnk = document.createElement('a');
169
160
170 lnk.href = '#';
161 lnk.href = '#';
171 lnk.innerHTML = _tm('Show more');
162 lnk.innerHTML = _ngettext('Show more');
172 lnk.id = 'code_stats_show_more';
163 lnk.id = 'code_stats_show_more';
173 td.appendChild(lnk);
164 td.appendChild(lnk);
174
165
175 show_more.appendChild(td);
166 show_more.appendChild(td);
176 show_more.appendChild(document.createElement('td'));
167 show_more.appendChild(document.createElement('td'));
177 tbl.appendChild(show_more);
168 tbl.appendChild(show_more);
178 }
169 }
179 });
170 });
180
171
181 $(container).html(tbl);
172 $(container).html(tbl);
182 $(container).addClass('loaded');
173 $(container).addClass('loaded');
183
174
184 $('#code_stats_show_more').on('click', function (e) {
175 $('#code_stats_show_more').on('click', function (e) {
185 e.preventDefault();
176 e.preventDefault();
186 $('.stats_hidden').each(function (idx) {
177 $('.stats_hidden').each(function (idx) {
187 $(this).css("display", "");
178 $(this).css("display", "");
188 });
179 });
189 $('#code_stats_show_more').hide();
180 $('#code_stats_show_more').hide();
190 });
181 });
191
182
192 };
183 };
193
184
194
185
195 // Toggle Collapsable Content
186 // Toggle Collapsable Content
196 function collapsableContent() {
187 function collapsableContent() {
197
188
198 $('.collapsable-content').not('.no-hide').hide();
189 $('.collapsable-content').not('.no-hide').hide();
199
190
200 $('.btn-collapse').unbind(); //in case we've been here before
191 $('.btn-collapse').unbind(); //in case we've been here before
201 $('.btn-collapse').click(function() {
192 $('.btn-collapse').click(function() {
202 var button = $(this);
193 var button = $(this);
203 var togglename = $(this).data("toggle");
194 var togglename = $(this).data("toggle");
204 $('.collapsable-content[data-toggle='+togglename+']').toggle();
195 $('.collapsable-content[data-toggle='+togglename+']').toggle();
205 if ($(this).html()=="Show Less")
196 if ($(this).html()=="Show Less")
206 $(this).html("Show More");
197 $(this).html("Show More");
207 else
198 else
208 $(this).html("Show Less");
199 $(this).html("Show Less");
209 });
200 });
210 };
201 };
211
202
212 var timeagoActivate = function() {
203 var timeagoActivate = function() {
213 $("time.timeago").timeago();
204 $("time.timeago").timeago();
214 };
205 };
215
206
216 // Formatting values in a Select2 dropdown of commit references
207 // Formatting values in a Select2 dropdown of commit references
217 var formatSelect2SelectionRefs = function(commit_ref){
208 var formatSelect2SelectionRefs = function(commit_ref){
218 var tmpl = '';
209 var tmpl = '';
219 if (!commit_ref.text || commit_ref.type === 'sha'){
210 if (!commit_ref.text || commit_ref.type === 'sha'){
220 return commit_ref.text;
211 return commit_ref.text;
221 }
212 }
222 if (commit_ref.type === 'branch'){
213 if (commit_ref.type === 'branch'){
223 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
214 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
224 } else if (commit_ref.type === 'tag'){
215 } else if (commit_ref.type === 'tag'){
225 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
216 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
226 } else if (commit_ref.type === 'book'){
217 } else if (commit_ref.type === 'book'){
227 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
218 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
228 }
219 }
229 return tmpl.concat(commit_ref.text);
220 return tmpl.concat(commit_ref.text);
230 };
221 };
231
222
232 // takes a given html element and scrolls it down offset pixels
223 // takes a given html element and scrolls it down offset pixels
233 function offsetScroll(element, offset){
224 function offsetScroll(element, offset){
234 setTimeout(function(){
225 setTimeout(function(){
235 console.log(element);
226 console.log(element);
236 var location = element.offset().top;
227 var location = element.offset().top;
237 // some browsers use body, some use html
228 // some browsers use body, some use html
238 $('html, body').animate({ scrollTop: (location - offset) });
229 $('html, body').animate({ scrollTop: (location - offset) });
239 }, 100);
230 }, 100);
240 }
231 }
241
232
242 /**
233 /**
243 * global hooks after DOM is loaded
234 * global hooks after DOM is loaded
244 */
235 */
245 $(document).ready(function() {
236 $(document).ready(function() {
246 firefoxAnchorFix();
237 firefoxAnchorFix();
247
238
248 $('.navigation a.menulink').on('click', function(e){
239 $('.navigation a.menulink').on('click', function(e){
249 var menuitem = $(this).parent('li');
240 var menuitem = $(this).parent('li');
250 if (menuitem.hasClass('open')) {
241 if (menuitem.hasClass('open')) {
251 menuitem.removeClass('open');
242 menuitem.removeClass('open');
252 } else {
243 } else {
253 menuitem.addClass('open');
244 menuitem.addClass('open');
254 $(document).on('click', function(event) {
245 $(document).on('click', function(event) {
255 if (!$(event.target).closest(menuitem).length) {
246 if (!$(event.target).closest(menuitem).length) {
256 menuitem.removeClass('open');
247 menuitem.removeClass('open');
257 }
248 }
258 });
249 });
259 }
250 }
260 });
251 });
261 // Add tooltips
252 // Add tooltips
262 $('tr.line .lineno a').attr("title","Click to select line").addClass('tooltip');
253 $('tr.line .lineno a').attr("title","Click to select line").addClass('tooltip');
263 $('tr.line .add-comment-line a').attr("title","Click to comment").addClass('tooltip');
254 $('tr.line .add-comment-line a').attr("title","Click to comment").addClass('tooltip');
264
255
265 // Set colors and styles
256 // Set colors and styles
266 $('tr.line .lineno a').hover(
257 $('tr.line .lineno a').hover(
267 function(){
258 function(){
268 $(this).parents('tr.line').addClass('hover');
259 $(this).parents('tr.line').addClass('hover');
269 }, function(){
260 }, function(){
270 $(this).parents('tr.line').removeClass('hover');
261 $(this).parents('tr.line').removeClass('hover');
271 }
262 }
272 );
263 );
273
264
274 $('tr.line .lineno a').click(
265 $('tr.line .lineno a').click(
275 function(){
266 function(){
276 if ($(this).text() != ""){
267 if ($(this).text() != ""){
277 $('tr.line').removeClass('selected');
268 $('tr.line').removeClass('selected');
278 $(this).parents("tr.line").addClass('selected');
269 $(this).parents("tr.line").addClass('selected');
279
270
280 // Replace URL without jumping to it if browser supports.
271 // Replace URL without jumping to it if browser supports.
281 // Default otherwise
272 // Default otherwise
282 if (history.pushState) {
273 if (history.pushState) {
283 var new_location = location.href
274 var new_location = location.href
284 if (location.hash){
275 if (location.hash){
285 new_location = new_location.replace(location.hash, "");
276 new_location = new_location.replace(location.hash, "");
286 }
277 }
287
278
288 // Make new anchor url
279 // Make new anchor url
289 var new_location = new_location+$(this).attr('href');
280 var new_location = new_location+$(this).attr('href');
290 history.pushState(true, document.title, new_location);
281 history.pushState(true, document.title, new_location);
291
282
292 return false;
283 return false;
293 }
284 }
294 }
285 }
295 }
286 }
296 );
287 );
297
288
298 $('tr.line .add-comment-line a').hover(
289 $('tr.line .add-comment-line a').hover(
299 function(){
290 function(){
300 $(this).parents('tr.line').addClass('commenting');
291 $(this).parents('tr.line').addClass('commenting');
301 }, function(){
292 }, function(){
302 $(this).parents('tr.line').removeClass('commenting');
293 $(this).parents('tr.line').removeClass('commenting');
303 }
294 }
304 );
295 );
305
296
306 $('tr.line .add-comment-line a').on('click', function(e){
297 $('tr.line .add-comment-line a').on('click', function(e){
307 var tr = $(e.currentTarget).parents('tr.line')[0];
298 var tr = $(e.currentTarget).parents('tr.line')[0];
308 injectInlineForm(tr);
299 injectInlineForm(tr);
309 return false;
300 return false;
310 });
301 });
311
302
312
303
313 $('.collapse_file').on('click', function(e) {
304 $('.collapse_file').on('click', function(e) {
314 e.stopPropagation();
305 e.stopPropagation();
315 if ($(e.target).is('a')) { return; }
306 if ($(e.target).is('a')) { return; }
316 var node = $(e.delegateTarget).first();
307 var node = $(e.delegateTarget).first();
317 var icon = $($(node.children().first()).children().first());
308 var icon = $($(node.children().first()).children().first());
318 var id = node.attr('fid');
309 var id = node.attr('fid');
319 var target = $('#'+id);
310 var target = $('#'+id);
320 var tr = $('#tr_'+id);
311 var tr = $('#tr_'+id);
321 var diff = $('#diff_'+id);
312 var diff = $('#diff_'+id);
322 if(node.hasClass('expand_file')){
313 if(node.hasClass('expand_file')){
323 node.removeClass('expand_file');
314 node.removeClass('expand_file');
324 icon.removeClass('expand_file_icon');
315 icon.removeClass('expand_file_icon');
325 node.addClass('collapse_file');
316 node.addClass('collapse_file');
326 icon.addClass('collapse_file_icon');
317 icon.addClass('collapse_file_icon');
327 diff.show();
318 diff.show();
328 tr.show();
319 tr.show();
329 target.show();
320 target.show();
330 } else {
321 } else {
331 node.removeClass('collapse_file');
322 node.removeClass('collapse_file');
332 icon.removeClass('collapse_file_icon');
323 icon.removeClass('collapse_file_icon');
333 node.addClass('expand_file');
324 node.addClass('expand_file');
334 icon.addClass('expand_file_icon');
325 icon.addClass('expand_file_icon');
335 diff.hide();
326 diff.hide();
336 tr.hide();
327 tr.hide();
337 target.hide();
328 target.hide();
338 }
329 }
339 });
330 });
340
331
341 $('#expand_all_files').click(function() {
332 $('#expand_all_files').click(function() {
342 $('.expand_file').each(function() {
333 $('.expand_file').each(function() {
343 var node = $(this);
334 var node = $(this);
344 var icon = $($(node.children().first()).children().first());
335 var icon = $($(node.children().first()).children().first());
345 var id = $(this).attr('fid');
336 var id = $(this).attr('fid');
346 var target = $('#'+id);
337 var target = $('#'+id);
347 var tr = $('#tr_'+id);
338 var tr = $('#tr_'+id);
348 var diff = $('#diff_'+id);
339 var diff = $('#diff_'+id);
349 node.removeClass('expand_file');
340 node.removeClass('expand_file');
350 icon.removeClass('expand_file_icon');
341 icon.removeClass('expand_file_icon');
351 node.addClass('collapse_file');
342 node.addClass('collapse_file');
352 icon.addClass('collapse_file_icon');
343 icon.addClass('collapse_file_icon');
353 diff.show();
344 diff.show();
354 tr.show();
345 tr.show();
355 target.show();
346 target.show();
356 });
347 });
357 });
348 });
358
349
359 $('#collapse_all_files').click(function() {
350 $('#collapse_all_files').click(function() {
360 $('.collapse_file').each(function() {
351 $('.collapse_file').each(function() {
361 var node = $(this);
352 var node = $(this);
362 var icon = $($(node.children().first()).children().first());
353 var icon = $($(node.children().first()).children().first());
363 var id = $(this).attr('fid');
354 var id = $(this).attr('fid');
364 var target = $('#'+id);
355 var target = $('#'+id);
365 var tr = $('#tr_'+id);
356 var tr = $('#tr_'+id);
366 var diff = $('#diff_'+id);
357 var diff = $('#diff_'+id);
367 node.removeClass('collapse_file');
358 node.removeClass('collapse_file');
368 icon.removeClass('collapse_file_icon');
359 icon.removeClass('collapse_file_icon');
369 node.addClass('expand_file');
360 node.addClass('expand_file');
370 icon.addClass('expand_file_icon');
361 icon.addClass('expand_file_icon');
371 diff.hide();
362 diff.hide();
372 tr.hide();
363 tr.hide();
373 target.hide();
364 target.hide();
374 });
365 });
375 });
366 });
376
367
377 // Mouse over behavior for comments and line selection
368 // Mouse over behavior for comments and line selection
378
369
379 // Select the line that comes from the url anchor
370 // Select the line that comes from the url anchor
380 // At the time of development, Chrome didn't seem to support jquery's :target
371 // At the time of development, Chrome didn't seem to support jquery's :target
381 // element, so I had to scroll manually
372 // element, so I had to scroll manually
382 if (location.hash) {
373 if (location.hash) {
383 var splitIx = location.hash.indexOf('/?/');
374 var splitIx = location.hash.indexOf('/?/');
384 if (splitIx !== -1){
375 if (splitIx !== -1){
385 var loc = location.hash.slice(0, splitIx);
376 var loc = location.hash.slice(0, splitIx);
386 var remainder = location.hash.slice(splitIx + 2);
377 var remainder = location.hash.slice(splitIx + 2);
387 }
378 }
388 else{
379 else{
389 var loc = location.hash;
380 var loc = location.hash;
390 var remainder = null;
381 var remainder = null;
391 }
382 }
392 if (loc.length > 1){
383 if (loc.length > 1){
393 var lineno = $(loc+'.lineno');
384 var lineno = $(loc+'.lineno');
394 if (lineno.length > 0){
385 if (lineno.length > 0){
395 var tr = lineno.parents('tr.line');
386 var tr = lineno.parents('tr.line');
396 tr.addClass('selected');
387 tr.addClass('selected');
397
388
398 // once we scrolled into our line, trigger chat app
389 // once we scrolled into our line, trigger chat app
399 if (remainder){
390 if (remainder){
400 tr.find('.add-comment-line a').trigger( "click" );
391 tr.find('.add-comment-line a').trigger( "click" );
401 setTimeout(function(){
392 setTimeout(function(){
402 var nextNode = $(tr).next();
393 var nextNode = $(tr).next();
403 if(nextNode.hasClass('inline-comments')){
394 if(nextNode.hasClass('inline-comments')){
404 nextNode.next().find('.switch-to-chat').trigger( "click" );
395 nextNode.next().find('.switch-to-chat').trigger( "click" );
405 }
396 }
406 else{
397 else{
407 nextNode.find('.switch-to-chat').trigger( "click" );
398 nextNode.find('.switch-to-chat').trigger( "click" );
408 }
399 }
409 // trigger scroll into, later so all elements are already loaded
400 // trigger scroll into, later so all elements are already loaded
410 tr[0].scrollIntoView();
401 tr[0].scrollIntoView();
411 }, 250);
402 }, 250);
412
403
413 }
404 }
414 else{
405 else{
415 tr[0].scrollIntoView();
406 tr[0].scrollIntoView();
416 }
407 }
417 }
408 }
418 }
409 }
419 };
410 };
420
411
421 collapsableContent();
412 collapsableContent();
422 });
413 });
@@ -1,530 +1,530 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
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 Affero General Public License
12 // # You should have received a copy of the GNU Affero 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 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 * Code Mirror
20 * Code Mirror
21 */
21 */
22 // global code-mirror logger;, to enable run
22 // global code-mirror logger;, to enable run
23 // Logger.get('CodeMirror').setLevel(Logger.DEBUG)
23 // Logger.get('CodeMirror').setLevel(Logger.DEBUG)
24
24
25 cmLog = Logger.get('CodeMirror');
25 cmLog = Logger.get('CodeMirror');
26 cmLog.setLevel(Logger.OFF);
26 cmLog.setLevel(Logger.OFF);
27
27
28
28
29 //global cache for inline forms
29 //global cache for inline forms
30 var userHintsCache = {};
30 var userHintsCache = {};
31
31
32
32
33 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
33 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
34 var ta = $('#' + textAreadId).get(0);
34 var ta = $('#' + textAreadId).get(0);
35 if (focus === undefined) {
35 if (focus === undefined) {
36 focus = true;
36 focus = true;
37 }
37 }
38
38
39 // default options
39 // default options
40 var codeMirrorOptions = {
40 var codeMirrorOptions = {
41 mode: "null",
41 mode: "null",
42 lineNumbers: true,
42 lineNumbers: true,
43 indentUnit: 4,
43 indentUnit: 4,
44 autofocus: focus
44 autofocus: focus
45 };
45 };
46
46
47 if (options !== undefined) {
47 if (options !== undefined) {
48 // extend with custom options
48 // extend with custom options
49 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
49 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
50 }
50 }
51
51
52 var myCodeMirror = CodeMirror.fromTextArea(ta, codeMirrorOptions);
52 var myCodeMirror = CodeMirror.fromTextArea(ta, codeMirrorOptions);
53
53
54 $('#reset').on('click', function(e) {
54 $('#reset').on('click', function(e) {
55 window.location = resetUrl;
55 window.location = resetUrl;
56 });
56 });
57
57
58 return myCodeMirror;
58 return myCodeMirror;
59 };
59 };
60
60
61 var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
61 var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
62 var initialHeight = 100;
62 var initialHeight = 100;
63
63
64 // global timer, used to cancel async loading
64 // global timer, used to cancel async loading
65 var loadUserHintTimer;
65 var loadUserHintTimer;
66
66
67 if (typeof userHintsCache === "undefined") {
67 if (typeof userHintsCache === "undefined") {
68 userHintsCache = {};
68 userHintsCache = {};
69 cmLog.debug('Init empty cache for mentions');
69 cmLog.debug('Init empty cache for mentions');
70 }
70 }
71 if (!$(textAreaId).get(0)) {
71 if (!$(textAreaId).get(0)) {
72 cmLog.debug('Element for textarea not found', textAreaId);
72 cmLog.debug('Element for textarea not found', textAreaId);
73 return;
73 return;
74 }
74 }
75 var escapeRegExChars = function(value) {
75 var escapeRegExChars = function(value) {
76 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
76 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
77 };
77 };
78 /**
78 /**
79 * Load hints from external source returns an array of objects in a format
79 * Load hints from external source returns an array of objects in a format
80 * that hinting lib requires
80 * that hinting lib requires
81 * @returns {Array}
81 * @returns {Array}
82 */
82 */
83 var loadUserHints = function(query, triggerHints) {
83 var loadUserHints = function(query, triggerHints) {
84 cmLog.debug('Loading mentions users via AJAX');
84 cmLog.debug('Loading mentions users via AJAX');
85 var _users = [];
85 var _users = [];
86 $.ajax({
86 $.ajax({
87 type: 'GET',
87 type: 'GET',
88 data: {query: query},
88 data: {query: query},
89 url: pyroutes.url('user_autocomplete_data'),
89 url: pyroutes.url('user_autocomplete_data'),
90 headers: {'X-PARTIAL-XHR': true},
90 headers: {'X-PARTIAL-XHR': true},
91 async: true
91 async: true
92 })
92 })
93 .done(function(data) {
93 .done(function(data) {
94 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
94 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
95 $.each(data.suggestions, function(i) {
95 $.each(data.suggestions, function(i) {
96 var userObj = data.suggestions[i];
96 var userObj = data.suggestions[i];
97
97
98 if (userObj.username !== "default") {
98 if (userObj.username !== "default") {
99 _users.push({
99 _users.push({
100 text: userObj.username + " ",
100 text: userObj.username + " ",
101 org_text: userObj.username,
101 org_text: userObj.username,
102 displayText: userObj.value_display, // search that field
102 displayText: userObj.value_display, // search that field
103 // internal caches
103 // internal caches
104 _icon_link: userObj.icon_link,
104 _icon_link: userObj.icon_link,
105 _text: userObj.value_display,
105 _text: userObj.value_display,
106
106
107 render: function(elt, data, completion) {
107 render: function(elt, data, completion) {
108 var el = document.createElement('div');
108 var el = document.createElement('div');
109 el.className = "CodeMirror-hint-entry";
109 el.className = "CodeMirror-hint-entry";
110 el.innerHTML = tmpl.format(
110 el.innerHTML = tmpl.format(
111 completion._icon_link, completion._text);
111 completion._icon_link, completion._text);
112 elt.appendChild(el);
112 elt.appendChild(el);
113 }
113 }
114 });
114 });
115 }
115 }
116 });
116 });
117 cmLog.debug('Mention users loaded');
117 cmLog.debug('Mention users loaded');
118 // set to global cache
118 // set to global cache
119 userHintsCache[query] = _users;
119 userHintsCache[query] = _users;
120 triggerHints(userHintsCache[query]);
120 triggerHints(userHintsCache[query]);
121 })
121 })
122 .fail(function(data, textStatus, xhr) {
122 .fail(function(data, textStatus, xhr) {
123 alert("error processing request: " + textStatus);
123 alert("error processing request: " + textStatus);
124 });
124 });
125 };
125 };
126
126
127 /**
127 /**
128 * filters the results based on the current context
128 * filters the results based on the current context
129 * @param users
129 * @param users
130 * @param context
130 * @param context
131 * @returns {Array}
131 * @returns {Array}
132 */
132 */
133 var filterUsers = function(users, context) {
133 var filterUsers = function(users, context) {
134 var MAX_LIMIT = 10;
134 var MAX_LIMIT = 10;
135 var filtered_users = [];
135 var filtered_users = [];
136 var curWord = context.string;
136 var curWord = context.string;
137
137
138 cmLog.debug('Filtering users based on query:', curWord);
138 cmLog.debug('Filtering users based on query:', curWord);
139 $.each(users, function(i) {
139 $.each(users, function(i) {
140 var match = users[i];
140 var match = users[i];
141 var searchText = match.displayText;
141 var searchText = match.displayText;
142
142
143 if (!curWord ||
143 if (!curWord ||
144 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
144 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
145 // reset state
145 // reset state
146 match._text = match.displayText;
146 match._text = match.displayText;
147 if (curWord) {
147 if (curWord) {
148 // do highlighting
148 // do highlighting
149 var pattern = '(' + escapeRegExChars(curWord) + ')';
149 var pattern = '(' + escapeRegExChars(curWord) + ')';
150 match._text = searchText.replace(
150 match._text = searchText.replace(
151 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
151 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
152 }
152 }
153
153
154 filtered_users.push(match);
154 filtered_users.push(match);
155 }
155 }
156 // to not return to many results, use limit of filtered results
156 // to not return to many results, use limit of filtered results
157 if (filtered_users.length > MAX_LIMIT) {
157 if (filtered_users.length > MAX_LIMIT) {
158 return false;
158 return false;
159 }
159 }
160 });
160 });
161
161
162 return filtered_users;
162 return filtered_users;
163 };
163 };
164
164
165 /**
165 /**
166 * Filter action based on typed in text
166 * Filter action based on typed in text
167 * @param actions
167 * @param actions
168 * @param context
168 * @param context
169 * @returns {Array}
169 * @returns {Array}
170 */
170 */
171
171
172 var filterActions = function(actions, context){
172 var filterActions = function(actions, context){
173 var MAX_LIMIT = 10;
173 var MAX_LIMIT = 10;
174 var filtered_actions= [];
174 var filtered_actions= [];
175 var curWord = context.string;
175 var curWord = context.string;
176
176
177 cmLog.debug('Filtering actions based on query:', curWord);
177 cmLog.debug('Filtering actions based on query:', curWord);
178 $.each(actions, function(i) {
178 $.each(actions, function(i) {
179 var match = actions[i];
179 var match = actions[i];
180 var searchText = match.displayText;
180 var searchText = match.displayText;
181
181
182 if (!curWord ||
182 if (!curWord ||
183 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
183 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
184 // reset state
184 // reset state
185 match._text = match.displayText;
185 match._text = match.displayText;
186 if (curWord) {
186 if (curWord) {
187 // do highlighting
187 // do highlighting
188 var pattern = '(' + escapeRegExChars(curWord) + ')';
188 var pattern = '(' + escapeRegExChars(curWord) + ')';
189 match._text = searchText.replace(
189 match._text = searchText.replace(
190 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
190 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
191 }
191 }
192
192
193 filtered_actions.push(match);
193 filtered_actions.push(match);
194 }
194 }
195 // to not return to many results, use limit of filtered results
195 // to not return to many results, use limit of filtered results
196 if (filtered_actions.length > MAX_LIMIT) {
196 if (filtered_actions.length > MAX_LIMIT) {
197 return false;
197 return false;
198 }
198 }
199 });
199 });
200 return filtered_actions;
200 return filtered_actions;
201 };
201 };
202
202
203 var completeAfter = function(cm, pred) {
203 var completeAfter = function(cm, pred) {
204 var options = {
204 var options = {
205 completeSingle: false,
205 completeSingle: false,
206 async: true,
206 async: true,
207 closeOnUnfocus: true
207 closeOnUnfocus: true
208 };
208 };
209 var cur = cm.getCursor();
209 var cur = cm.getCursor();
210 setTimeout(function() {
210 setTimeout(function() {
211 if (!cm.state.completionActive) {
211 if (!cm.state.completionActive) {
212 cmLog.debug('Trigger mentions hinting');
212 cmLog.debug('Trigger mentions hinting');
213 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
213 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
214 }
214 }
215 }, 100);
215 }, 100);
216
216
217 // tell CodeMirror we didn't handle the key
217 // tell CodeMirror we didn't handle the key
218 // trick to trigger on a char but still complete it
218 // trick to trigger on a char but still complete it
219 return CodeMirror.Pass;
219 return CodeMirror.Pass;
220 };
220 };
221
221
222 var submitForm = function(cm, pred) {
222 var submitForm = function(cm, pred) {
223 $(cm.display.input.textarea.form).submit();
223 $(cm.display.input.textarea.form).submit();
224 return CodeMirror.Pass;
224 return CodeMirror.Pass;
225 };
225 };
226
226
227 var completeActions = function(cm, pred) {
227 var completeActions = function(cm, pred) {
228 var cur = cm.getCursor();
228 var cur = cm.getCursor();
229 var options = {
229 var options = {
230 closeOnUnfocus: true
230 closeOnUnfocus: true
231 };
231 };
232 setTimeout(function() {
232 setTimeout(function() {
233 if (!cm.state.completionActive) {
233 if (!cm.state.completionActive) {
234 cmLog.debug('Trigger actions hinting');
234 cmLog.debug('Trigger actions hinting');
235 CodeMirror.showHint(cm, CodeMirror.hint.actions, options);
235 CodeMirror.showHint(cm, CodeMirror.hint.actions, options);
236 }
236 }
237 }, 100);
237 }, 100);
238 };
238 };
239
239
240 var extraKeys = {
240 var extraKeys = {
241 "'@'": completeAfter,
241 "'@'": completeAfter,
242 Tab: function(cm) {
242 Tab: function(cm) {
243 // space indent instead of TABS
243 // space indent instead of TABS
244 var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
244 var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
245 cm.replaceSelection(spaces);
245 cm.replaceSelection(spaces);
246 }
246 }
247 };
247 };
248 // submit form on Meta-Enter
248 // submit form on Meta-Enter
249 if (OSType === "mac") {
249 if (OSType === "mac") {
250 extraKeys["Cmd-Enter"] = submitForm;
250 extraKeys["Cmd-Enter"] = submitForm;
251 }
251 }
252 else {
252 else {
253 extraKeys["Ctrl-Enter"] = submitForm;
253 extraKeys["Ctrl-Enter"] = submitForm;
254 }
254 }
255
255
256 if (triggerActions) {
256 if (triggerActions) {
257 extraKeys["Ctrl-Space"] = completeActions;
257 extraKeys["Ctrl-Space"] = completeActions;
258 }
258 }
259
259
260 var cm = CodeMirror.fromTextArea($(textAreaId).get(0), {
260 var cm = CodeMirror.fromTextArea($(textAreaId).get(0), {
261 lineNumbers: false,
261 lineNumbers: false,
262 indentUnit: 4,
262 indentUnit: 4,
263 viewportMargin: 30,
263 viewportMargin: 30,
264 // this is a trick to trigger some logic behind codemirror placeholder
264 // this is a trick to trigger some logic behind codemirror placeholder
265 // it influences styling and behaviour.
265 // it influences styling and behaviour.
266 placeholder: " ",
266 placeholder: " ",
267 extraKeys: extraKeys,
267 extraKeys: extraKeys,
268 lineWrapping: true
268 lineWrapping: true
269 });
269 });
270
270
271 cm.setSize(null, initialHeight);
271 cm.setSize(null, initialHeight);
272 cm.setOption("mode", DEFAULT_RENDERER);
272 cm.setOption("mode", DEFAULT_RENDERER);
273 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
273 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
274 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
274 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
275 // start listening on changes to make auto-expanded editor
275 // start listening on changes to make auto-expanded editor
276 cm.on("change", function(self) {
276 cm.on("change", function(self) {
277 var height = initialHeight;
277 var height = initialHeight;
278 var lines = self.lineCount();
278 var lines = self.lineCount();
279 if ( lines > 6 && lines < 20) {
279 if ( lines > 6 && lines < 20) {
280 height = "auto";
280 height = "auto";
281 }
281 }
282 else if (lines >= 20){
282 else if (lines >= 20){
283 zheight = 20*15;
283 zheight = 20*15;
284 }
284 }
285 self.setSize(null, height);
285 self.setSize(null, height);
286 });
286 });
287
287
288 var mentionHint = function(editor, callback, options) {
288 var mentionHint = function(editor, callback, options) {
289 var cur = editor.getCursor();
289 var cur = editor.getCursor();
290 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
290 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
291
291
292 // match on @ +1char
292 // match on @ +1char
293 var tokenMatch = new RegExp(
293 var tokenMatch = new RegExp(
294 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
294 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
295
295
296 var tokenStr = '';
296 var tokenStr = '';
297 if (tokenMatch !== null && tokenMatch.length > 0){
297 if (tokenMatch !== null && tokenMatch.length > 0){
298 tokenStr = tokenMatch[0].strip();
298 tokenStr = tokenMatch[0].strip();
299 } else {
299 } else {
300 // skip if we didn't match our token
300 // skip if we didn't match our token
301 return;
301 return;
302 }
302 }
303
303
304 var context = {
304 var context = {
305 start: (cur.ch - tokenStr.length) + 1,
305 start: (cur.ch - tokenStr.length) + 1,
306 end: cur.ch,
306 end: cur.ch,
307 string: tokenStr.slice(1),
307 string: tokenStr.slice(1),
308 type: null
308 type: null
309 };
309 };
310
310
311 // case when we put the @sign in fron of a string,
311 // case when we put the @sign in fron of a string,
312 // eg <@ we put it here>sometext then we need to prepend to text
312 // eg <@ we put it here>sometext then we need to prepend to text
313 if (context.end > cur.ch) {
313 if (context.end > cur.ch) {
314 context.start = context.start + 1; // we add to the @ sign
314 context.start = context.start + 1; // we add to the @ sign
315 context.end = cur.ch; // don't eat front part just append
315 context.end = cur.ch; // don't eat front part just append
316 context.string = context.string.slice(1, cur.ch - context.start);
316 context.string = context.string.slice(1, cur.ch - context.start);
317 }
317 }
318
318
319 cmLog.debug('Mention context', context);
319 cmLog.debug('Mention context', context);
320
320
321 var triggerHints = function(userHints){
321 var triggerHints = function(userHints){
322 return callback({
322 return callback({
323 list: filterUsers(userHints, context),
323 list: filterUsers(userHints, context),
324 from: CodeMirror.Pos(cur.line, context.start),
324 from: CodeMirror.Pos(cur.line, context.start),
325 to: CodeMirror.Pos(cur.line, context.end)
325 to: CodeMirror.Pos(cur.line, context.end)
326 });
326 });
327 };
327 };
328
328
329 var queryBasedHintsCache = undefined;
329 var queryBasedHintsCache = undefined;
330 // if we have something in the cache, try to fetch the query based cache
330 // if we have something in the cache, try to fetch the query based cache
331 if (userHintsCache !== {}){
331 if (userHintsCache !== {}){
332 queryBasedHintsCache = userHintsCache[context.string];
332 queryBasedHintsCache = userHintsCache[context.string];
333 }
333 }
334
334
335 if (queryBasedHintsCache !== undefined) {
335 if (queryBasedHintsCache !== undefined) {
336 cmLog.debug('Users loaded from cache');
336 cmLog.debug('Users loaded from cache');
337 triggerHints(queryBasedHintsCache);
337 triggerHints(queryBasedHintsCache);
338 } else {
338 } else {
339 // this takes care for async loading, and then displaying results
339 // this takes care for async loading, and then displaying results
340 // and also propagates the userHintsCache
340 // and also propagates the userHintsCache
341 window.clearTimeout(loadUserHintTimer);
341 window.clearTimeout(loadUserHintTimer);
342 loadUserHintTimer = setTimeout(function() {
342 loadUserHintTimer = setTimeout(function() {
343 loadUserHints(context.string, triggerHints);
343 loadUserHints(context.string, triggerHints);
344 }, 300);
344 }, 300);
345 }
345 }
346 };
346 };
347
347
348 var actionHint = function(editor, options) {
348 var actionHint = function(editor, options) {
349 var cur = editor.getCursor();
349 var cur = editor.getCursor();
350 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
350 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
351
351
352 var tokenMatch = new RegExp('[a-zA-Z]{1}[a-zA-Z]*$').exec(curLine);
352 var tokenMatch = new RegExp('[a-zA-Z]{1}[a-zA-Z]*$').exec(curLine);
353
353
354 var tokenStr = '';
354 var tokenStr = '';
355 if (tokenMatch !== null && tokenMatch.length > 0){
355 if (tokenMatch !== null && tokenMatch.length > 0){
356 tokenStr = tokenMatch[0].strip();
356 tokenStr = tokenMatch[0].strip();
357 }
357 }
358
358
359 var context = {
359 var context = {
360 start: cur.ch - tokenStr.length,
360 start: cur.ch - tokenStr.length,
361 end: cur.ch,
361 end: cur.ch,
362 string: tokenStr,
362 string: tokenStr,
363 type: null
363 type: null
364 };
364 };
365
365
366 var actions = [
366 var actions = [
367 {
367 {
368 text: "approve",
368 text: "approve",
369 displayText: _TM['Set status to Approved'],
369 displayText: _gettext('Set status to Approved'),
370 hint: function(CodeMirror, data, completion) {
370 hint: function(CodeMirror, data, completion) {
371 CodeMirror.replaceRange("", completion.from || data.from,
371 CodeMirror.replaceRange("", completion.from || data.from,
372 completion.to || data.to, "complete");
372 completion.to || data.to, "complete");
373 $('#change_status').select2("val", 'approved').trigger('change');
373 $('#change_status').select2("val", 'approved').trigger('change');
374 },
374 },
375 render: function(elt, data, completion) {
375 render: function(elt, data, completion) {
376 var el = document.createElement('div');
376 var el = document.createElement('div');
377 el.className = "flag_status flag_status_comment_box approved pull-left";
377 el.className = "flag_status flag_status_comment_box approved pull-left";
378 elt.appendChild(el);
378 elt.appendChild(el);
379
379
380 el = document.createElement('span');
380 el = document.createElement('span');
381 el.innerHTML = completion.displayText;
381 el.innerHTML = completion.displayText;
382 elt.appendChild(el);
382 elt.appendChild(el);
383 }
383 }
384 },
384 },
385 {
385 {
386 text: "reject",
386 text: "reject",
387 displayText: _TM['Set status to Rejected'],
387 displayText: _gettext('Set status to Rejected'),
388 hint: function(CodeMirror, data, completion) {
388 hint: function(CodeMirror, data, completion) {
389 CodeMirror.replaceRange("", completion.from || data.from,
389 CodeMirror.replaceRange("", completion.from || data.from,
390 completion.to || data.to, "complete");
390 completion.to || data.to, "complete");
391 $('#change_status').select2("val", 'rejected').trigger('change');
391 $('#change_status').select2("val", 'rejected').trigger('change');
392 },
392 },
393 render: function(elt, data, completion) {
393 render: function(elt, data, completion) {
394 var el = document.createElement('div');
394 var el = document.createElement('div');
395 el.className = "flag_status flag_status_comment_box rejected pull-left";
395 el.className = "flag_status flag_status_comment_box rejected pull-left";
396 elt.appendChild(el);
396 elt.appendChild(el);
397
397
398 el = document.createElement('span');
398 el = document.createElement('span');
399 el.innerHTML = completion.displayText;
399 el.innerHTML = completion.displayText;
400 elt.appendChild(el);
400 elt.appendChild(el);
401 }
401 }
402 }
402 }
403 ];
403 ];
404
404
405 return {
405 return {
406 list: filterActions(actions, context),
406 list: filterActions(actions, context),
407 from: CodeMirror.Pos(cur.line, context.start),
407 from: CodeMirror.Pos(cur.line, context.start),
408 to: CodeMirror.Pos(cur.line, context.end)
408 to: CodeMirror.Pos(cur.line, context.end)
409 };
409 };
410 };
410 };
411 CodeMirror.registerHelper("hint", "mentions", mentionHint);
411 CodeMirror.registerHelper("hint", "mentions", mentionHint);
412 CodeMirror.registerHelper("hint", "actions", actionHint);
412 CodeMirror.registerHelper("hint", "actions", actionHint);
413 return cm;
413 return cm;
414 };
414 };
415
415
416 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
416 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
417 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
417 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
418 codeMirrorInstance.setOption("mode", mode);
418 codeMirrorInstance.setOption("mode", mode);
419 };
419 };
420
420
421 var setCodeMirrorLineWrap = function(codeMirrorInstance, line_wrap) {
421 var setCodeMirrorLineWrap = function(codeMirrorInstance, line_wrap) {
422 codeMirrorInstance.setOption("lineWrapping", line_wrap);
422 codeMirrorInstance.setOption("lineWrapping", line_wrap);
423 };
423 };
424
424
425 var setCodeMirrorModeFromSelect = function(
425 var setCodeMirrorModeFromSelect = function(
426 targetSelect, targetFileInput, codeMirrorInstance, callback){
426 targetSelect, targetFileInput, codeMirrorInstance, callback){
427
427
428 $(targetSelect).on('change', function(e) {
428 $(targetSelect).on('change', function(e) {
429 cmLog.debug('codemirror select2 mode change event !');
429 cmLog.debug('codemirror select2 mode change event !');
430 var selected = e.currentTarget;
430 var selected = e.currentTarget;
431 var node = selected.options[selected.selectedIndex];
431 var node = selected.options[selected.selectedIndex];
432 var mimetype = node.value;
432 var mimetype = node.value;
433 cmLog.debug('picked mimetype', mimetype);
433 cmLog.debug('picked mimetype', mimetype);
434 var new_mode = $(node).attr('mode');
434 var new_mode = $(node).attr('mode');
435 setCodeMirrorMode(codeMirrorInstance, new_mode);
435 setCodeMirrorMode(codeMirrorInstance, new_mode);
436 cmLog.debug('set new mode', new_mode);
436 cmLog.debug('set new mode', new_mode);
437
437
438 //propose filename from picked mode
438 //propose filename from picked mode
439 cmLog.debug('setting mimetype', mimetype);
439 cmLog.debug('setting mimetype', mimetype);
440 var proposed_ext = getExtFromMimeType(mimetype);
440 var proposed_ext = getExtFromMimeType(mimetype);
441 cmLog.debug('file input', $(targetFileInput).val());
441 cmLog.debug('file input', $(targetFileInput).val());
442 var file_data = getFilenameAndExt($(targetFileInput).val());
442 var file_data = getFilenameAndExt($(targetFileInput).val());
443 var filename = file_data.filename || 'filename1';
443 var filename = file_data.filename || 'filename1';
444 $(targetFileInput).val(filename + proposed_ext);
444 $(targetFileInput).val(filename + proposed_ext);
445 cmLog.debug('proposed file', filename + proposed_ext);
445 cmLog.debug('proposed file', filename + proposed_ext);
446
446
447
447
448 if (typeof(callback) === 'function') {
448 if (typeof(callback) === 'function') {
449 try {
449 try {
450 cmLog.debug('running callback', callback);
450 cmLog.debug('running callback', callback);
451 callback(filename, mimetype, new_mode);
451 callback(filename, mimetype, new_mode);
452 } catch (err) {
452 } catch (err) {
453 console.log('failed to run callback', callback, err);
453 console.log('failed to run callback', callback, err);
454 }
454 }
455 }
455 }
456 cmLog.debug('finish iteration...');
456 cmLog.debug('finish iteration...');
457 });
457 });
458 };
458 };
459
459
460 var setCodeMirrorModeFromInput = function(
460 var setCodeMirrorModeFromInput = function(
461 targetSelect, targetFileInput, codeMirrorInstance, callback) {
461 targetSelect, targetFileInput, codeMirrorInstance, callback) {
462
462
463 // on type the new filename set mode
463 // on type the new filename set mode
464 $(targetFileInput).on('keyup', function(e) {
464 $(targetFileInput).on('keyup', function(e) {
465 var file_data = getFilenameAndExt(this.value);
465 var file_data = getFilenameAndExt(this.value);
466 if (file_data.ext === null) {
466 if (file_data.ext === null) {
467 return;
467 return;
468 }
468 }
469
469
470 var mimetypes = getMimeTypeFromExt(file_data.ext, true);
470 var mimetypes = getMimeTypeFromExt(file_data.ext, true);
471 cmLog.debug('mimetype from file', file_data, mimetypes);
471 cmLog.debug('mimetype from file', file_data, mimetypes);
472 var detected_mode;
472 var detected_mode;
473 var detected_option;
473 var detected_option;
474 for (var i in mimetypes) {
474 for (var i in mimetypes) {
475 var mt = mimetypes[i];
475 var mt = mimetypes[i];
476 if (!detected_mode) {
476 if (!detected_mode) {
477 detected_mode = detectCodeMirrorMode(this.value, mt);
477 detected_mode = detectCodeMirrorMode(this.value, mt);
478 }
478 }
479
479
480 if (!detected_option) {
480 if (!detected_option) {
481 cmLog.debug('#mimetype option[value="{0}"]'.format(mt));
481 cmLog.debug('#mimetype option[value="{0}"]'.format(mt));
482 if ($(targetSelect).find('option[value="{0}"]'.format(mt)).length) {
482 if ($(targetSelect).find('option[value="{0}"]'.format(mt)).length) {
483 detected_option = mt;
483 detected_option = mt;
484 }
484 }
485 }
485 }
486 }
486 }
487
487
488 cmLog.debug('detected mode', detected_mode);
488 cmLog.debug('detected mode', detected_mode);
489 cmLog.debug('detected option', detected_option);
489 cmLog.debug('detected option', detected_option);
490 if (detected_mode && detected_option){
490 if (detected_mode && detected_option){
491
491
492 $(targetSelect).select2("val", detected_option);
492 $(targetSelect).select2("val", detected_option);
493 setCodeMirrorMode(codeMirrorInstance, detected_mode);
493 setCodeMirrorMode(codeMirrorInstance, detected_mode);
494
494
495 if(typeof(callback) === 'function'){
495 if(typeof(callback) === 'function'){
496 try{
496 try{
497 cmLog.debug('running callback', callback);
497 cmLog.debug('running callback', callback);
498 var filename = file_data.filename + "." + file_data.ext;
498 var filename = file_data.filename + "." + file_data.ext;
499 callback(filename, detected_option, detected_mode);
499 callback(filename, detected_option, detected_mode);
500 }catch (err){
500 }catch (err){
501 console.log('failed to run callback', callback, err);
501 console.log('failed to run callback', callback, err);
502 }
502 }
503 }
503 }
504 }
504 }
505
505
506 });
506 });
507 };
507 };
508
508
509 var fillCodeMirrorOptions = function(targetSelect) {
509 var fillCodeMirrorOptions = function(targetSelect) {
510 //inject new modes, based on codeMirrors modeInfo object
510 //inject new modes, based on codeMirrors modeInfo object
511 var modes_select = $(targetSelect);
511 var modes_select = $(targetSelect);
512 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
512 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
513 var m = CodeMirror.modeInfo[i];
513 var m = CodeMirror.modeInfo[i];
514 var opt = new Option(m.name, m.mime);
514 var opt = new Option(m.name, m.mime);
515 $(opt).attr('mode', m.mode);
515 $(opt).attr('mode', m.mode);
516 modes_select.append(opt);
516 modes_select.append(opt);
517 }
517 }
518 };
518 };
519
519
520 var CodeMirrorPreviewEnable = function(edit_mode) {
520 var CodeMirrorPreviewEnable = function(edit_mode) {
521 // in case it a preview enabled mode enable the button
521 // in case it a preview enabled mode enable the button
522 if (['markdown', 'rst', 'gfm'].indexOf(edit_mode) !== -1) {
522 if (['markdown', 'rst', 'gfm'].indexOf(edit_mode) !== -1) {
523 $('#render_preview').removeClass('hidden');
523 $('#render_preview').removeClass('hidden');
524 }
524 }
525 else {
525 else {
526 if (!$('#render_preview').hasClass('hidden')) {
526 if (!$('#render_preview').hasClass('hidden')) {
527 $('#render_preview').addClass('hidden');
527 $('#render_preview').addClass('hidden');
528 }
528 }
529 }
529 }
530 };
530 };
@@ -1,664 +1,664 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
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 Affero General Public License
12 // # You should have received a copy of the GNU Affero 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 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var firefoxAnchorFix = function() {
19 var firefoxAnchorFix = function() {
20 // hack to make anchor links behave properly on firefox, in our inline
20 // hack to make anchor links behave properly on firefox, in our inline
21 // comments generation when comments are injected firefox is misbehaving
21 // comments generation when comments are injected firefox is misbehaving
22 // when jumping to anchor links
22 // when jumping to anchor links
23 if (location.href.indexOf('#') > -1) {
23 if (location.href.indexOf('#') > -1) {
24 location.href += '';
24 location.href += '';
25 }
25 }
26 };
26 };
27
27
28 // returns a node from given html;
28 // returns a node from given html;
29 var fromHTML = function(html){
29 var fromHTML = function(html){
30 var _html = document.createElement('element');
30 var _html = document.createElement('element');
31 _html.innerHTML = html;
31 _html.innerHTML = html;
32 return _html;
32 return _html;
33 };
33 };
34
34
35 var tableTr = function(cls, body){
35 var tableTr = function(cls, body){
36 var _el = document.createElement('div');
36 var _el = document.createElement('div');
37 var _body = $(body).attr('id');
37 var _body = $(body).attr('id');
38 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
38 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
39 var id = 'comment-tr-{0}'.format(comment_id);
39 var id = 'comment-tr-{0}'.format(comment_id);
40 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
40 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
41 '<td class="add-comment-line"><span class="add-comment-content"></span></td>'+
41 '<td class="add-comment-line"><span class="add-comment-content"></span></td>'+
42 '<td></td>'+
42 '<td></td>'+
43 '<td></td>'+
43 '<td></td>'+
44 '<td>{2}</td>'+
44 '<td>{2}</td>'+
45 '</tr></tbody></table>').format(id, cls, body);
45 '</tr></tbody></table>').format(id, cls, body);
46 $(_el).html(_html);
46 $(_el).html(_html);
47 return _el.children[0].children[0].children[0];
47 return _el.children[0].children[0].children[0];
48 };
48 };
49
49
50 var removeInlineForm = function(form) {
50 var removeInlineForm = function(form) {
51 form.parentNode.removeChild(form);
51 form.parentNode.removeChild(form);
52 };
52 };
53
53
54 var createInlineForm = function(parent_tr, f_path, line) {
54 var createInlineForm = function(parent_tr, f_path, line) {
55 var tmpl = $('#comment-inline-form-template').html();
55 var tmpl = $('#comment-inline-form-template').html();
56 tmpl = tmpl.format(f_path, line);
56 tmpl = tmpl.format(f_path, line);
57 var form = tableTr('comment-form-inline', tmpl);
57 var form = tableTr('comment-form-inline', tmpl);
58 var form_hide_button = $(form).find('.hide-inline-form');
58 var form_hide_button = $(form).find('.hide-inline-form');
59
59
60 $(form_hide_button).click(function(e) {
60 $(form_hide_button).click(function(e) {
61 $('.inline-comments').removeClass('hide-comment-button');
61 $('.inline-comments').removeClass('hide-comment-button');
62 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
62 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
63 if ($(newtr.nextElementSibling).hasClass('inline-comments-button')) {
63 if ($(newtr.nextElementSibling).hasClass('inline-comments-button')) {
64 $(newtr.nextElementSibling).show();
64 $(newtr.nextElementSibling).show();
65 }
65 }
66 $(newtr).parents('.comment-form-inline').remove();
66 $(newtr).parents('.comment-form-inline').remove();
67 $(parent_tr).removeClass('form-open');
67 $(parent_tr).removeClass('form-open');
68 $(parent_tr).removeClass('hl-comment');
68 $(parent_tr).removeClass('hl-comment');
69 });
69 });
70
70
71 return form;
71 return form;
72 };
72 };
73
73
74 var getLineNo = function(tr) {
74 var getLineNo = function(tr) {
75 var line;
75 var line;
76 // Try to get the id and return "" (empty string) if it doesn't exist
76 // Try to get the id and return "" (empty string) if it doesn't exist
77 var o = ($(tr).find('.lineno.old').attr('id')||"").split('_');
77 var o = ($(tr).find('.lineno.old').attr('id')||"").split('_');
78 var n = ($(tr).find('.lineno.new').attr('id')||"").split('_');
78 var n = ($(tr).find('.lineno.new').attr('id')||"").split('_');
79 if (n.length >= 2) {
79 if (n.length >= 2) {
80 line = n[n.length-1];
80 line = n[n.length-1];
81 } else if (o.length >= 2) {
81 } else if (o.length >= 2) {
82 line = o[o.length-1];
82 line = o[o.length-1];
83 }
83 }
84 return line;
84 return line;
85 };
85 };
86
86
87 /**
87 /**
88 * make a single inline comment and place it inside
88 * make a single inline comment and place it inside
89 */
89 */
90 var renderInlineComment = function(json_data, show_add_button) {
90 var renderInlineComment = function(json_data, show_add_button) {
91 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
91 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
92 try {
92 try {
93 var html = json_data.rendered_text;
93 var html = json_data.rendered_text;
94 var lineno = json_data.line_no;
94 var lineno = json_data.line_no;
95 var target_id = json_data.target_id;
95 var target_id = json_data.target_id;
96 placeInline(target_id, lineno, html, show_add_button);
96 placeInline(target_id, lineno, html, show_add_button);
97 } catch (e) {
97 } catch (e) {
98 console.error(e);
98 console.error(e);
99 }
99 }
100 };
100 };
101
101
102 function bindDeleteCommentButtons() {
102 function bindDeleteCommentButtons() {
103 $('.delete-comment').one('click', function() {
103 $('.delete-comment').one('click', function() {
104 var comment_id = $(this).data("comment-id");
104 var comment_id = $(this).data("comment-id");
105
105
106 if (comment_id){
106 if (comment_id){
107 deleteComment(comment_id);
107 deleteComment(comment_id);
108 }
108 }
109 });
109 });
110 }
110 }
111
111
112 /**
112 /**
113 * Inject inline comment for on given TR this tr should be always an .line
113 * Inject inline comment for on given TR this tr should be always an .line
114 * tr containing the line. Code will detect comment, and always put the comment
114 * tr containing the line. Code will detect comment, and always put the comment
115 * block at the very bottom
115 * block at the very bottom
116 */
116 */
117 var injectInlineForm = function(tr){
117 var injectInlineForm = function(tr){
118 if (!$(tr).hasClass('line')) {
118 if (!$(tr).hasClass('line')) {
119 return;
119 return;
120 }
120 }
121
121
122 var _td = $(tr).find('.code').get(0);
122 var _td = $(tr).find('.code').get(0);
123 if ($(tr).hasClass('form-open') ||
123 if ($(tr).hasClass('form-open') ||
124 $(tr).hasClass('context') ||
124 $(tr).hasClass('context') ||
125 $(_td).hasClass('no-comment')) {
125 $(_td).hasClass('no-comment')) {
126 return;
126 return;
127 }
127 }
128 $(tr).addClass('form-open');
128 $(tr).addClass('form-open');
129 $(tr).addClass('hl-comment');
129 $(tr).addClass('hl-comment');
130 var node = $(tr.parentNode.parentNode.parentNode).find('.full_f_path').get(0);
130 var node = $(tr.parentNode.parentNode.parentNode).find('.full_f_path').get(0);
131 var f_path = $(node).attr('path');
131 var f_path = $(node).attr('path');
132 var lineno = getLineNo(tr);
132 var lineno = getLineNo(tr);
133 var form = createInlineForm(tr, f_path, lineno);
133 var form = createInlineForm(tr, f_path, lineno);
134
134
135 var parent = tr;
135 var parent = tr;
136 while (1) {
136 while (1) {
137 var n = parent.nextElementSibling;
137 var n = parent.nextElementSibling;
138 // next element are comments !
138 // next element are comments !
139 if ($(n).hasClass('inline-comments')) {
139 if ($(n).hasClass('inline-comments')) {
140 parent = n;
140 parent = n;
141 }
141 }
142 else {
142 else {
143 break;
143 break;
144 }
144 }
145 }
145 }
146 var _parent = $(parent).get(0);
146 var _parent = $(parent).get(0);
147 $(_parent).after(form);
147 $(_parent).after(form);
148 $('.comment-form-inline').prev('.inline-comments').addClass('hide-comment-button');
148 $('.comment-form-inline').prev('.inline-comments').addClass('hide-comment-button');
149 var f = $(form).get(0);
149 var f = $(form).get(0);
150
150
151 var _form = $(f).find('.inline-form').get(0);
151 var _form = $(f).find('.inline-form').get(0);
152
152
153 $('.switch-to-chat', _form).on('click', function(evt){
153 $('.switch-to-chat', _form).on('click', function(evt){
154 var fParent = $(_parent).closest('.injected_diff').parent().prev('*[fid]');
154 var fParent = $(_parent).closest('.injected_diff').parent().prev('*[fid]');
155 var fid = fParent.attr('fid');
155 var fid = fParent.attr('fid');
156
156
157 // activate chat and trigger subscription to channels
157 // activate chat and trigger subscription to channels
158 $.Topic('/chat_controller').publish({
158 $.Topic('/chat_controller').publish({
159 action:'subscribe_to_channels',
159 action:'subscribe_to_channels',
160 data: ['/chat${0}$/fid/{1}/{2}'.format(templateContext.repo_name, fid, lineno)]
160 data: ['/chat${0}$/fid/{1}/{2}'.format(templateContext.repo_name, fid, lineno)]
161 });
161 });
162 $(_form).closest('td').find('.comment-inline-form').addClass('hidden');
162 $(_form).closest('td').find('.comment-inline-form').addClass('hidden');
163 $(_form).closest('td').find('.chat-holder').removeClass('hidden');
163 $(_form).closest('td').find('.chat-holder').removeClass('hidden');
164 });
164 });
165
165
166 var pullRequestId = templateContext.pull_request_data.pull_request_id;
166 var pullRequestId = templateContext.pull_request_data.pull_request_id;
167 var commitId = templateContext.commit_data.commit_id;
167 var commitId = templateContext.commit_data.commit_id;
168
168
169 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
169 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
170 var cm = commentForm.getCmInstance();
170 var cm = commentForm.getCmInstance();
171
171
172 // set a CUSTOM submit handler for inline comments.
172 // set a CUSTOM submit handler for inline comments.
173 commentForm.setHandleFormSubmit(function(o) {
173 commentForm.setHandleFormSubmit(function(o) {
174 var text = commentForm.cm.getValue();
174 var text = commentForm.cm.getValue();
175
175
176 if (text === "") {
176 if (text === "") {
177 return;
177 return;
178 }
178 }
179
179
180 if (lineno === undefined) {
180 if (lineno === undefined) {
181 alert('missing line !');
181 alert('missing line !');
182 return;
182 return;
183 }
183 }
184 if (f_path === undefined) {
184 if (f_path === undefined) {
185 alert('missing file path !');
185 alert('missing file path !');
186 return;
186 return;
187 }
187 }
188
188
189 var excludeCancelBtn = false;
189 var excludeCancelBtn = false;
190 var submitEvent = true;
190 var submitEvent = true;
191 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
191 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
192 commentForm.cm.setOption("readOnly", true);
192 commentForm.cm.setOption("readOnly", true);
193 var postData = {
193 var postData = {
194 'text': text,
194 'text': text,
195 'f_path': f_path,
195 'f_path': f_path,
196 'line': lineno,
196 'line': lineno,
197 'csrf_token': CSRF_TOKEN
197 'csrf_token': CSRF_TOKEN
198 };
198 };
199 var submitSuccessCallback = function(o) {
199 var submitSuccessCallback = function(o) {
200 $(tr).removeClass('form-open');
200 $(tr).removeClass('form-open');
201 removeInlineForm(f);
201 removeInlineForm(f);
202 renderInlineComment(o);
202 renderInlineComment(o);
203 $('.inline-comments').removeClass('hide-comment-button');
203 $('.inline-comments').removeClass('hide-comment-button');
204
204
205 // re trigger the linkification of next/prev navigation
205 // re trigger the linkification of next/prev navigation
206 linkifyComments($('.inline-comment-injected'));
206 linkifyComments($('.inline-comment-injected'));
207 timeagoActivate();
207 timeagoActivate();
208 tooltip_activate();
208 tooltip_activate();
209 bindDeleteCommentButtons();
209 bindDeleteCommentButtons();
210 commentForm.setActionButtonsDisabled(false);
210 commentForm.setActionButtonsDisabled(false);
211
211
212 };
212 };
213 var submitFailCallback = function(){
213 var submitFailCallback = function(){
214 commentForm.resetCommentFormState(text)
214 commentForm.resetCommentFormState(text)
215 };
215 };
216 commentForm.submitAjaxPOST(
216 commentForm.submitAjaxPOST(
217 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
217 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
218 });
218 });
219
219
220 setTimeout(function() {
220 setTimeout(function() {
221 // callbacks
221 // callbacks
222 if (cm !== undefined) {
222 if (cm !== undefined) {
223 cm.focus();
223 cm.focus();
224 }
224 }
225 }, 10);
225 }, 10);
226
226
227 };
227 };
228
228
229 var deleteComment = function(comment_id) {
229 var deleteComment = function(comment_id) {
230 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
230 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
231 var postData = {
231 var postData = {
232 '_method': 'delete',
232 '_method': 'delete',
233 'csrf_token': CSRF_TOKEN
233 'csrf_token': CSRF_TOKEN
234 };
234 };
235
235
236 var success = function(o) {
236 var success = function(o) {
237 window.location.reload();
237 window.location.reload();
238 };
238 };
239 ajaxPOST(url, postData, success);
239 ajaxPOST(url, postData, success);
240 };
240 };
241
241
242 var createInlineAddButton = function(tr){
242 var createInlineAddButton = function(tr){
243 var label = _TM['Add another comment'];
243 var label = _gettext('Add another comment');
244 var html_el = document.createElement('div');
244 var html_el = document.createElement('div');
245 $(html_el).addClass('add-comment');
245 $(html_el).addClass('add-comment');
246 html_el.innerHTML = '<span class="btn btn-secondary">{0}</span>'.format(label);
246 html_el.innerHTML = '<span class="btn btn-secondary">{0}</span>'.format(label);
247 var add = new $(html_el);
247 var add = new $(html_el);
248 add.on('click', function(e) {
248 add.on('click', function(e) {
249 injectInlineForm(tr);
249 injectInlineForm(tr);
250 });
250 });
251 return add;
251 return add;
252 };
252 };
253
253
254 var placeAddButton = function(target_tr){
254 var placeAddButton = function(target_tr){
255 if(!target_tr){
255 if(!target_tr){
256 return;
256 return;
257 }
257 }
258 var last_node = target_tr;
258 var last_node = target_tr;
259 // scan
259 // scan
260 while (1){
260 while (1){
261 var n = last_node.nextElementSibling;
261 var n = last_node.nextElementSibling;
262 // next element are comments !
262 // next element are comments !
263 if($(n).hasClass('inline-comments')){
263 if($(n).hasClass('inline-comments')){
264 last_node = n;
264 last_node = n;
265 // also remove the comment button from previous
265 // also remove the comment button from previous
266 var comment_add_buttons = $(last_node).find('.add-comment');
266 var comment_add_buttons = $(last_node).find('.add-comment');
267 for(var i=0; i<comment_add_buttons.length; i++){
267 for(var i=0; i<comment_add_buttons.length; i++){
268 var b = comment_add_buttons[i];
268 var b = comment_add_buttons[i];
269 b.parentNode.removeChild(b);
269 b.parentNode.removeChild(b);
270 }
270 }
271 }
271 }
272 else{
272 else{
273 break;
273 break;
274 }
274 }
275 }
275 }
276 var add = createInlineAddButton(target_tr);
276 var add = createInlineAddButton(target_tr);
277 // get the comment div
277 // get the comment div
278 var comment_block = $(last_node).find('.comment')[0];
278 var comment_block = $(last_node).find('.comment')[0];
279 // attach add button
279 // attach add button
280 $(add).insertAfter(comment_block);
280 $(add).insertAfter(comment_block);
281 };
281 };
282
282
283 /**
283 /**
284 * Places the inline comment into the changeset block in proper line position
284 * Places the inline comment into the changeset block in proper line position
285 */
285 */
286 var placeInline = function(target_container, lineno, html, show_add_button) {
286 var placeInline = function(target_container, lineno, html, show_add_button) {
287 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
287 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
288
288
289 var lineid = "{0}_{1}".format(target_container, lineno);
289 var lineid = "{0}_{1}".format(target_container, lineno);
290 var target_line = $('#' + lineid).get(0);
290 var target_line = $('#' + lineid).get(0);
291 var comment = new $(tableTr('inline-comments', html));
291 var comment = new $(tableTr('inline-comments', html));
292 // check if there are comments already !
292 // check if there are comments already !
293 var parent_node = target_line.parentNode;
293 var parent_node = target_line.parentNode;
294 var root_parent = parent_node;
294 var root_parent = parent_node;
295 while (1) {
295 while (1) {
296 var n = parent_node.nextElementSibling;
296 var n = parent_node.nextElementSibling;
297 // next element are comments !
297 // next element are comments !
298 if ($(n).hasClass('inline-comments')) {
298 if ($(n).hasClass('inline-comments')) {
299 parent_node = n;
299 parent_node = n;
300 }
300 }
301 else {
301 else {
302 break;
302 break;
303 }
303 }
304 }
304 }
305 // put in the comment at the bottom
305 // put in the comment at the bottom
306 $(comment).insertAfter(parent_node);
306 $(comment).insertAfter(parent_node);
307 $(comment).find('.comment-inline').addClass('inline-comment-injected');
307 $(comment).find('.comment-inline').addClass('inline-comment-injected');
308 // scan nodes, and attach add button to last one
308 // scan nodes, and attach add button to last one
309 if (show_add_button) {
309 if (show_add_button) {
310 placeAddButton(root_parent);
310 placeAddButton(root_parent);
311 }
311 }
312
312
313 return target_line;
313 return target_line;
314 };
314 };
315
315
316 var linkifyComments = function(comments) {
316 var linkifyComments = function(comments) {
317
317
318 for (var i = 0; i < comments.length; i++) {
318 for (var i = 0; i < comments.length; i++) {
319 var comment_id = $(comments[i]).data('comment-id');
319 var comment_id = $(comments[i]).data('comment-id');
320 var prev_comment_id = $(comments[i - 1]).data('comment-id');
320 var prev_comment_id = $(comments[i - 1]).data('comment-id');
321 var next_comment_id = $(comments[i + 1]).data('comment-id');
321 var next_comment_id = $(comments[i + 1]).data('comment-id');
322
322
323 // place next/prev links
323 // place next/prev links
324 if (prev_comment_id) {
324 if (prev_comment_id) {
325 $('#prev_c_' + comment_id).show();
325 $('#prev_c_' + comment_id).show();
326 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
326 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
327 'href', '#comment-' + prev_comment_id).removeClass('disabled');
327 'href', '#comment-' + prev_comment_id).removeClass('disabled');
328 }
328 }
329 if (next_comment_id) {
329 if (next_comment_id) {
330 $('#next_c_' + comment_id).show();
330 $('#next_c_' + comment_id).show();
331 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
331 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
332 'href', '#comment-' + next_comment_id).removeClass('disabled');
332 'href', '#comment-' + next_comment_id).removeClass('disabled');
333 }
333 }
334 // place a first link to the total counter
334 // place a first link to the total counter
335 if (i === 0) {
335 if (i === 0) {
336 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
336 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
337 }
337 }
338 }
338 }
339
339
340 };
340 };
341
341
342 /**
342 /**
343 * Iterates over all the inlines, and places them inside proper blocks of data
343 * Iterates over all the inlines, and places them inside proper blocks of data
344 */
344 */
345 var renderInlineComments = function(file_comments, show_add_button) {
345 var renderInlineComments = function(file_comments, show_add_button) {
346 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
346 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
347
347
348 for (var i = 0; i < file_comments.length; i++) {
348 for (var i = 0; i < file_comments.length; i++) {
349 var box = file_comments[i];
349 var box = file_comments[i];
350
350
351 var target_id = $(box).attr('target_id');
351 var target_id = $(box).attr('target_id');
352
352
353 // actually comments with line numbers
353 // actually comments with line numbers
354 var comments = box.children;
354 var comments = box.children;
355
355
356 for (var j = 0; j < comments.length; j++) {
356 for (var j = 0; j < comments.length; j++) {
357 var data = {
357 var data = {
358 'rendered_text': comments[j].outerHTML,
358 'rendered_text': comments[j].outerHTML,
359 'line_no': $(comments[j]).attr('line'),
359 'line_no': $(comments[j]).attr('line'),
360 'target_id': target_id
360 'target_id': target_id
361 };
361 };
362 renderInlineComment(data, show_add_button);
362 renderInlineComment(data, show_add_button);
363 }
363 }
364 }
364 }
365
365
366 // since order of injection is random, we're now re-iterating
366 // since order of injection is random, we're now re-iterating
367 // from correct order and filling in links
367 // from correct order and filling in links
368 linkifyComments($('.inline-comment-injected'));
368 linkifyComments($('.inline-comment-injected'));
369 bindDeleteCommentButtons();
369 bindDeleteCommentButtons();
370 firefoxAnchorFix();
370 firefoxAnchorFix();
371 };
371 };
372
372
373
373
374 /* Comment form for main and inline comments */
374 /* Comment form for main and inline comments */
375 var CommentForm = (function() {
375 var CommentForm = (function() {
376 "use strict";
376 "use strict";
377
377
378 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
378 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
379
379
380 this.withLineNo = function(selector) {
380 this.withLineNo = function(selector) {
381 var lineNo = this.lineNo;
381 var lineNo = this.lineNo;
382 if (lineNo === undefined) {
382 if (lineNo === undefined) {
383 return selector
383 return selector
384 } else {
384 } else {
385 return selector + '_' + lineNo;
385 return selector + '_' + lineNo;
386 }
386 }
387 };
387 };
388
388
389 this.commitId = commitId;
389 this.commitId = commitId;
390 this.pullRequestId = pullRequestId;
390 this.pullRequestId = pullRequestId;
391 this.lineNo = lineNo;
391 this.lineNo = lineNo;
392 this.initAutocompleteActions = initAutocompleteActions;
392 this.initAutocompleteActions = initAutocompleteActions;
393
393
394 this.previewButton = this.withLineNo('#preview-btn');
394 this.previewButton = this.withLineNo('#preview-btn');
395 this.previewContainer = this.withLineNo('#preview-container');
395 this.previewContainer = this.withLineNo('#preview-container');
396
396
397 this.previewBoxSelector = this.withLineNo('#preview-box');
397 this.previewBoxSelector = this.withLineNo('#preview-box');
398
398
399 this.editButton = this.withLineNo('#edit-btn');
399 this.editButton = this.withLineNo('#edit-btn');
400 this.editContainer = this.withLineNo('#edit-container');
400 this.editContainer = this.withLineNo('#edit-container');
401
401
402 this.cancelButton = this.withLineNo('#cancel-btn');
402 this.cancelButton = this.withLineNo('#cancel-btn');
403
403
404 this.statusChange = '#change_status';
404 this.statusChange = '#change_status';
405 this.cmBox = this.withLineNo('#text');
405 this.cmBox = this.withLineNo('#text');
406 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
406 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
407
407
408 this.submitForm = formElement;
408 this.submitForm = formElement;
409 this.submitButton = $(this.submitForm).find('input[type="submit"]');
409 this.submitButton = $(this.submitForm).find('input[type="submit"]');
410 this.submitButtonText = this.submitButton.val();
410 this.submitButtonText = this.submitButton.val();
411
411
412 this.previewUrl = pyroutes.url('changeset_comment_preview',
412 this.previewUrl = pyroutes.url('changeset_comment_preview',
413 {'repo_name': templateContext.repo_name});
413 {'repo_name': templateContext.repo_name});
414
414
415 // based on commitId, or pullReuqestId decide where do we submit
415 // based on commitId, or pullReuqestId decide where do we submit
416 // out data
416 // out data
417 if (this.commitId){
417 if (this.commitId){
418 this.submitUrl = pyroutes.url('changeset_comment',
418 this.submitUrl = pyroutes.url('changeset_comment',
419 {'repo_name': templateContext.repo_name,
419 {'repo_name': templateContext.repo_name,
420 'revision': this.commitId});
420 'revision': this.commitId});
421
421
422 } else if (this.pullRequestId) {
422 } else if (this.pullRequestId) {
423 this.submitUrl = pyroutes.url('pullrequest_comment',
423 this.submitUrl = pyroutes.url('pullrequest_comment',
424 {'repo_name': templateContext.repo_name,
424 {'repo_name': templateContext.repo_name,
425 'pull_request_id': this.pullRequestId});
425 'pull_request_id': this.pullRequestId});
426
426
427 } else {
427 } else {
428 throw new Error(
428 throw new Error(
429 'CommentForm requires pullRequestId, or commitId to be specified.')
429 'CommentForm requires pullRequestId, or commitId to be specified.')
430 }
430 }
431
431
432 this.getCmInstance = function(){
432 this.getCmInstance = function(){
433 return this.cm
433 return this.cm
434 };
434 };
435
435
436 var self = this;
436 var self = this;
437
437
438 this.getCommentStatus = function() {
438 this.getCommentStatus = function() {
439 return $(this.submitForm).find(this.statusChange).val();
439 return $(this.submitForm).find(this.statusChange).val();
440 };
440 };
441
441
442 this.isAllowedToSubmit = function() {
442 this.isAllowedToSubmit = function() {
443 return !$(this.submitButton).prop('disabled');
443 return !$(this.submitButton).prop('disabled');
444 };
444 };
445
445
446 this.initStatusChangeSelector = function(){
446 this.initStatusChangeSelector = function(){
447 var formatChangeStatus = function(state, escapeMarkup) {
447 var formatChangeStatus = function(state, escapeMarkup) {
448 var originalOption = state.element;
448 var originalOption = state.element;
449 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
449 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
450 '<span>' + escapeMarkup(state.text) + '</span>';
450 '<span>' + escapeMarkup(state.text) + '</span>';
451 };
451 };
452 var formatResult = function(result, container, query, escapeMarkup) {
452 var formatResult = function(result, container, query, escapeMarkup) {
453 return formatChangeStatus(result, escapeMarkup);
453 return formatChangeStatus(result, escapeMarkup);
454 };
454 };
455
455
456 var formatSelection = function(data, container, escapeMarkup) {
456 var formatSelection = function(data, container, escapeMarkup) {
457 return formatChangeStatus(data, escapeMarkup);
457 return formatChangeStatus(data, escapeMarkup);
458 };
458 };
459
459
460 $(this.submitForm).find(this.statusChange).select2({
460 $(this.submitForm).find(this.statusChange).select2({
461 placeholder: _TM['Status Review'],
461 placeholder: _gettext('Status Review'),
462 formatResult: formatResult,
462 formatResult: formatResult,
463 formatSelection: formatSelection,
463 formatSelection: formatSelection,
464 containerCssClass: "drop-menu status_box_menu",
464 containerCssClass: "drop-menu status_box_menu",
465 dropdownCssClass: "drop-menu-dropdown",
465 dropdownCssClass: "drop-menu-dropdown",
466 dropdownAutoWidth: true,
466 dropdownAutoWidth: true,
467 minimumResultsForSearch: -1
467 minimumResultsForSearch: -1
468 });
468 });
469 $(this.submitForm).find(this.statusChange).on('change', function() {
469 $(this.submitForm).find(this.statusChange).on('change', function() {
470 var status = self.getCommentStatus();
470 var status = self.getCommentStatus();
471 if (status && !self.lineNo) {
471 if (status && !self.lineNo) {
472 $(self.submitButton).prop('disabled', false);
472 $(self.submitButton).prop('disabled', false);
473 }
473 }
474 //todo, fix this name
474 //todo, fix this name
475 var placeholderText = _TM['Comment text will be set automatically based on currently selected status ({0}) ...'].format(status);
475 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
476 self.cm.setOption('placeholder', placeholderText);
476 self.cm.setOption('placeholder', placeholderText);
477 })
477 })
478 };
478 };
479
479
480 // reset the comment form into it's original state
480 // reset the comment form into it's original state
481 this.resetCommentFormState = function(content) {
481 this.resetCommentFormState = function(content) {
482 content = content || '';
482 content = content || '';
483
483
484 $(this.editContainer).show();
484 $(this.editContainer).show();
485 $(this.editButton).hide();
485 $(this.editButton).hide();
486
486
487 $(this.previewContainer).hide();
487 $(this.previewContainer).hide();
488 $(this.previewButton).show();
488 $(this.previewButton).show();
489
489
490 this.setActionButtonsDisabled(true);
490 this.setActionButtonsDisabled(true);
491 self.cm.setValue(content);
491 self.cm.setValue(content);
492 self.cm.setOption("readOnly", false);
492 self.cm.setOption("readOnly", false);
493 };
493 };
494
494
495 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
495 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
496 failHandler = failHandler || function() {};
496 failHandler = failHandler || function() {};
497 var postData = toQueryString(postData);
497 var postData = toQueryString(postData);
498 var request = $.ajax({
498 var request = $.ajax({
499 url: url,
499 url: url,
500 type: 'POST',
500 type: 'POST',
501 data: postData,
501 data: postData,
502 headers: {'X-PARTIAL-XHR': true}
502 headers: {'X-PARTIAL-XHR': true}
503 })
503 })
504 .done(function(data) {
504 .done(function(data) {
505 successHandler(data);
505 successHandler(data);
506 })
506 })
507 .fail(function(data, textStatus, errorThrown){
507 .fail(function(data, textStatus, errorThrown){
508 alert(
508 alert(
509 "Error while submitting comment.\n" +
509 "Error while submitting comment.\n" +
510 "Error code {0} ({1}).".format(data.status, data.statusText));
510 "Error code {0} ({1}).".format(data.status, data.statusText));
511 failHandler()
511 failHandler()
512 });
512 });
513 return request;
513 return request;
514 };
514 };
515
515
516 // overwrite a submitHandler, we need to do it for inline comments
516 // overwrite a submitHandler, we need to do it for inline comments
517 this.setHandleFormSubmit = function(callback) {
517 this.setHandleFormSubmit = function(callback) {
518 this.handleFormSubmit = callback;
518 this.handleFormSubmit = callback;
519 };
519 };
520
520
521 // default handler for for submit for main comments
521 // default handler for for submit for main comments
522 this.handleFormSubmit = function() {
522 this.handleFormSubmit = function() {
523 var text = self.cm.getValue();
523 var text = self.cm.getValue();
524 var status = self.getCommentStatus();
524 var status = self.getCommentStatus();
525
525
526 if (text === "" && !status) {
526 if (text === "" && !status) {
527 return;
527 return;
528 }
528 }
529
529
530 var excludeCancelBtn = false;
530 var excludeCancelBtn = false;
531 var submitEvent = true;
531 var submitEvent = true;
532 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
532 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
533 self.cm.setOption("readOnly", true);
533 self.cm.setOption("readOnly", true);
534 var postData = {
534 var postData = {
535 'text': text,
535 'text': text,
536 'changeset_status': status,
536 'changeset_status': status,
537 'csrf_token': CSRF_TOKEN
537 'csrf_token': CSRF_TOKEN
538 };
538 };
539
539
540 var submitSuccessCallback = function(o) {
540 var submitSuccessCallback = function(o) {
541 if (status) {
541 if (status) {
542 location.reload(true);
542 location.reload(true);
543 } else {
543 } else {
544 $('#injected_page_comments').append(o.rendered_text);
544 $('#injected_page_comments').append(o.rendered_text);
545 self.resetCommentFormState();
545 self.resetCommentFormState();
546 bindDeleteCommentButtons();
546 bindDeleteCommentButtons();
547 timeagoActivate();
547 timeagoActivate();
548 tooltip_activate();
548 tooltip_activate();
549 }
549 }
550 };
550 };
551 var submitFailCallback = function(){
551 var submitFailCallback = function(){
552 self.resetCommentFormState(text)
552 self.resetCommentFormState(text)
553 };
553 };
554 self.submitAjaxPOST(
554 self.submitAjaxPOST(
555 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
555 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
556 };
556 };
557
557
558 this.previewSuccessCallback = function(o) {
558 this.previewSuccessCallback = function(o) {
559 $(self.previewBoxSelector).html(o);
559 $(self.previewBoxSelector).html(o);
560 $(self.previewBoxSelector).removeClass('unloaded');
560 $(self.previewBoxSelector).removeClass('unloaded');
561
561
562 // swap buttons
562 // swap buttons
563 $(self.previewButton).hide();
563 $(self.previewButton).hide();
564 $(self.editButton).show();
564 $(self.editButton).show();
565
565
566 // unlock buttons
566 // unlock buttons
567 self.setActionButtonsDisabled(false);
567 self.setActionButtonsDisabled(false);
568 };
568 };
569
569
570 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
570 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
571 excludeCancelBtn = excludeCancelBtn || false;
571 excludeCancelBtn = excludeCancelBtn || false;
572 submitEvent = submitEvent || false;
572 submitEvent = submitEvent || false;
573
573
574 $(this.editButton).prop('disabled', state);
574 $(this.editButton).prop('disabled', state);
575 $(this.previewButton).prop('disabled', state);
575 $(this.previewButton).prop('disabled', state);
576
576
577 if (!excludeCancelBtn) {
577 if (!excludeCancelBtn) {
578 $(this.cancelButton).prop('disabled', state);
578 $(this.cancelButton).prop('disabled', state);
579 }
579 }
580
580
581 var submitState = state;
581 var submitState = state;
582 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
582 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
583 // if the value of commit review status is set, we allow
583 // if the value of commit review status is set, we allow
584 // submit button, but only on Main form, lineNo means inline
584 // submit button, but only on Main form, lineNo means inline
585 submitState = false
585 submitState = false
586 }
586 }
587 $(this.submitButton).prop('disabled', submitState);
587 $(this.submitButton).prop('disabled', submitState);
588 if (submitEvent) {
588 if (submitEvent) {
589 $(this.submitButton).val(_TM['Submitting...']);
589 $(this.submitButton).val(_gettext('Submitting...'));
590 } else {
590 } else {
591 $(this.submitButton).val(this.submitButtonText);
591 $(this.submitButton).val(this.submitButtonText);
592 }
592 }
593
593
594 };
594 };
595
595
596 // lock preview/edit/submit buttons on load, but exclude cancel button
596 // lock preview/edit/submit buttons on load, but exclude cancel button
597 var excludeCancelBtn = true;
597 var excludeCancelBtn = true;
598 this.setActionButtonsDisabled(true, excludeCancelBtn);
598 this.setActionButtonsDisabled(true, excludeCancelBtn);
599
599
600 // anonymous users don't have access to initialized CM instance
600 // anonymous users don't have access to initialized CM instance
601 if (this.cm !== undefined){
601 if (this.cm !== undefined){
602 this.cm.on('change', function(cMirror) {
602 this.cm.on('change', function(cMirror) {
603 if (cMirror.getValue() === "") {
603 if (cMirror.getValue() === "") {
604 self.setActionButtonsDisabled(true, excludeCancelBtn)
604 self.setActionButtonsDisabled(true, excludeCancelBtn)
605 } else {
605 } else {
606 self.setActionButtonsDisabled(false, excludeCancelBtn)
606 self.setActionButtonsDisabled(false, excludeCancelBtn)
607 }
607 }
608 });
608 });
609 }
609 }
610
610
611 $(this.editButton).on('click', function(e) {
611 $(this.editButton).on('click', function(e) {
612 e.preventDefault();
612 e.preventDefault();
613
613
614 $(self.previewButton).show();
614 $(self.previewButton).show();
615 $(self.previewContainer).hide();
615 $(self.previewContainer).hide();
616 $(self.editButton).hide();
616 $(self.editButton).hide();
617 $(self.editContainer).show();
617 $(self.editContainer).show();
618
618
619 });
619 });
620
620
621 $(this.previewButton).on('click', function(e) {
621 $(this.previewButton).on('click', function(e) {
622 e.preventDefault();
622 e.preventDefault();
623 var text = self.cm.getValue();
623 var text = self.cm.getValue();
624
624
625 if (text === "") {
625 if (text === "") {
626 return;
626 return;
627 }
627 }
628
628
629 var postData = {
629 var postData = {
630 'text': text,
630 'text': text,
631 'renderer': DEFAULT_RENDERER,
631 'renderer': DEFAULT_RENDERER,
632 'csrf_token': CSRF_TOKEN
632 'csrf_token': CSRF_TOKEN
633 };
633 };
634
634
635 // lock ALL buttons on preview
635 // lock ALL buttons on preview
636 self.setActionButtonsDisabled(true);
636 self.setActionButtonsDisabled(true);
637
637
638 $(self.previewBoxSelector).addClass('unloaded');
638 $(self.previewBoxSelector).addClass('unloaded');
639 $(self.previewBoxSelector).html(_TM['Loading ...']);
639 $(self.previewBoxSelector).html(_gettext('Loading ...'));
640 $(self.editContainer).hide();
640 $(self.editContainer).hide();
641 $(self.previewContainer).show();
641 $(self.previewContainer).show();
642
642
643 // by default we reset state of comment preserving the text
643 // by default we reset state of comment preserving the text
644 var previewFailCallback = function(){
644 var previewFailCallback = function(){
645 self.resetCommentFormState(text)
645 self.resetCommentFormState(text)
646 };
646 };
647 self.submitAjaxPOST(
647 self.submitAjaxPOST(
648 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
648 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
649
649
650 });
650 });
651
651
652 $(this.submitForm).submit(function(e) {
652 $(this.submitForm).submit(function(e) {
653 e.preventDefault();
653 e.preventDefault();
654 var allowedToSubmit = self.isAllowedToSubmit();
654 var allowedToSubmit = self.isAllowedToSubmit();
655 if (!allowedToSubmit){
655 if (!allowedToSubmit){
656 return false;
656 return false;
657 }
657 }
658 self.handleFormSubmit();
658 self.handleFormSubmit();
659 });
659 });
660
660
661 }
661 }
662
662
663 return CommentForm;
663 return CommentForm;
664 })();
664 })();
@@ -1,309 +1,309 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
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 Affero General Public License
12 // # You should have received a copy of the GNU Affero 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 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 * Search file list
20 * Search file list
21 */
21 */
22 // global reference to file-node filter
22 // global reference to file-node filter
23 var _NODEFILTER = {};
23 var _NODEFILTER = {};
24
24
25 var fileBrowserListeners = function(node_list_url, url_base){
25 var fileBrowserListeners = function(node_list_url, url_base){
26 var n_filter = $('#node_filter').get(0);
26 var n_filter = $('#node_filter').get(0);
27
27
28 _NODEFILTER.filterTimeout = null;
28 _NODEFILTER.filterTimeout = null;
29 var nodes = null;
29 var nodes = null;
30
30
31 _NODEFILTER.fetchNodes = function(callback) {
31 _NODEFILTER.fetchNodes = function(callback) {
32 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': true}})
32 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': true}})
33 .done(function(data){
33 .done(function(data){
34 nodes = data.nodes;
34 nodes = data.nodes;
35 if (callback) {
35 if (callback) {
36 callback();
36 callback();
37 }
37 }
38 })
38 })
39 .fail(function(data){
39 .fail(function(data){
40 console.log('failed to load');
40 console.log('failed to load');
41 });
41 });
42 };
42 };
43
43
44 _NODEFILTER.fetchNodesCallback = function() {
44 _NODEFILTER.fetchNodesCallback = function() {
45 $('#node_filter_box_loading').hide();
45 $('#node_filter_box_loading').hide();
46 $('#node_filter_box').removeClass('hidden').show();
46 $('#node_filter_box').removeClass('hidden').show();
47 n_filter.focus();
47 n_filter.focus();
48 if ($('#node_filter').hasClass('init')){
48 if ($('#node_filter').hasClass('init')){
49 n_filter.value = '';
49 n_filter.value = '';
50 $('#node_filter').removeClass('init');
50 $('#node_filter').removeClass('init');
51 }
51 }
52 };
52 };
53
53
54 _NODEFILTER.initFilter = function(){
54 _NODEFILTER.initFilter = function(){
55 $('#node_filter_box_loading').removeClass('hidden').show();
55 $('#node_filter_box_loading').removeClass('hidden').show();
56 $('#search_activate_id').hide();
56 $('#search_activate_id').hide();
57 $('#search_deactivate_id').removeClass('hidden').show();
57 $('#search_deactivate_id').removeClass('hidden').show();
58 $('#add_node_id').hide();
58 $('#add_node_id').hide();
59 _NODEFILTER.fetchNodes(_NODEFILTER.fetchNodesCallback);
59 _NODEFILTER.fetchNodes(_NODEFILTER.fetchNodesCallback);
60 };
60 };
61
61
62 _NODEFILTER.resetFilter = function(){
62 _NODEFILTER.resetFilter = function(){
63 $('#node_filter_box_loading').hide();
63 $('#node_filter_box_loading').hide();
64 $('#node_filter_box').hide();
64 $('#node_filter_box').hide();
65 $('#search_activate_id').show();
65 $('#search_activate_id').show();
66 $('#search_deactivate_id').hide();
66 $('#search_deactivate_id').hide();
67 $('#add_node_id').show();
67 $('#add_node_id').show();
68 $('#tbody').show();
68 $('#tbody').show();
69 $('#tbody_filtered').hide();
69 $('#tbody_filtered').hide();
70 $('#node_filter').val('');
70 $('#node_filter').val('');
71 };
71 };
72
72
73 _NODEFILTER.fuzzy_match = function(filepath, query) {
73 _NODEFILTER.fuzzy_match = function(filepath, query) {
74 var highlight = [];
74 var highlight = [];
75 var order = 0;
75 var order = 0;
76 for (var i = 0; i < query.length; i++) {
76 for (var i = 0; i < query.length; i++) {
77 var match_position = filepath.indexOf(query[i]);
77 var match_position = filepath.indexOf(query[i]);
78 if (match_position !== -1) {
78 if (match_position !== -1) {
79 var prev_match_position = highlight[highlight.length-1];
79 var prev_match_position = highlight[highlight.length-1];
80 if (prev_match_position === undefined) {
80 if (prev_match_position === undefined) {
81 highlight.push(match_position);
81 highlight.push(match_position);
82 } else {
82 } else {
83 var current_match_position = prev_match_position + match_position + 1;
83 var current_match_position = prev_match_position + match_position + 1;
84 highlight.push(current_match_position);
84 highlight.push(current_match_position);
85 order = order + current_match_position - prev_match_position;
85 order = order + current_match_position - prev_match_position;
86 }
86 }
87 filepath = filepath.substring(match_position+1);
87 filepath = filepath.substring(match_position+1);
88 } else {
88 } else {
89 return false;
89 return false;
90 }
90 }
91 }
91 }
92 return {'order': order,
92 return {'order': order,
93 'highlight': highlight};
93 'highlight': highlight};
94 };
94 };
95
95
96 _NODEFILTER.sortPredicate = function(a, b) {
96 _NODEFILTER.sortPredicate = function(a, b) {
97 if (a.order < b.order) return -1;
97 if (a.order < b.order) return -1;
98 if (a.order > b.order) return 1;
98 if (a.order > b.order) return 1;
99 if (a.filepath < b.filepath) return -1;
99 if (a.filepath < b.filepath) return -1;
100 if (a.filepath > b.filepath) return 1;
100 if (a.filepath > b.filepath) return 1;
101 return 0;
101 return 0;
102 };
102 };
103
103
104 _NODEFILTER.updateFilter = function(elem, e) {
104 _NODEFILTER.updateFilter = function(elem, e) {
105 return function(){
105 return function(){
106 // Reset timeout
106 // Reset timeout
107 _NODEFILTER.filterTimeout = null;
107 _NODEFILTER.filterTimeout = null;
108 var query = elem.value.toLowerCase();
108 var query = elem.value.toLowerCase();
109 var match = [];
109 var match = [];
110 var matches_max = 20;
110 var matches_max = 20;
111 if (query !== ""){
111 if (query !== ""){
112 var results = [];
112 var results = [];
113 for(var k=0;k<nodes.length;k++){
113 for(var k=0;k<nodes.length;k++){
114 var result = _NODEFILTER.fuzzy_match(
114 var result = _NODEFILTER.fuzzy_match(
115 nodes[k].name.toLowerCase(), query);
115 nodes[k].name.toLowerCase(), query);
116 if (result) {
116 if (result) {
117 result.type = nodes[k].type;
117 result.type = nodes[k].type;
118 result.filepath = nodes[k].name;
118 result.filepath = nodes[k].name;
119 results.push(result);
119 results.push(result);
120 }
120 }
121 }
121 }
122 results = results.sort(_NODEFILTER.sortPredicate);
122 results = results.sort(_NODEFILTER.sortPredicate);
123 var limit = matches_max;
123 var limit = matches_max;
124 if (results.length < matches_max) {
124 if (results.length < matches_max) {
125 limit = results.length;
125 limit = results.length;
126 }
126 }
127 for (var i=0; i<limit; i++){
127 for (var i=0; i<limit; i++){
128 if(query && results.length > 0){
128 if(query && results.length > 0){
129 var n = results[i].filepath;
129 var n = results[i].filepath;
130 var t = results[i].type;
130 var t = results[i].type;
131 var n_hl = n.split("");
131 var n_hl = n.split("");
132 var pos = results[i].highlight;
132 var pos = results[i].highlight;
133 for (var j = 0; j < pos.length; j++) {
133 for (var j = 0; j < pos.length; j++) {
134 n_hl[pos[j]] = "<em>" + n_hl[pos[j]] + "</em>";
134 n_hl[pos[j]] = "<em>" + n_hl[pos[j]] + "</em>";
135 }
135 }
136 n_hl = n_hl.join("");
136 n_hl = n_hl.join("");
137 var new_url = url_base.replace('__FPATH__',n);
137 var new_url = url_base.replace('__FPATH__',n);
138
138
139 var typeObj = {
139 var typeObj = {
140 dir: 'icon-folder browser-dir',
140 dir: 'icon-folder browser-dir',
141 file: 'icon-file browser-file'
141 file: 'icon-file browser-file'
142 };
142 };
143 var typeIcon = '<i class="{0}"></i>'.format(typeObj[t]);
143 var typeIcon = '<i class="{0}"></i>'.format(typeObj[t]);
144 match.push('<tr class="browser-result"><td><a class="browser-{0} pjax-link" href="{1}">{2}{3}</a></td><td colspan="5"></td></tr>'.format(t,new_url,typeIcon, n_hl));
144 match.push('<tr class="browser-result"><td><a class="browser-{0} pjax-link" href="{1}">{2}{3}</a></td><td colspan="5"></td></tr>'.format(t,new_url,typeIcon, n_hl));
145 }
145 }
146 }
146 }
147 if(results.length > limit){
147 if(results.length > limit){
148 var truncated_count = results.length - matches_max;
148 var truncated_count = results.length - matches_max;
149 if (truncated_count === 1) {
149 if (truncated_count === 1) {
150 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _TM['truncated result']));
150 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated result')));
151 } else {
151 } else {
152 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _TM['truncated results']));
152 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated results')));
153 }
153 }
154 }
154 }
155 }
155 }
156 if (query !== ""){
156 if (query !== ""){
157 $('#tbody').hide();
157 $('#tbody').hide();
158 $('#tbody_filtered').show();
158 $('#tbody_filtered').show();
159
159
160 if (match.length === 0){
160 if (match.length === 0){
161 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
161 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_gettext('No matching files')));
162 }
162 }
163 $('#tbody_filtered').html(match.join(""));
163 $('#tbody_filtered').html(match.join(""));
164 }
164 }
165 else{
165 else{
166 $('#tbody').show();
166 $('#tbody').show();
167 $('#tbody_filtered').hide();
167 $('#tbody_filtered').hide();
168 }
168 }
169
169
170 };
170 };
171 };
171 };
172
172
173 var scrollDown = function(element){
173 var scrollDown = function(element){
174 var elementBottom = element.offset().top + $(element).outerHeight();
174 var elementBottom = element.offset().top + $(element).outerHeight();
175 var windowBottom = window.innerHeight + $(window).scrollTop();
175 var windowBottom = window.innerHeight + $(window).scrollTop();
176 if (elementBottom > windowBottom) {
176 if (elementBottom > windowBottom) {
177 var offset = elementBottom - window.innerHeight;
177 var offset = elementBottom - window.innerHeight;
178 $('html,body').scrollTop(offset);
178 $('html,body').scrollTop(offset);
179 return false;
179 return false;
180 }
180 }
181 return true;
181 return true;
182 };
182 };
183
183
184 var scrollUp = function(element){
184 var scrollUp = function(element){
185 if (element.offset().top < $(window).scrollTop()) {
185 if (element.offset().top < $(window).scrollTop()) {
186 $('html,body').scrollTop(element.offset().top);
186 $('html,body').scrollTop(element.offset().top);
187 return false;
187 return false;
188 }
188 }
189 return true;
189 return true;
190 };
190 };
191
191
192 $('#filter_activate').click(function() {
192 $('#filter_activate').click(function() {
193 _NODEFILTER.initFilter();
193 _NODEFILTER.initFilter();
194 });
194 });
195
195
196 $('#filter_deactivate').click(function() {
196 $('#filter_deactivate').click(function() {
197 _NODEFILTER.resetFilter();
197 _NODEFILTER.resetFilter();
198 });
198 });
199
199
200 $(n_filter).click(function() {
200 $(n_filter).click(function() {
201 if ($('#node_filter').hasClass('init')){
201 if ($('#node_filter').hasClass('init')){
202 n_filter.value = '';
202 n_filter.value = '';
203 $('#node_filter').removeClass('init');
203 $('#node_filter').removeClass('init');
204 }
204 }
205 });
205 });
206
206
207 $(n_filter).keydown(function(e) {
207 $(n_filter).keydown(function(e) {
208 if (e.keyCode === 40){ // Down
208 if (e.keyCode === 40){ // Down
209 if ($('.browser-highlight').length === 0){
209 if ($('.browser-highlight').length === 0){
210 $('.browser-result').first().addClass('browser-highlight');
210 $('.browser-result').first().addClass('browser-highlight');
211 } else {
211 } else {
212 var next = $('.browser-highlight').next();
212 var next = $('.browser-highlight').next();
213 if (next.length !== 0) {
213 if (next.length !== 0) {
214 $('.browser-highlight').removeClass('browser-highlight');
214 $('.browser-highlight').removeClass('browser-highlight');
215 next.addClass('browser-highlight');
215 next.addClass('browser-highlight');
216 }
216 }
217 }
217 }
218 scrollDown($('.browser-highlight'));
218 scrollDown($('.browser-highlight'));
219 }
219 }
220 if (e.keyCode === 38){ // Up
220 if (e.keyCode === 38){ // Up
221 e.preventDefault();
221 e.preventDefault();
222 if ($('.browser-highlight').length !== 0){
222 if ($('.browser-highlight').length !== 0){
223 var next = $('.browser-highlight').prev();
223 var next = $('.browser-highlight').prev();
224 if (next.length !== 0) {
224 if (next.length !== 0) {
225 $('.browser-highlight').removeClass('browser-highlight');
225 $('.browser-highlight').removeClass('browser-highlight');
226 next.addClass('browser-highlight');
226 next.addClass('browser-highlight');
227 }
227 }
228 }
228 }
229 scrollUp($('.browser-highlight'));
229 scrollUp($('.browser-highlight'));
230 }
230 }
231 if (e.keyCode === 13){ // Enter
231 if (e.keyCode === 13){ // Enter
232 if ($('.browser-highlight').length !== 0){
232 if ($('.browser-highlight').length !== 0){
233 var url = $('.browser-highlight').find('.pjax-link').attr('href');
233 var url = $('.browser-highlight').find('.pjax-link').attr('href');
234 $.pjax({url: url, container: '#pjax-container', timeout: pjaxTimeout});
234 $.pjax({url: url, container: '#pjax-container', timeout: pjaxTimeout});
235 }
235 }
236 }
236 }
237 if (e.keyCode === 27){ // Esc
237 if (e.keyCode === 27){ // Esc
238 _NODEFILTER.resetFilter();
238 _NODEFILTER.resetFilter();
239 $('html,body').scrollTop(0);
239 $('html,body').scrollTop(0);
240 }
240 }
241 });
241 });
242 var capture_keys = [40, 38, 39, 37, 13, 27];
242 var capture_keys = [40, 38, 39, 37, 13, 27];
243 $(n_filter).keyup(function(e) {
243 $(n_filter).keyup(function(e) {
244 if ($.inArray(e.keyCode, capture_keys) === -1){
244 if ($.inArray(e.keyCode, capture_keys) === -1){
245 clearTimeout(_NODEFILTER.filterTimeout);
245 clearTimeout(_NODEFILTER.filterTimeout);
246 _NODEFILTER.filterTimeout = setTimeout(_NODEFILTER.updateFilter(n_filter, e),200);
246 _NODEFILTER.filterTimeout = setTimeout(_NODEFILTER.updateFilter(n_filter, e),200);
247 }
247 }
248 });
248 });
249 };
249 };
250
250
251 var getIdentNode = function(n){
251 var getIdentNode = function(n){
252 // iterate through nodes until matched interesting node
252 // iterate through nodes until matched interesting node
253 if (typeof n === 'undefined'){
253 if (typeof n === 'undefined'){
254 return -1;
254 return -1;
255 }
255 }
256 if(typeof n.id !== "undefined" && n.id.match('L[0-9]+')){
256 if(typeof n.id !== "undefined" && n.id.match('L[0-9]+')){
257 return n;
257 return n;
258 }
258 }
259 else{
259 else{
260 return getIdentNode(n.parentNode);
260 return getIdentNode(n.parentNode);
261 }
261 }
262 };
262 };
263
263
264 var getSelectionLink = function(e) {
264 var getSelectionLink = function(e) {
265 // get selection from start/to nodes
265 // get selection from start/to nodes
266 if (typeof window.getSelection !== "undefined") {
266 if (typeof window.getSelection !== "undefined") {
267 s = window.getSelection();
267 s = window.getSelection();
268
268
269 from = getIdentNode(s.anchorNode);
269 from = getIdentNode(s.anchorNode);
270 till = getIdentNode(s.focusNode);
270 till = getIdentNode(s.focusNode);
271
271
272 f_int = parseInt(from.id.replace('L',''));
272 f_int = parseInt(from.id.replace('L',''));
273 t_int = parseInt(till.id.replace('L',''));
273 t_int = parseInt(till.id.replace('L',''));
274
274
275 if (f_int > t_int){
275 if (f_int > t_int){
276 // highlight from bottom
276 // highlight from bottom
277 offset = -35;
277 offset = -35;
278 ranges = [t_int,f_int];
278 ranges = [t_int,f_int];
279 }
279 }
280 else{
280 else{
281 // highligth from top
281 // highligth from top
282 offset = 35;
282 offset = 35;
283 ranges = [f_int,t_int];
283 ranges = [f_int,t_int];
284 }
284 }
285 // if we select more than 2 lines
285 // if we select more than 2 lines
286 if (ranges[0] !== ranges[1]){
286 if (ranges[0] !== ranges[1]){
287 if($('#linktt').length === 0){
287 if($('#linktt').length === 0){
288 hl_div = document.createElement('div');
288 hl_div = document.createElement('div');
289 hl_div.id = 'linktt';
289 hl_div.id = 'linktt';
290 }
290 }
291 hl_div.innerHTML = '';
291 hl_div.innerHTML = '';
292
292
293 anchor = '#L'+ranges[0]+'-'+ranges[1];
293 anchor = '#L'+ranges[0]+'-'+ranges[1];
294 var link = document.createElement('a');
294 var link = document.createElement('a');
295 link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
295 link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
296 link.innerHTML = _TM['Selection link'];
296 link.innerHTML = _gettext('Selection link');
297 hl_div.appendChild(link);
297 hl_div.appendChild(link);
298 $('#codeblock').append(hl_div);
298 $('#codeblock').append(hl_div);
299
299
300 var xy = $(till).offset();
300 var xy = $(till).offset();
301 $('#linktt').addClass('hl-tip-box tip-box');
301 $('#linktt').addClass('hl-tip-box tip-box');
302 $('#linktt').offset({top: xy.top + offset, left: xy.left});
302 $('#linktt').offset({top: xy.top + offset, left: xy.left});
303 $('#linktt').css('visibility','visible');
303 $('#linktt').css('visibility','visible');
304 }
304 }
305 else{
305 else{
306 $('#linktt').css('visibility','hidden');
306 $('#linktt').css('visibility','hidden');
307 }
307 }
308 }
308 }
309 };
309 };
@@ -1,74 +1,74 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
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 Affero General Public License
12 // # You should have received a copy of the GNU Affero 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 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var onSuccessFollow = function(target){
19 var onSuccessFollow = function(target){
20 var f = $(target);
20 var f = $(target);
21 var f_cnt = $('#current_followers_count');
21 var f_cnt = $('#current_followers_count');
22
22
23 if(f.hasClass('follow')){
23 if(f.hasClass('follow')){
24 f.removeClass('follow');
24 f.removeClass('follow');
25 f.addClass('following');
25 f.addClass('following');
26 f.attr('title', _TM['Stop following this repository']);
26 f.attr('title', _gettext('Stop following this repository'));
27 $(f).html(_TM['Unfollow']);
27 $(f).html(_gettext('Unfollow'));
28 if(f_cnt.length){
28 if(f_cnt.length){
29 var cnt = Number(f_cnt.html())+1;
29 var cnt = Number(f_cnt.html())+1;
30 f_cnt.html(cnt);
30 f_cnt.html(cnt);
31 }
31 }
32 }
32 }
33 else{
33 else{
34 f.removeClass('following');
34 f.removeClass('following');
35 f.addClass('follow');
35 f.addClass('follow');
36 f.attr('title', _TM['Start following this repository']);
36 f.attr('title', _gettext('Start following this repository'));
37 $(f).html(_TM['Follow']);
37 $(f).html(_gettext('Follow'));
38 if(f_cnt.length){
38 if(f_cnt.length){
39 var cnt = Number(f_cnt.html())-1;
39 var cnt = Number(f_cnt.html())-1;
40 f_cnt.html(cnt);
40 f_cnt.html(cnt);
41 }
41 }
42 }
42 }
43 };
43 };
44
44
45 // TODO:: check if the function is needed. 0 usage found
45 // TODO:: check if the function is needed. 0 usage found
46 var toggleFollowingUser = function(target,follows_user_id,token,user_id){
46 var toggleFollowingUser = function(target,follows_user_id,token,user_id){
47 var args = {
47 var args = {
48 'follows_user_id': follows_user_id,
48 'follows_user_id': follows_user_id,
49 'auth_token': token,
49 'auth_token': token,
50 'csrf_token': CSRF_TOKEN
50 'csrf_token': CSRF_TOKEN
51 };
51 };
52 if(user_id != undefined){
52 if(user_id != undefined){
53 args.user_id = user_id
53 args.user_id = user_id
54 }
54 }
55 ajaxPOST(pyroutes.url('toggle_following'), args, function(){
55 ajaxPOST(pyroutes.url('toggle_following'), args, function(){
56 onSuccessFollow(target);
56 onSuccessFollow(target);
57 });
57 });
58 return false;
58 return false;
59 };
59 };
60
60
61 var toggleFollowingRepo = function(target,follows_repo_id,token,user_id){
61 var toggleFollowingRepo = function(target,follows_repo_id,token,user_id){
62 var args = {
62 var args = {
63 'follows_repo_id': follows_repo_id,
63 'follows_repo_id': follows_repo_id,
64 'auth_token': token,
64 'auth_token': token,
65 'csrf_token': CSRF_TOKEN
65 'csrf_token': CSRF_TOKEN
66 };
66 };
67 if(user_id != undefined){
67 if(user_id != undefined){
68 args.user_id = user_id
68 args.user_id = user_id
69 }
69 }
70 ajaxPOST(pyroutes.url('toggle_following'), args, function(){
70 ajaxPOST(pyroutes.url('toggle_following'), args, function(){
71 onSuccessFollow(target);
71 onSuccessFollow(target);
72 });
72 });
73 return false;
73 return false;
74 };
74 };
@@ -1,126 +1,126 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
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 Affero General Public License
12 // # You should have received a copy of the GNU Affero 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 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 * TOOLTIP IMPL.
20 * TOOLTIP IMPL.
21 */
21 */
22
22
23 var TTIP = {};
23 var TTIP = {};
24
24
25 TTIP.main = {
25 TTIP.main = {
26 offset: [15,15],
26 offset: [15,15],
27 maxWidth: 600,
27 maxWidth: 600,
28
28
29 set_listeners: function(tt){
29 set_listeners: function(tt){
30 $(tt).mouseover(tt, yt.show_tip);
30 $(tt).mouseover(tt, yt.show_tip);
31 $(tt).mousemove(tt, yt.move_tip);
31 $(tt).mousemove(tt, yt.move_tip);
32 $(tt).mouseout(tt, yt.close_tip);
32 $(tt).mouseout(tt, yt.close_tip);
33 },
33 },
34
34
35 init: function(){
35 init: function(){
36 $('#tip-box').remove();
36 $('#tip-box').remove();
37 yt.tipBox = document.createElement('div');
37 yt.tipBox = document.createElement('div');
38 document.body.appendChild(yt.tipBox);
38 document.body.appendChild(yt.tipBox);
39 yt.tipBox.id = 'tip-box';
39 yt.tipBox.id = 'tip-box';
40
40
41 $(yt.tipBox).hide();
41 $(yt.tipBox).hide();
42 $(yt.tipBox).css('position', 'absolute');
42 $(yt.tipBox).css('position', 'absolute');
43 if(yt.maxWidth !== null){
43 if(yt.maxWidth !== null){
44 $(yt.tipBox).css('max-width', yt.maxWidth+'px');
44 $(yt.tipBox).css('max-width', yt.maxWidth+'px');
45 }
45 }
46
46
47 var tooltips = $('.tooltip');
47 var tooltips = $('.tooltip');
48 var ttLen = tooltips.length;
48 var ttLen = tooltips.length;
49
49
50 for(i=0;i<ttLen;i++){
50 for(i=0;i<ttLen;i++){
51 yt.set_listeners(tooltips[i]);
51 yt.set_listeners(tooltips[i]);
52 }
52 }
53 },
53 },
54
54
55 show_tip: function(e, el){
55 show_tip: function(e, el){
56 e.stopPropagation();
56 e.stopPropagation();
57 e.preventDefault();
57 e.preventDefault();
58 var el = e.data || el;
58 var el = e.data || el;
59 if(el.tagName.toLowerCase() === 'img'){
59 if(el.tagName.toLowerCase() === 'img'){
60 yt.tipText = el.alt ? el.alt : '';
60 yt.tipText = el.alt ? el.alt : '';
61 } else {
61 } else {
62 yt.tipText = el.title ? el.title : '';
62 yt.tipText = el.title ? el.title : '';
63 }
63 }
64
64
65 if(yt.tipText !== ''){
65 if(yt.tipText !== ''){
66 // save org title
66 // save org title
67 $(el).attr('tt_title', yt.tipText);
67 $(el).attr('tt_title', yt.tipText);
68 // reset title to not show org tooltips
68 // reset title to not show org tooltips
69 $(el).attr('title', '');
69 $(el).attr('title', '');
70
70
71 yt.tipBox.innerHTML = yt.tipText;
71 yt.tipBox.innerHTML = yt.tipText;
72 $(yt.tipBox).show();
72 $(yt.tipBox).show();
73 }
73 }
74 },
74 },
75
75
76 move_tip: function(e, el){
76 move_tip: function(e, el){
77 e.stopPropagation();
77 e.stopPropagation();
78 e.preventDefault();
78 e.preventDefault();
79 var el = e.data || el;
79 var el = e.data || el;
80 var movePos = [e.pageX, e.pageY];
80 var movePos = [e.pageX, e.pageY];
81 $(yt.tipBox).css('top', (movePos[1] + yt.offset[1]) + 'px')
81 $(yt.tipBox).css('top', (movePos[1] + yt.offset[1]) + 'px')
82 $(yt.tipBox).css('left', (movePos[0] + yt.offset[0]) + 'px')
82 $(yt.tipBox).css('left', (movePos[0] + yt.offset[0]) + 'px')
83 },
83 },
84
84
85 close_tip: function(e, el){
85 close_tip: function(e, el){
86 e.stopPropagation();
86 e.stopPropagation();
87 e.preventDefault();
87 e.preventDefault();
88 var el = e.data || el;
88 var el = e.data || el;
89 $(yt.tipBox).hide();
89 $(yt.tipBox).hide();
90 $(el).attr('title', $(el).attr('tt_title'));
90 $(el).attr('title', $(el).attr('tt_title'));
91 $('#tip-box').hide();
91 $('#tip-box').hide();
92 }
92 }
93 };
93 };
94
94
95 /**
95 /**
96 * tooltip activate
96 * tooltip activate
97 */
97 */
98 var tooltip_activate = function(){
98 var tooltip_activate = function(){
99 yt = TTIP.main;
99 yt = TTIP.main;
100 $(document).ready(yt.init);
100 $(document).ready(yt.init);
101 };
101 };
102
102
103 /**
103 /**
104 * show changeset tooltip
104 * show changeset tooltip
105 */
105 */
106 var show_changeset_tooltip = function(){
106 var show_changeset_tooltip = function(){
107 $('.lazy-cs').mouseover(function(e) {
107 $('.lazy-cs').mouseover(function(e) {
108 var target = e.currentTarget;
108 var target = e.currentTarget;
109 var rid = $(target).attr('raw_id');
109 var rid = $(target).attr('raw_id');
110 var repo_name = $(target).attr('repo_name');
110 var repo_name = $(target).attr('repo_name');
111 var ttid = 'tt-'+rid;
111 var ttid = 'tt-'+rid;
112 var success = function(o){
112 var success = function(o){
113 $(target).addClass('tooltip')
113 $(target).addClass('tooltip')
114 $(target).attr('title', o['message']);
114 $(target).attr('title', o['message']);
115 TTIP.main.show_tip(e, target);
115 TTIP.main.show_tip(e, target);
116 }
116 }
117 if(rid && !$(target).hasClass('tooltip')){
117 if(rid && !$(target).hasClass('tooltip')){
118 $(target).attr('id', ttid);
118 $(target).attr('id', ttid);
119 $(target).attr('title', _TM['loading ...']);
119 $(target).attr('title', _gettext('loading ...'));
120 TTIP.main.set_listeners(target);
120 TTIP.main.set_listeners(target);
121 TTIP.main.show_tip(e, target);
121 TTIP.main.show_tip(e, target);
122 var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});
122 var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});
123 ajaxGET(url, success);
123 ajaxGET(url, success);
124 }
124 }
125 });
125 });
126 };
126 };
@@ -1,116 +1,116 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Authentication Settings')}
5 ${_('Authentication Settings')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}}
7 &middot; ${h.branding(c.rhodecode_name)}}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Authentication Plugins')}
14 ${_('Authentication Plugins')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22
22
23 <div class="box">
23 <div class="box">
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27
27
28 <div class='sidebar-col-wrapper'>
28 <div class='sidebar-col-wrapper'>
29
29
30 <div class="sidebar">
30 <div class="sidebar">
31 <ul class="nav nav-pills nav-stacked">
31 <ul class="nav nav-pills nav-stacked">
32 % for item in resource.get_root().get_nav_list():
32 % for item in resource.get_root().get_nav_list():
33 <li ${'class=active' if item == resource else ''}>
33 <li ${'class=active' if item == resource else ''}>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
35 </li>
35 </li>
36 % endfor
36 % endfor
37 </ul>
37 </ul>
38 </div>
38 </div>
39
39
40 <div class="main-content-full-width">
40 <div class="main-content-full-width">
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
42 <div class="form">
42 <div class="form">
43
43
44 <div class="panel panel-default">
44 <div class="panel panel-default">
45
45
46 <div class="panel-heading">
46 <div class="panel-heading">
47 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
47 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
48 </div>
48 </div>
49
49
50 <div class="fields panel-body">
50 <div class="fields panel-body">
51
51
52 <div class="field">
52 <div class="field">
53 <div class="label">${_("Enabled Plugins")}</div>
53 <div class="label">${_("Enabled Plugins")}</div>
54 <div class="textarea text-area editor">
54 <div class="textarea text-area editor">
55 ${h.textarea('auth_plugins',cols=23,rows=5,class_="medium")}
55 ${h.textarea('auth_plugins',cols=23,rows=5,class_="medium")}
56 </div>
56 </div>
57 <p class="help-block">
57 <p class="help-block">
58 ${_('Add a list of plugins, separated by commas. '
58 ${_('Add a list of plugins, separated by commas. '
59 'The order of the plugins is also the order in which '
59 'The order of the plugins is also the order in which '
60 'RhodeCode Enterprise will try to authenticate a user.')}
60 'RhodeCode Enterprise will try to authenticate a user.')}
61 </p>
61 </p>
62 </div>
62 </div>
63
63
64 <div class="field">
64 <div class="field">
65 <div class="label">${_('Available Built-in Plugins')}</div>
65 <div class="label">${_('Available Built-in Plugins')}</div>
66 <ul class="auth_plugins">
66 <ul class="auth_plugins">
67 %for plugin in available_plugins:
67 %for plugin in available_plugins:
68 <li>
68 <li>
69 <div class="auth_buttons">
69 <div class="auth_buttons">
70 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
70 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
71 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
71 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
72 </span>
72 </span>
73 ${plugin.get_display_name()} (${plugin.get_id()})
73 ${plugin.get_display_name()} (${plugin.get_id()})
74 </div>
74 </div>
75 </li>
75 </li>
76 %endfor
76 %endfor
77 </ul>
77 </ul>
78 </div>
78 </div>
79
79
80 <div class="buttons">
80 <div class="buttons">
81 ${h.submit('save',_('Save'),class_="btn")}
81 ${h.submit('save',_('Save'),class_="btn")}
82 </div>
82 </div>
83 </div>
83 </div>
84 </div>
84 </div>
85 </div>
85 </div>
86 ${h.end_form()}
86 ${h.end_form()}
87 </div>
87 </div>
88 </div>
88 </div>
89 </div>
89 </div>
90
90
91 <script>
91 <script>
92 $('.toggle-plugin').click(function(e){
92 $('.toggle-plugin').click(function(e){
93 var auth_plugins_input = $('#auth_plugins');
93 var auth_plugins_input = $('#auth_plugins');
94 var notEmpty = function(element, index, array) {
94 var notEmpty = function(element, index, array) {
95 return (element != "");
95 return (element != "");
96 }
96 }
97 var elems = auth_plugins_input.val().split(',').filter(notEmpty);
97 var elems = auth_plugins_input.val().split(',').filter(notEmpty);
98 var cur_button = e.currentTarget;
98 var cur_button = e.currentTarget;
99 var plugin_id = $(cur_button).attr('plugin_id');
99 var plugin_id = $(cur_button).attr('plugin_id');
100 if($(cur_button).hasClass('btn-success')){
100 if($(cur_button).hasClass('btn-success')){
101 elems.splice(elems.indexOf(plugin_id), 1);
101 elems.splice(elems.indexOf(plugin_id), 1);
102 auth_plugins_input.val(elems.join(','));
102 auth_plugins_input.val(elems.join(','));
103 $(cur_button).removeClass('btn-success');
103 $(cur_button).removeClass('btn-success');
104 cur_button.innerHTML = _TM['disabled'];
104 cur_button.innerHTML = _gettext('disabled');
105 }
105 }
106 else{
106 else{
107 if(elems.indexOf(plugin_id) == -1){
107 if(elems.indexOf(plugin_id) == -1){
108 elems.push(plugin_id);
108 elems.push(plugin_id);
109 }
109 }
110 auth_plugins_input.val(elems.join(','));
110 auth_plugins_input.val(elems.join(','));
111 $(cur_button).addClass('btn-success');
111 $(cur_button).addClass('btn-success');
112 cur_button.innerHTML = _TM['enabled'];
112 cur_button.innerHTML = _gettext('enabled');
113 }
113 }
114 });
114 });
115 </script>
115 </script>
116 </%def>
116 </%def>
@@ -1,141 +1,141 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Users administration')}
5 ${_('Users administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_count">0</span>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_count">0</span>
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22
22
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 <ul class="links">
25 <ul class="links">
26 <li>
26 <li>
27 <a href="${h.url('new_user')}" class="btn btn-small btn-success">${_(u'Add User')}</a>
27 <a href="${h.url('new_user')}" class="btn btn-small btn-success">${_(u'Add User')}</a>
28 </li>
28 </li>
29 </ul>
29 </ul>
30 </div>
30 </div>
31
31
32 <div id="repos_list_wrap">
32 <div id="repos_list_wrap">
33 <table id="user_list_table" class="display"></table>
33 <table id="user_list_table" class="display"></table>
34 </div>
34 </div>
35 </div>
35 </div>
36
36
37 <script>
37 <script>
38 $(document).ready(function() {
38 $(document).ready(function() {
39
39
40 var get_datatable_count = function(){
40 var get_datatable_count = function(){
41 var datatable = $('#user_list_table').dataTable();
41 var datatable = $('#user_list_table').dataTable();
42 var api = datatable.api();
42 var api = datatable.api();
43 var total = api.page.info().recordsDisplay;
43 var total = api.page.info().recordsDisplay;
44 var active = datatable.fnGetFilteredData();
44 var active = datatable.fnGetFilteredData();
45
45 var _text = _gettext("{0} active out of {1} users").format(active, total);
46 $('#user_count').text(_TM["{0} active out of {1} users"].format(active, total));
46 $('#user_count').text(_text);
47 };
47 };
48
48
49 // custom filter that filters by username OR email
49 // custom filter that filters by username OR email
50 $.fn.dataTable.ext.search.push(
50 $.fn.dataTable.ext.search.push(
51 function( settings, data, dataIndex ) {
51 function( settings, data, dataIndex ) {
52 var query = $('#q_filter').val();
52 var query = $('#q_filter').val();
53 var username = data[1];
53 var username = data[1];
54 var email = data[2];
54 var email = data[2];
55 var first_name = data[3];
55 var first_name = data[3];
56 var last_name = data[4];
56 var last_name = data[4];
57
57
58 var query_str = username + " " +
58 var query_str = username + " " +
59 email + " " +
59 email + " " +
60 first_name + " " +
60 first_name + " " +
61 last_name;
61 last_name;
62 if((query_str).indexOf(query) !== -1){
62 if((query_str).indexOf(query) !== -1){
63 return true;
63 return true;
64 }
64 }
65 return false;
65 return false;
66 }
66 }
67 );
67 );
68 // filtered data plugin
68 // filtered data plugin
69 $.fn.dataTableExt.oApi.fnGetFilteredData = function ( oSettings ) {
69 $.fn.dataTableExt.oApi.fnGetFilteredData = function ( oSettings ) {
70 var res = [];
70 var res = [];
71 for ( var i=0, iLen=oSettings.fnRecordsDisplay() ; i<iLen ; i++ ) {
71 for ( var i=0, iLen=oSettings.fnRecordsDisplay() ; i<iLen ; i++ ) {
72 var record = oSettings.aoData[i]._aData;
72 var record = oSettings.aoData[i]._aData;
73 if(record['active_raw']){
73 if(record['active_raw']){
74 res.push(record);
74 res.push(record);
75 }
75 }
76 }
76 }
77 return res.length;
77 return res.length;
78 };
78 };
79
79
80 // user list
80 // user list
81 $('#user_list_table').DataTable({
81 $('#user_list_table').DataTable({
82 data: ${c.data|n},
82 data: ${c.data|n},
83 dom: 'rtp',
83 dom: 'rtp',
84 pageLength: ${c.visual.admin_grid_items},
84 pageLength: ${c.visual.admin_grid_items},
85 order: [[ 1, "asc" ]],
85 order: [[ 1, "asc" ]],
86 columns: [
86 columns: [
87 { data: {"_": "gravatar"}, className: "td-gravatar" },
87 { data: {"_": "gravatar"}, className: "td-gravatar" },
88 { data: {"_": "username",
88 { data: {"_": "username",
89 "sort": "username_raw"}, title: "${_('Username')}", className: "td-user" },
89 "sort": "username_raw"}, title: "${_('Username')}", className: "td-user" },
90 { data: {"_": "email",
90 { data: {"_": "email",
91 "sort": "email"}, title: "${_('Email')}", className: "td-email" },
91 "sort": "email"}, title: "${_('Email')}", className: "td-email" },
92 { data: {"_": "first_name",
92 { data: {"_": "first_name",
93 "sort": "first_name"}, title: "${_('First Name')}", className: "td-user" },
93 "sort": "first_name"}, title: "${_('First Name')}", className: "td-user" },
94 { data: {"_": "last_name",
94 { data: {"_": "last_name",
95 "sort": "last_name"}, title: "${_('Last Name')}", className: "td-user" },
95 "sort": "last_name"}, title: "${_('Last Name')}", className: "td-user" },
96 { data: {"_": "last_login",
96 { data: {"_": "last_login",
97 "sort": "last_login_raw",
97 "sort": "last_login_raw",
98 "type": Number}, title: "${_('Last login')}", className: "td-time" },
98 "type": Number}, title: "${_('Last login')}", className: "td-time" },
99 { data: {"_": "active",
99 { data: {"_": "active",
100 "sort": "active_raw"}, title: "${_('Active')}", className: "td-active" },
100 "sort": "active_raw"}, title: "${_('Active')}", className: "td-active" },
101 { data: {"_": "admin",
101 { data: {"_": "admin",
102 "sort": "admin_raw"}, title: "${_('Admin')}", className: "td-admin" },
102 "sort": "admin_raw"}, title: "${_('Admin')}", className: "td-admin" },
103 { data: {"_": "extern_type",
103 { data: {"_": "extern_type",
104 "sort": "extern_type"}, title: "${_('Authentication type')}", className: "td-type" },
104 "sort": "extern_type"}, title: "${_('Authentication type')}", className: "td-type" },
105 { data: {"_": "action",
105 { data: {"_": "action",
106 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
106 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
107 ],
107 ],
108 language: {
108 language: {
109 paginate: DEFAULT_GRID_PAGINATION
109 paginate: DEFAULT_GRID_PAGINATION
110 },
110 },
111 "initComplete": function( settings, json ) {
111 "initComplete": function( settings, json ) {
112 get_datatable_count();
112 get_datatable_count();
113 tooltip_activate();
113 tooltip_activate();
114 },
114 },
115 "createdRow": function ( row, data, index ) {
115 "createdRow": function ( row, data, index ) {
116 if (!data['active_raw']){
116 if (!data['active_raw']){
117 $(row).addClass('closed')
117 $(row).addClass('closed')
118 }
118 }
119 }
119 }
120 });
120 });
121
121
122 // update the counter when doing search
122 // update the counter when doing search
123 $('#user_list_table').on( 'search.dt', function (e,settings) {
123 $('#user_list_table').on( 'search.dt', function (e,settings) {
124 get_datatable_count();
124 get_datatable_count();
125 });
125 });
126
126
127 // filter, filter both grids
127 // filter, filter both grids
128 $('#q_filter').on( 'keyup', function () {
128 $('#q_filter').on( 'keyup', function () {
129 var user_api = $('#user_list_table').dataTable().api();
129 var user_api = $('#user_list_table').dataTable().api();
130 user_api
130 user_api
131 .draw();
131 .draw();
132 });
132 });
133
133
134 // refilter table if page load via back button
134 // refilter table if page load via back button
135 $("#q_filter").trigger('keyup');
135 $("#q_filter").trigger('keyup');
136
136
137 });
137 });
138
138
139 </script>
139 </script>
140
140
141 </%def>
141 </%def>
@@ -1,415 +1,415 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4
4
5 <%def name="title()">
5 <%def name="title()">
6 ${_('%s Changelog') % c.repo_name}
6 ${_('%s Changelog') % c.repo_name}
7 %if c.changelog_for_path:
7 %if c.changelog_for_path:
8 /${c.changelog_for_path}
8 /${c.changelog_for_path}
9 %endif
9 %endif
10 %if c.rhodecode_name:
10 %if c.rhodecode_name:
11 &middot; ${h.branding(c.rhodecode_name)}
11 &middot; ${h.branding(c.rhodecode_name)}
12 %endif
12 %endif
13 </%def>
13 </%def>
14
14
15 <%def name="breadcrumbs_links()">
15 <%def name="breadcrumbs_links()">
16 %if c.changelog_for_path:
16 %if c.changelog_for_path:
17 /${c.changelog_for_path}
17 /${c.changelog_for_path}
18 %endif
18 %endif
19 ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)}
19 ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_nav()">
22 <%def name="menu_bar_nav()">
23 ${self.menu_items(active='repositories')}
23 ${self.menu_items(active='repositories')}
24 </%def>
24 </%def>
25
25
26 <%def name="menu_bar_subnav()">
26 <%def name="menu_bar_subnav()">
27 ${self.repo_menu(active='changelog')}
27 ${self.repo_menu(active='changelog')}
28 </%def>
28 </%def>
29
29
30 <%def name="main()">
30 <%def name="main()">
31
31
32 <div class="box">
32 <div class="box">
33 <div class="title">
33 <div class="title">
34 ${self.repo_page_title(c.rhodecode_db_repo)}
34 ${self.repo_page_title(c.rhodecode_db_repo)}
35 <ul class="links">
35 <ul class="links">
36 <li>
36 <li>
37 <a href="#" class="btn btn-small" id="rev_range_container" style="display:none;"></a>
37 <a href="#" class="btn btn-small" id="rev_range_container" style="display:none;"></a>
38 %if c.rhodecode_db_repo.fork:
38 %if c.rhodecode_db_repo.fork:
39 <span>
39 <span>
40 <a id="compare_fork_button"
40 <a id="compare_fork_button"
41 title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}"
41 title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}"
42 class="btn btn-small"
42 class="btn btn-small"
43 href="${h.url('compare_url',
43 href="${h.url('compare_url',
44 repo_name=c.rhodecode_db_repo.fork.repo_name,
44 repo_name=c.rhodecode_db_repo.fork.repo_name,
45 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
45 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
46 source_ref=c.rhodecode_db_repo.landing_rev[1],
46 source_ref=c.rhodecode_db_repo.landing_rev[1],
47 target_repo=c.repo_name,
47 target_repo=c.repo_name,
48 target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
48 target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
49 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
49 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
50 merge=1)
50 merge=1)
51 }">
51 }">
52 <i class="icon-loop"></i>
52 <i class="icon-loop"></i>
53 ${_('Compare fork with Parent (%s)' % c.rhodecode_db_repo.fork.repo_name)}
53 ${_('Compare fork with Parent (%s)' % c.rhodecode_db_repo.fork.repo_name)}
54 </a>
54 </a>
55 </span>
55 </span>
56 %endif
56 %endif
57
57
58 ## pr open link
58 ## pr open link
59 %if h.is_hg(c.rhodecode_repo) or h.is_git(c.rhodecode_repo):
59 %if h.is_hg(c.rhodecode_repo) or h.is_git(c.rhodecode_repo):
60 <span>
60 <span>
61 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.url('pullrequest_home',repo_name=c.repo_name)}">
61 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.url('pullrequest_home',repo_name=c.repo_name)}">
62 ${_('Open new pull request')}
62 ${_('Open new pull request')}
63 </a>
63 </a>
64 </span>
64 </span>
65 %endif
65 %endif
66
66
67 ## clear selection
67 ## clear selection
68 <div title="${_('Clear selection')}" class="btn" id="rev_range_clear" style="display:none">
68 <div title="${_('Clear selection')}" class="btn" id="rev_range_clear" style="display:none">
69 ${_('Clear selection')}
69 ${_('Clear selection')}
70 </div>
70 </div>
71
71
72 </li>
72 </li>
73 </ul>
73 </ul>
74 </div>
74 </div>
75
75
76 % if c.pagination:
76 % if c.pagination:
77
77
78 <div class="graph-header">
78 <div class="graph-header">
79 <div id="filter_changelog">
79 <div id="filter_changelog">
80 ${h.hidden('branch_filter')}
80 ${h.hidden('branch_filter')}
81 %if c.selected_name:
81 %if c.selected_name:
82 <div class="btn btn-default" id="clear_filter" >
82 <div class="btn btn-default" id="clear_filter" >
83 ${_('Clear filter')}
83 ${_('Clear filter')}
84 </div>
84 </div>
85 %endif
85 %endif
86 </div>
86 </div>
87 ${self.breadcrumbs('breadcrumbs_light')}
87 ${self.breadcrumbs('breadcrumbs_light')}
88 </div>
88 </div>
89
89
90 <div id="graph">
90 <div id="graph">
91 <div class="graph-col-wrapper">
91 <div class="graph-col-wrapper">
92 <div id="graph_nodes">
92 <div id="graph_nodes">
93 <div id="graph_canvas" data-graph='${c.jsdata|n}'></div>
93 <div id="graph_canvas" data-graph='${c.jsdata|n}'></div>
94 </div>
94 </div>
95 <div id="graph_content" class="main-content graph_full_width">
95 <div id="graph_content" class="main-content graph_full_width">
96
96
97 <div class="table">
97 <div class="table">
98 <table id="changesets" class="rctable">
98 <table id="changesets" class="rctable">
99 <tr>
99 <tr>
100 <th></th>
100 <th></th>
101 <th></th>
101 <th></th>
102 <th>${_('Author')}</th>
102 <th>${_('Author')}</th>
103 <th>${_('Age')}</th>
103 <th>${_('Age')}</th>
104 <th></th>
104 <th></th>
105 <th>${_('Commit Message')}</th>
105 <th>${_('Commit Message')}</th>
106 <th>${_('Commit')}</th>
106 <th>${_('Commit')}</th>
107 <th></th>
107 <th></th>
108 <th>${_('Refs')}</th>
108 <th>${_('Refs')}</th>
109 </tr>
109 </tr>
110 <tbody>
110 <tbody>
111 %for cnt,commit in enumerate(c.pagination):
111 %for cnt,commit in enumerate(c.pagination):
112 <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
112 <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
113
113
114 <td class="td-checkbox">
114 <td class="td-checkbox">
115 ${h.checkbox(commit.raw_id,class_="commit-range")}
115 ${h.checkbox(commit.raw_id,class_="commit-range")}
116 </td>
116 </td>
117 <td class="td-status">
117 <td class="td-status">
118
118
119 %if c.statuses.get(commit.raw_id):
119 %if c.statuses.get(commit.raw_id):
120 <div class="changeset-status-ico">
120 <div class="changeset-status-ico">
121 %if c.statuses.get(commit.raw_id)[2]:
121 %if c.statuses.get(commit.raw_id)[2]:
122 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
122 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
123 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
123 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
124 </a>
124 </a>
125 %else:
125 %else:
126 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(commit.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
126 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(commit.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
127 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
127 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
128 </a>
128 </a>
129 %endif
129 %endif
130 </div>
130 </div>
131 %endif
131 %endif
132 </td>
132 </td>
133 <td class="td-user">
133 <td class="td-user">
134 ${self.gravatar(h.email_or_none(commit.author))}
134 ${self.gravatar(h.email_or_none(commit.author))}
135 <span title="${commit.author}" class="user">${h.link_to_user(commit.author, length=22)}</span>
135 <span title="${commit.author}" class="user">${h.link_to_user(commit.author, length=22)}</span>
136 </td>
136 </td>
137 <td class="td-time">
137 <td class="td-time">
138 ${h.age_component(commit.date)}
138 ${h.age_component(commit.date)}
139 </td>
139 </td>
140
140
141 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}">
141 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}">
142 <div class="show_more_col">
142 <div class="show_more_col">
143 <i class="show_more"></i>&nbsp;
143 <i class="show_more"></i>&nbsp;
144 </div>
144 </div>
145 </td>
145 </td>
146 <td class="mid td-description">
146 <td class="mid td-description">
147 <div class="log-container truncate-wrap">
147 <div class="log-container truncate-wrap">
148 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
148 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
149 </div>
149 </div>
150 </td>
150 </td>
151
151
152 <td class="td-hash">
152 <td class="td-hash">
153 <code>
153 <code>
154 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">
154 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">
155 <span class="commit_hash">${h.show_id(commit)}</span>
155 <span class="commit_hash">${h.show_id(commit)}</span>
156 </a>
156 </a>
157 </code>
157 </code>
158 </td>
158 </td>
159
159
160 <td class="td-comments comments-col">
160 <td class="td-comments comments-col">
161 %if c.comments.get(commit.raw_id):
161 %if c.comments.get(commit.raw_id):
162 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
162 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
163 ${len(c.comments[commit.raw_id])} <i class="icon-comment icon-comment-colored"></i>
163 ${len(c.comments[commit.raw_id])} <i class="icon-comment icon-comment-colored"></i>
164 </a>
164 </a>
165 %endif
165 %endif
166 </td>
166 </td>
167
167
168 <td class="td-tags tags-col truncate-wrap">
168 <td class="td-tags tags-col truncate-wrap">
169 <div class="truncate tags-truncate" id="t-${commit.raw_id}">
169 <div class="truncate tags-truncate" id="t-${commit.raw_id}">
170 ## branch
170 ## branch
171 %if commit.branch:
171 %if commit.branch:
172 <span class="branchtag tag" title="${_('Branch %s') % commit.branch}">
172 <span class="branchtag tag" title="${_('Branch %s') % commit.branch}">
173 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
173 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
174 </span>
174 </span>
175 %endif
175 %endif
176
176
177 ## bookmarks
177 ## bookmarks
178 %if h.is_hg(c.rhodecode_repo):
178 %if h.is_hg(c.rhodecode_repo):
179 %for book in commit.bookmarks:
179 %for book in commit.bookmarks:
180 <span class="tag booktag" title="${_('Bookmark %s') % book}">
180 <span class="tag booktag" title="${_('Bookmark %s') % book}">
181 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
181 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
182 </span>
182 </span>
183 %endfor
183 %endfor
184 %endif
184 %endif
185
185
186 ## tags
186 ## tags
187 %for tag in commit.tags:
187 %for tag in commit.tags:
188 <span class="tagtag tag" title="${_('Tag %s') % tag}">
188 <span class="tagtag tag" title="${_('Tag %s') % tag}">
189 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
189 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
190 </span>
190 </span>
191 %endfor
191 %endfor
192
192
193 </div>
193 </div>
194 </td>
194 </td>
195 </tr>
195 </tr>
196 %endfor
196 %endfor
197 </tbody>
197 </tbody>
198 </table>
198 </table>
199 </div>
199 </div>
200 </div>
200 </div>
201 </div>
201 </div>
202 <div class="pagination-wh pagination-left">
202 <div class="pagination-wh pagination-left">
203 ${c.pagination.pager('$link_previous ~2~ $link_next')}
203 ${c.pagination.pager('$link_previous ~2~ $link_next')}
204 </div>
204 </div>
205
205
206 <script type="text/javascript" src="${h.url('/js/jquery.commits-graph.js')}"></script>
206 <script type="text/javascript" src="${h.url('/js/jquery.commits-graph.js')}"></script>
207 <script type="text/javascript">
207 <script type="text/javascript">
208 var cache = {};
208 var cache = {};
209 $(function(){
209 $(function(){
210
210
211 // Create links to commit ranges when range checkboxes are selected
211 // Create links to commit ranges when range checkboxes are selected
212 var $commitCheckboxes = $('.commit-range');
212 var $commitCheckboxes = $('.commit-range');
213 // cache elements
213 // cache elements
214 var $commitRangeContainer = $('#rev_range_container');
214 var $commitRangeContainer = $('#rev_range_container');
215 var $commitRangeClear = $('#rev_range_clear');
215 var $commitRangeClear = $('#rev_range_clear');
216
216
217 var checkboxRangeSelector = function(e){
217 var checkboxRangeSelector = function(e){
218 var selectedCheckboxes = [];
218 var selectedCheckboxes = [];
219 for (pos in $commitCheckboxes){
219 for (pos in $commitCheckboxes){
220 if($commitCheckboxes[pos].checked){
220 if($commitCheckboxes[pos].checked){
221 selectedCheckboxes.push($commitCheckboxes[pos]);
221 selectedCheckboxes.push($commitCheckboxes[pos]);
222 }
222 }
223 }
223 }
224 var open_new_pull_request = $('#open_new_pull_request');
224 var open_new_pull_request = $('#open_new_pull_request');
225 if(open_new_pull_request){
225 if(open_new_pull_request){
226 var selected_changes = selectedCheckboxes.length;
226 var selected_changes = selectedCheckboxes.length;
227 if (selected_changes > 1 || selected_changes == 1 && templateContext.repo_type != 'hg') {
227 if (selected_changes > 1 || selected_changes == 1 && templateContext.repo_type != 'hg') {
228 open_new_pull_request.hide();
228 open_new_pull_request.hide();
229 } else {
229 } else {
230 if (selected_changes == 1) {
230 if (selected_changes == 1) {
231 open_new_pull_request.html(_TM['Open new pull request for selected commit']);
231 open_new_pull_request.html(_gettext('Open new pull request for selected commit'));
232 } else if (selected_changes == 0) {
232 } else if (selected_changes == 0) {
233 open_new_pull_request.html(_TM['Open new pull request']);
233 open_new_pull_request.html(_gettext('Open new pull request'));
234 }
234 }
235 open_new_pull_request.show();
235 open_new_pull_request.show();
236 }
236 }
237 }
237 }
238
238
239 if (selectedCheckboxes.length>0){
239 if (selectedCheckboxes.length>0){
240 var revEnd = selectedCheckboxes[0].name;
240 var revEnd = selectedCheckboxes[0].name;
241 var revStart = selectedCheckboxes[selectedCheckboxes.length-1].name;
241 var revStart = selectedCheckboxes[selectedCheckboxes.length-1].name;
242 var url = pyroutes.url('changeset_home',
242 var url = pyroutes.url('changeset_home',
243 {'repo_name': '${c.repo_name}',
243 {'repo_name': '${c.repo_name}',
244 'revision': revStart+'...'+revEnd});
244 'revision': revStart+'...'+revEnd});
245
245
246 var link = (revStart == revEnd)
246 var link = (revStart == revEnd)
247 ? _TM['Show selected commit __S']
247 ? _gettext('Show selected commit __S')
248 : _TM['Show selected commits __S ... __E'];
248 : _gettext('Show selected commits __S ... __E');
249
249
250 link = link.replace('__S', revStart.substr(0,6));
250 link = link.replace('__S', revStart.substr(0,6));
251 link = link.replace('__E', revEnd.substr(0,6));
251 link = link.replace('__E', revEnd.substr(0,6));
252
252
253 $commitRangeContainer
253 $commitRangeContainer
254 .attr('href',url)
254 .attr('href',url)
255 .html(link)
255 .html(link)
256 .show();
256 .show();
257
257
258 $commitRangeClear.show();
258 $commitRangeClear.show();
259 var _url = pyroutes.url('pullrequest_home',
259 var _url = pyroutes.url('pullrequest_home',
260 {'repo_name': '${c.repo_name}',
260 {'repo_name': '${c.repo_name}',
261 'commit': revEnd});
261 'commit': revEnd});
262 open_new_pull_request.attr('href', _url);
262 open_new_pull_request.attr('href', _url);
263 $('#compare_fork_button').hide();
263 $('#compare_fork_button').hide();
264 } else {
264 } else {
265 $commitRangeContainer.hide();
265 $commitRangeContainer.hide();
266 $commitRangeClear.hide();
266 $commitRangeClear.hide();
267
267
268 %if c.branch_name:
268 %if c.branch_name:
269 var _url = pyroutes.url('pullrequest_home',
269 var _url = pyroutes.url('pullrequest_home',
270 {'repo_name': '${c.repo_name}',
270 {'repo_name': '${c.repo_name}',
271 'branch':'${c.branch_name}'});
271 'branch':'${c.branch_name}'});
272 open_new_pull_request.attr('href', _url);
272 open_new_pull_request.attr('href', _url);
273 %else:
273 %else:
274 var _url = pyroutes.url('pullrequest_home',
274 var _url = pyroutes.url('pullrequest_home',
275 {'repo_name': '${c.repo_name}'});
275 {'repo_name': '${c.repo_name}'});
276 open_new_pull_request.attr('href', _url);
276 open_new_pull_request.attr('href', _url);
277 %endif
277 %endif
278 $('#compare_fork_button').show();
278 $('#compare_fork_button').show();
279 }
279 }
280 };
280 };
281
281
282 $commitCheckboxes.on('click', checkboxRangeSelector);
282 $commitCheckboxes.on('click', checkboxRangeSelector);
283
283
284 $commitRangeClear.on('click',function(e) {
284 $commitRangeClear.on('click',function(e) {
285 $commitCheckboxes.attr('checked', false)
285 $commitCheckboxes.attr('checked', false)
286 checkboxRangeSelector();
286 checkboxRangeSelector();
287 e.preventDefault();
287 e.preventDefault();
288 });
288 });
289
289
290 // make sure the buttons are consistent when navigate back and forth
290 // make sure the buttons are consistent when navigate back and forth
291 checkboxRangeSelector();
291 checkboxRangeSelector();
292
292
293
293
294 var msgs = $('.message');
294 var msgs = $('.message');
295 // get first element height
295 // get first element height
296 var el = $('#graph_content .container')[0];
296 var el = $('#graph_content .container')[0];
297 var row_h = el.clientHeight;
297 var row_h = el.clientHeight;
298 for (var i=0; i < msgs.length; i++) {
298 for (var i=0; i < msgs.length; i++) {
299 var m = msgs[i];
299 var m = msgs[i];
300
300
301 var h = m.clientHeight;
301 var h = m.clientHeight;
302 var pad = $(m).css('padding');
302 var pad = $(m).css('padding');
303 if (h > row_h) {
303 if (h > row_h) {
304 var offset = row_h - (h+12);
304 var offset = row_h - (h+12);
305 $(m.nextElementSibling).css('display','block');
305 $(m.nextElementSibling).css('display','block');
306 $(m.nextElementSibling).css('margin-top',offset+'px');
306 $(m.nextElementSibling).css('margin-top',offset+'px');
307 }
307 }
308 }
308 }
309
309
310 $('.expand_commit').on('click',function(e){
310 $('.expand_commit').on('click',function(e){
311 var target_expand = $(this);
311 var target_expand = $(this);
312 var cid = target_expand.data('commitId');
312 var cid = target_expand.data('commitId');
313
313
314 if (target_expand.hasClass('open')){
314 if (target_expand.hasClass('open')){
315 $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'});
315 $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'});
316 $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden', 'white-space':'nowrap'});
316 $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden', 'white-space':'nowrap'});
317 target_expand.removeClass('open');
317 target_expand.removeClass('open');
318 }
318 }
319 else {
319 else {
320 $('#c-'+cid).css({'height': 'auto', 'white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'});
320 $('#c-'+cid).css({'height': 'auto', 'white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'});
321 $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'});
321 $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'});
322 target_expand.addClass('open');
322 target_expand.addClass('open');
323 }
323 }
324 // redraw the graph
324 // redraw the graph
325 graph_options.height = $("#changesets").height();
325 graph_options.height = $("#changesets").height();
326 $("canvas").remove();
326 $("canvas").remove();
327 $("[data-graph]").commits(graph_options);
327 $("[data-graph]").commits(graph_options);
328 });
328 });
329
329
330 $("#clear_filter").on("click", function() {
330 $("#clear_filter").on("click", function() {
331 var filter = {'repo_name': '${c.repo_name}'};
331 var filter = {'repo_name': '${c.repo_name}'};
332 window.location = pyroutes.url('changelog_home', filter);
332 window.location = pyroutes.url('changelog_home', filter);
333 });
333 });
334
334
335 $("#branch_filter").select2({
335 $("#branch_filter").select2({
336 'dropdownAutoWidth': true,
336 'dropdownAutoWidth': true,
337 'width': 'resolve',
337 'width': 'resolve',
338 'placeholder': "${c.selected_name or _('Filter changelog')}",
338 'placeholder': "${c.selected_name or _('Filter changelog')}",
339 containerCssClass: "drop-menu",
339 containerCssClass: "drop-menu",
340 dropdownCssClass: "drop-menu-dropdown",
340 dropdownCssClass: "drop-menu-dropdown",
341 query: function(query){
341 query: function(query){
342 var key = 'cache';
342 var key = 'cache';
343 var cached = cache[key] ;
343 var cached = cache[key] ;
344 if(cached) {
344 if(cached) {
345 var data = {results: []};
345 var data = {results: []};
346 //filter results
346 //filter results
347 $.each(cached.results, function(){
347 $.each(cached.results, function(){
348 var section = this.text;
348 var section = this.text;
349 var children = [];
349 var children = [];
350 $.each(this.children, function(){
350 $.each(this.children, function(){
351 if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){
351 if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){
352 children.push({'id': this.id, 'text': this.text, 'type': this.type})
352 children.push({'id': this.id, 'text': this.text, 'type': this.type})
353 }
353 }
354 });
354 });
355 data.results.push({'text': section, 'children': children});
355 data.results.push({'text': section, 'children': children});
356 query.callback({results: data.results});
356 query.callback({results: data.results});
357 });
357 });
358 }else{
358 }else{
359 $.ajax({
359 $.ajax({
360 url: pyroutes.url('repo_refs_changelog_data', {'repo_name': '${c.repo_name}'}),
360 url: pyroutes.url('repo_refs_changelog_data', {'repo_name': '${c.repo_name}'}),
361 data: {},
361 data: {},
362 dataType: 'json',
362 dataType: 'json',
363 type: 'GET',
363 type: 'GET',
364 success: function(data) {
364 success: function(data) {
365 cache[key] = data;
365 cache[key] = data;
366 query.callback({results: data.results});
366 query.callback({results: data.results});
367 }
367 }
368 })
368 })
369 }
369 }
370 }
370 }
371 });
371 });
372
372
373 $('#branch_filter').on('change', function(e){
373 $('#branch_filter').on('change', function(e){
374 var data = $('#branch_filter').select2('data');
374 var data = $('#branch_filter').select2('data');
375 var selected = data.text;
375 var selected = data.text;
376 var filter = {'repo_name': '${c.repo_name}'};
376 var filter = {'repo_name': '${c.repo_name}'};
377 if(data.type == 'branch' || data.type == 'branch_closed'){
377 if(data.type == 'branch' || data.type == 'branch_closed'){
378 filter["branch"] = selected;
378 filter["branch"] = selected;
379 }
379 }
380 else if (data.type == 'book'){
380 else if (data.type == 'book'){
381 filter["bookmark"] = selected;
381 filter["bookmark"] = selected;
382 }
382 }
383 window.location = pyroutes.url('changelog_home', filter);
383 window.location = pyroutes.url('changelog_home', filter);
384 });
384 });
385
385
386 // Determine max number of edges per row in graph
386 // Determine max number of edges per row in graph
387 var jsdata = $.parseJSON($("[data-graph]").attr('data-graph'));
387 var jsdata = $.parseJSON($("[data-graph]").attr('data-graph'));
388 var edgeCount = 1;
388 var edgeCount = 1;
389 $.each(jsdata, function(i, item){
389 $.each(jsdata, function(i, item){
390 $.each(item[2], function(key, value) {
390 $.each(item[2], function(key, value) {
391 if (value[1] > edgeCount){
391 if (value[1] > edgeCount){
392 edgeCount = value[1];
392 edgeCount = value[1];
393 }
393 }
394 });
394 });
395 });
395 });
396 var x_step = Math.min(18, Math.floor(86 / edgeCount));
396 var x_step = Math.min(18, Math.floor(86 / edgeCount));
397 var graph_options = {
397 var graph_options = {
398 width: 100,
398 width: 100,
399 height: $("#changesets").height(),
399 height: $("#changesets").height(),
400 x_step: x_step,
400 x_step: x_step,
401 y_step: 42,
401 y_step: 42,
402 dotRadius: 3.5,
402 dotRadius: 3.5,
403 lineWidth: 2.5
403 lineWidth: 2.5
404 };
404 };
405 $("[data-graph]").commits(graph_options);
405 $("[data-graph]").commits(graph_options);
406
406
407 });
407 });
408
408
409 </script>
409 </script>
410 %else:
410 %else:
411 ${_('There are no changes yet')}
411 ${_('There are no changes yet')}
412 %endif
412 %endif
413 </div>
413 </div>
414 </div>
414 </div>
415 </%def>
415 </%def>
@@ -1,312 +1,312 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 ## usage:
2 ## usage:
3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
4 ## ${comment.comment_block(comment)}
4 ## ${comment.comment_block(comment)}
5 ##
5 ##
6 <%namespace name="base" file="/base/base.html"/>
6 <%namespace name="base" file="/base/base.html"/>
7
7
8 <%def name="comment_block(comment, inline=False)">
8 <%def name="comment_block(comment, inline=False)">
9 <div class="comment ${'comment-inline' if inline else ''}" id="comment-${comment.comment_id}" line="${comment.line_no}" data-comment-id="${comment.comment_id}">
9 <div class="comment ${'comment-inline' if inline else ''}" id="comment-${comment.comment_id}" line="${comment.line_no}" data-comment-id="${comment.comment_id}">
10 <div class="meta">
10 <div class="meta">
11 <div class="author">
11 <div class="author">
12 ${base.gravatar_with_user(comment.author.email, 16)}
12 ${base.gravatar_with_user(comment.author.email, 16)}
13 </div>
13 </div>
14 <div class="date">
14 <div class="date">
15 ${h.age_component(comment.modified_at, time_is_local=True)}
15 ${h.age_component(comment.modified_at, time_is_local=True)}
16 </div>
16 </div>
17 <div class="status-change">
17 <div class="status-change">
18 %if comment.pull_request:
18 %if comment.pull_request:
19 <a href="${h.url('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}">
19 <a href="${h.url('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}">
20 %if comment.status_change:
20 %if comment.status_change:
21 ${_('Vote on pull request #%s') % comment.pull_request.pull_request_id}:
21 ${_('Vote on pull request #%s') % comment.pull_request.pull_request_id}:
22 %else:
22 %else:
23 ${_('Comment on pull request #%s') % comment.pull_request.pull_request_id}
23 ${_('Comment on pull request #%s') % comment.pull_request.pull_request_id}
24 %endif
24 %endif
25 </a>
25 </a>
26 %else:
26 %else:
27 %if comment.status_change:
27 %if comment.status_change:
28 ${_('Status change on commit')}:
28 ${_('Status change on commit')}:
29 %else:
29 %else:
30 ${_('Comment on commit')}
30 ${_('Comment on commit')}
31 %endif
31 %endif
32 %endif
32 %endif
33 </div>
33 </div>
34 %if comment.status_change:
34 %if comment.status_change:
35 <div class="${'flag_status %s' % comment.status_change[0].status}"></div>
35 <div class="${'flag_status %s' % comment.status_change[0].status}"></div>
36 <div title="${_('Commit status')}" class="changeset-status-lbl">
36 <div title="${_('Commit status')}" class="changeset-status-lbl">
37 ${comment.status_change[0].status_lbl}
37 ${comment.status_change[0].status_lbl}
38 </div>
38 </div>
39 %endif
39 %endif
40 <a class="permalink" href="#comment-${comment.comment_id}"> &para;</a>
40 <a class="permalink" href="#comment-${comment.comment_id}"> &para;</a>
41
41
42
42
43 <div class="comment-links-block">
43 <div class="comment-links-block">
44
44
45 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
45 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
46 ## only super-admin, repo admin OR comment owner can delete
46 ## only super-admin, repo admin OR comment owner can delete
47 %if not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed()):
47 %if not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed()):
48 %if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
48 %if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
49 <div onClick="deleteComment(${comment.comment_id})" class="delete-comment"> ${_('Delete')}</div>
49 <div onClick="deleteComment(${comment.comment_id})" class="delete-comment"> ${_('Delete')}</div>
50 %if inline:
50 %if inline:
51 <div class="comment-links-divider"> | </div>
51 <div class="comment-links-divider"> | </div>
52 %endif
52 %endif
53 %endif
53 %endif
54 %endif
54 %endif
55
55
56 %if inline:
56 %if inline:
57
57
58 <div id="prev_c_${comment.comment_id}" class="comment-previous-link" title="${_('Previous comment')}">
58 <div id="prev_c_${comment.comment_id}" class="comment-previous-link" title="${_('Previous comment')}">
59 <a class="arrow_comment_link disabled"><i class="icon-left"></i></a>
59 <a class="arrow_comment_link disabled"><i class="icon-left"></i></a>
60 </div>
60 </div>
61
61
62 <div id="next_c_${comment.comment_id}" class="comment-next-link" title="${_('Next comment')}">
62 <div id="next_c_${comment.comment_id}" class="comment-next-link" title="${_('Next comment')}">
63 <a class="arrow_comment_link disabled"><i class="icon-right"></i></a>
63 <a class="arrow_comment_link disabled"><i class="icon-right"></i></a>
64 </div>
64 </div>
65 %endif
65 %endif
66
66
67 </div>
67 </div>
68 </div>
68 </div>
69 <div class="text">
69 <div class="text">
70 ${comment.render(mentions=True)|n}
70 ${comment.render(mentions=True)|n}
71 </div>
71 </div>
72 </div>
72 </div>
73 </%def>
73 </%def>
74
74
75 <%def name="comment_block_outdated(comment)">
75 <%def name="comment_block_outdated(comment)">
76 <div class="comments" id="comment-${comment.comment_id}">
76 <div class="comments" id="comment-${comment.comment_id}">
77 <div class="comment comment-wrapp">
77 <div class="comment comment-wrapp">
78 <div class="meta">
78 <div class="meta">
79 <div class="author">
79 <div class="author">
80 ${base.gravatar_with_user(comment.author.email, 16)}
80 ${base.gravatar_with_user(comment.author.email, 16)}
81 </div>
81 </div>
82 <div class="date">
82 <div class="date">
83 ${h.age_component(comment.modified_at, time_is_local=True)}
83 ${h.age_component(comment.modified_at, time_is_local=True)}
84 </div>
84 </div>
85 %if comment.status_change:
85 %if comment.status_change:
86 <span class="changeset-status-container">
86 <span class="changeset-status-container">
87 <span class="changeset-status-ico">
87 <span class="changeset-status-ico">
88 <div class="${'flag_status %s' % comment.status_change[0].status}"></div>
88 <div class="${'flag_status %s' % comment.status_change[0].status}"></div>
89 </span>
89 </span>
90 <span title="${_('Commit status')}" class="changeset-status-lbl"> ${comment.status_change[0].status_lbl}</span>
90 <span title="${_('Commit status')}" class="changeset-status-lbl"> ${comment.status_change[0].status_lbl}</span>
91 </span>
91 </span>
92 %endif
92 %endif
93 <a class="permalink" href="#comment-${comment.comment_id}">&para;</a>
93 <a class="permalink" href="#comment-${comment.comment_id}">&para;</a>
94 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
94 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
95 ## only super-admin, repo admin OR comment owner can delete
95 ## only super-admin, repo admin OR comment owner can delete
96 %if not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed()):
96 %if not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed()):
97 <div class="comment-links-block">
97 <div class="comment-links-block">
98 %if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
98 %if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
99 <div data-comment-id=${comment.comment_id} class="delete-comment">${_('Delete')}</div>
99 <div data-comment-id=${comment.comment_id} class="delete-comment">${_('Delete')}</div>
100 %endif
100 %endif
101 </div>
101 </div>
102 %endif
102 %endif
103 </div>
103 </div>
104 <div class="text">
104 <div class="text">
105 ${comment.render(mentions=True)|n}
105 ${comment.render(mentions=True)|n}
106 </div>
106 </div>
107 </div>
107 </div>
108 </div>
108 </div>
109 </%def>
109 </%def>
110
110
111 <%def name="comment_inline_form()">
111 <%def name="comment_inline_form()">
112 <div id="comment-inline-form-template" style="display: none;">
112 <div id="comment-inline-form-template" style="display: none;">
113 <div class="comment-inline-form ac">
113 <div class="comment-inline-form ac">
114 %if c.rhodecode_user.username != h.DEFAULT_USER:
114 %if c.rhodecode_user.username != h.DEFAULT_USER:
115 ${h.form('#', class_='inline-form', method='get')}
115 ${h.form('#', class_='inline-form', method='get')}
116 <div id="edit-container_{1}" class="clearfix">
116 <div id="edit-container_{1}" class="clearfix">
117 <div class="comment-title pull-left">
117 <div class="comment-title pull-left">
118 ${_('Create a comment on line {1}.')}
118 ${_('Create a comment on line {1}.')}
119 </div>
119 </div>
120 <div class="comment-help pull-right">
120 <div class="comment-help pull-right">
121 ${(_('Comments parsed using %s syntax with %s support.') % (
121 ${(_('Comments parsed using %s syntax with %s support.') % (
122 ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
122 ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
123 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user'))
123 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user'))
124 )
124 )
125 )|n
125 )|n
126 }
126 }
127 </div>
127 </div>
128 <div style="clear: both"></div>
128 <div style="clear: both"></div>
129 <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea>
129 <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea>
130 </div>
130 </div>
131 <div id="preview-container_{1}" class="clearfix" style="display: none;">
131 <div id="preview-container_{1}" class="clearfix" style="display: none;">
132 <div class="comment-help">
132 <div class="comment-help">
133 ${_('Comment preview')}
133 ${_('Comment preview')}
134 </div>
134 </div>
135 <div id="preview-box_{1}" class="preview-box"></div>
135 <div id="preview-box_{1}" class="preview-box"></div>
136 </div>
136 </div>
137 <div class="comment-footer">
137 <div class="comment-footer">
138 <div class="comment-button hide-inline-form-button cancel-button">
138 <div class="comment-button hide-inline-form-button cancel-button">
139 ${h.reset('hide-inline-form', _('Cancel'), class_='btn hide-inline-form', id_="cancel-btn_{1}")}
139 ${h.reset('hide-inline-form', _('Cancel'), class_='btn hide-inline-form', id_="cancel-btn_{1}")}
140 </div>
140 </div>
141 <div class="action-buttons">
141 <div class="action-buttons">
142 <input type="hidden" name="f_path" value="{0}">
142 <input type="hidden" name="f_path" value="{0}">
143 <input type="hidden" name="line" value="{1}">
143 <input type="hidden" name="line" value="{1}">
144 <button id="preview-btn_{1}" class="btn btn-secondary">${_('Preview')}</button>
144 <button id="preview-btn_{1}" class="btn btn-secondary">${_('Preview')}</button>
145 <button id="edit-btn_{1}" class="btn btn-secondary" style="display: none;">${_('Edit')}</button>
145 <button id="edit-btn_{1}" class="btn btn-secondary" style="display: none;">${_('Edit')}</button>
146 ${h.submit('save', _('Comment'), class_='btn btn-success save-inline-form')}
146 ${h.submit('save', _('Comment'), class_='btn btn-success save-inline-form')}
147 </div>
147 </div>
148 ${h.end_form()}
148 ${h.end_form()}
149 </div>
149 </div>
150 %else:
150 %else:
151 ${h.form('', class_='inline-form comment-form-login', method='get')}
151 ${h.form('', class_='inline-form comment-form-login', method='get')}
152 <div class="pull-left">
152 <div class="pull-left">
153 <div class="comment-help pull-right">
153 <div class="comment-help pull-right">
154 ${_('You need to be logged in to comment.')} <a href="${h.url('login_home',came_from=h.url.current())}">${_('Login now')}</a>
154 ${_('You need to be logged in to comment.')} <a href="${h.route_path('login', _query={'came_from': h.url.current()})}">${_('Login now')}</a>
155 </div>
155 </div>
156 </div>
156 </div>
157 <div class="comment-button pull-right">
157 <div class="comment-button pull-right">
158 ${h.reset('hide-inline-form', _('Hide'), class_='btn hide-inline-form')}
158 ${h.reset('hide-inline-form', _('Hide'), class_='btn hide-inline-form')}
159 </div>
159 </div>
160 <div class="clearfix"></div>
160 <div class="clearfix"></div>
161 ${h.end_form()}
161 ${h.end_form()}
162 %endif
162 %endif
163 </div>
163 </div>
164 </div>
164 </div>
165 </%def>
165 </%def>
166
166
167
167
168 ## generates inlines taken from c.comments var
168 ## generates inlines taken from c.comments var
169 <%def name="inlines(is_pull_request=False)">
169 <%def name="inlines(is_pull_request=False)">
170 %if is_pull_request:
170 %if is_pull_request:
171 <h2 id="comments">${ungettext("%d Pull Request Comment", "%d Pull Request Comments", len(c.comments)) % len(c.comments)}</h2>
171 <h2 id="comments">${ungettext("%d Pull Request Comment", "%d Pull Request Comments", len(c.comments)) % len(c.comments)}</h2>
172 %else:
172 %else:
173 <h2 id="comments">${ungettext("%d Commit Comment", "%d Commit Comments", len(c.comments)) % len(c.comments)}</h2>
173 <h2 id="comments">${ungettext("%d Commit Comment", "%d Commit Comments", len(c.comments)) % len(c.comments)}</h2>
174 %endif
174 %endif
175 %for path, lines_comments in c.inline_comments:
175 %for path, lines_comments in c.inline_comments:
176 % for line, comments in lines_comments.iteritems():
176 % for line, comments in lines_comments.iteritems():
177 <div style="display: none;" class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
177 <div style="display: none;" class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
178 ## for each comment in particular line
178 ## for each comment in particular line
179 %for comment in comments:
179 %for comment in comments:
180 ${comment_block(comment, inline=True)}
180 ${comment_block(comment, inline=True)}
181 %endfor
181 %endfor
182 </div>
182 </div>
183 %endfor
183 %endfor
184 %endfor
184 %endfor
185
185
186 </%def>
186 </%def>
187
187
188 ## generate inline comments and the main ones
188 ## generate inline comments and the main ones
189 <%def name="generate_comments(include_pull_request=False, is_pull_request=False)">
189 <%def name="generate_comments(include_pull_request=False, is_pull_request=False)">
190 ## generate inlines for this changeset
190 ## generate inlines for this changeset
191 ${inlines(is_pull_request)}
191 ${inlines(is_pull_request)}
192
192
193 %for comment in c.comments:
193 %for comment in c.comments:
194 <div id="comment-tr-${comment.comment_id}">
194 <div id="comment-tr-${comment.comment_id}">
195 ## only render comments that are not from pull request, or from
195 ## only render comments that are not from pull request, or from
196 ## pull request and a status change
196 ## pull request and a status change
197 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
197 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
198 ${comment_block(comment)}
198 ${comment_block(comment)}
199 %endif
199 %endif
200 </div>
200 </div>
201 %endfor
201 %endfor
202 ## to anchor ajax comments
202 ## to anchor ajax comments
203 <div id="injected_page_comments"></div>
203 <div id="injected_page_comments"></div>
204 </%def>
204 </%def>
205
205
206 ## MAIN COMMENT FORM
206 ## MAIN COMMENT FORM
207 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
207 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
208 %if is_compare:
208 %if is_compare:
209 <% form_id = "comments_form_compare" %>
209 <% form_id = "comments_form_compare" %>
210 %else:
210 %else:
211 <% form_id = "comments_form" %>
211 <% form_id = "comments_form" %>
212 %endif
212 %endif
213
213
214
214
215 %if is_pull_request:
215 %if is_pull_request:
216 <div class="pull-request-merge">
216 <div class="pull-request-merge">
217 %if c.allowed_to_merge:
217 %if c.allowed_to_merge:
218 <div class="pull-request-wrap">
218 <div class="pull-request-wrap">
219 <div class="pull-right">
219 <div class="pull-right">
220 ${h.secure_form(url('pullrequest_merge', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id), id='merge_pull_request_form')}
220 ${h.secure_form(url('pullrequest_merge', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id), id='merge_pull_request_form')}
221 <span data-role="merge-message">${c.pr_merge_msg} ${c.approval_msg if c.approval_msg else ''}</span>
221 <span data-role="merge-message">${c.pr_merge_msg} ${c.approval_msg if c.approval_msg else ''}</span>
222 <% merge_disabled = ' disabled' if c.pr_merge_status is False else '' %>
222 <% merge_disabled = ' disabled' if c.pr_merge_status is False else '' %>
223 <input type="submit" id="merge_pull_request" value="${_('Merge Pull Request')}" class="btn${merge_disabled}"${merge_disabled}>
223 <input type="submit" id="merge_pull_request" value="${_('Merge Pull Request')}" class="btn${merge_disabled}"${merge_disabled}>
224 ${h.end_form()}
224 ${h.end_form()}
225 </div>
225 </div>
226 </div>
226 </div>
227 %else:
227 %else:
228 <div class="pull-request-wrap">
228 <div class="pull-request-wrap">
229 <div class="pull-right">
229 <div class="pull-right">
230 <span>${c.pr_merge_msg} ${c.approval_msg if c.approval_msg else ''}</span>
230 <span>${c.pr_merge_msg} ${c.approval_msg if c.approval_msg else ''}</span>
231 </div>
231 </div>
232 </div>
232 </div>
233 %endif
233 %endif
234 </div>
234 </div>
235 %endif
235 %endif
236 <div class="comments">
236 <div class="comments">
237 %if c.rhodecode_user.username != h.DEFAULT_USER:
237 %if c.rhodecode_user.username != h.DEFAULT_USER:
238 <div class="comment-form ac">
238 <div class="comment-form ac">
239 ${h.secure_form(post_url, id_=form_id)}
239 ${h.secure_form(post_url, id_=form_id)}
240 <div id="edit-container" class="clearfix">
240 <div id="edit-container" class="clearfix">
241 <div class="comment-title pull-left">
241 <div class="comment-title pull-left">
242 %if is_pull_request:
242 %if is_pull_request:
243 ${(_('Create a comment on this Pull Request.'))}
243 ${(_('Create a comment on this Pull Request.'))}
244 %elif is_compare:
244 %elif is_compare:
245 ${(_('Create comments on this Commit range.'))}
245 ${(_('Create comments on this Commit range.'))}
246 %else:
246 %else:
247 ${(_('Create a comment on this Commit.'))}
247 ${(_('Create a comment on this Commit.'))}
248 %endif
248 %endif
249 </div>
249 </div>
250 <div class="comment-help pull-right">
250 <div class="comment-help pull-right">
251 ${(_('Comments parsed using %s syntax with %s support.') % (
251 ${(_('Comments parsed using %s syntax with %s support.') % (
252 ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
252 ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
253 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user'))
253 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user'))
254 )
254 )
255 )|n
255 )|n
256 }
256 }
257 </div>
257 </div>
258 <div style="clear: both"></div>
258 <div style="clear: both"></div>
259 ${h.textarea('text', class_="comment-block-ta")}
259 ${h.textarea('text', class_="comment-block-ta")}
260 </div>
260 </div>
261
261
262 <div id="preview-container" class="clearfix" style="display: none;">
262 <div id="preview-container" class="clearfix" style="display: none;">
263 <div class="comment-title">
263 <div class="comment-title">
264 ${_('Comment preview')}
264 ${_('Comment preview')}
265 </div>
265 </div>
266 <div id="preview-box" class="preview-box"></div>
266 <div id="preview-box" class="preview-box"></div>
267 </div>
267 </div>
268
268
269 <div id="comment_form_extras">
269 <div id="comment_form_extras">
270 %if form_extras and isinstance(form_extras, (list, tuple)):
270 %if form_extras and isinstance(form_extras, (list, tuple)):
271 % for form_ex_el in form_extras:
271 % for form_ex_el in form_extras:
272 ${form_ex_el|n}
272 ${form_ex_el|n}
273 % endfor
273 % endfor
274 %endif
274 %endif
275 </div>
275 </div>
276 <div class="comment-footer">
276 <div class="comment-footer">
277 %if change_status:
277 %if change_status:
278 <div class="status_box">
278 <div class="status_box">
279 <select id="change_status" name="changeset_status">
279 <select id="change_status" name="changeset_status">
280 <option></option> # Placeholder
280 <option></option> # Placeholder
281 %for status,lbl in c.commit_statuses:
281 %for status,lbl in c.commit_statuses:
282 <option value="${status}" data-status="${status}">${lbl}</option>
282 <option value="${status}" data-status="${status}">${lbl}</option>
283 %if is_pull_request and change_status and status in ('approved', 'rejected'):
283 %if is_pull_request and change_status and status in ('approved', 'rejected'):
284 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
284 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
285 %endif
285 %endif
286 %endfor
286 %endfor
287 </select>
287 </select>
288 </div>
288 </div>
289 %endif
289 %endif
290 <div class="action-buttons">
290 <div class="action-buttons">
291 <button id="preview-btn" class="btn btn-secondary">${_('Preview')}</button>
291 <button id="preview-btn" class="btn btn-secondary">${_('Preview')}</button>
292 <button id="edit-btn" class="btn btn-secondary" style="display:none;">${_('Edit')}</button>
292 <button id="edit-btn" class="btn btn-secondary" style="display:none;">${_('Edit')}</button>
293 <div class="comment-button">${h.submit('save', _('Comment'), class_="btn btn-success comment-button-input")}</div>
293 <div class="comment-button">${h.submit('save', _('Comment'), class_="btn btn-success comment-button-input")}</div>
294 </div>
294 </div>
295 </div>
295 </div>
296 ${h.end_form()}
296 ${h.end_form()}
297 </div>
297 </div>
298 %endif
298 %endif
299 </div>
299 </div>
300 <script>
300 <script>
301 // init active elements of commentForm
301 // init active elements of commentForm
302 var commitId = templateContext.commit_data.commit_id;
302 var commitId = templateContext.commit_data.commit_id;
303 var pullRequestId = templateContext.pull_request_data.pull_request_id;
303 var pullRequestId = templateContext.pull_request_data.pull_request_id;
304 var lineNo;
304 var lineNo;
305
305
306 var mainCommentForm = new CommentForm(
306 var mainCommentForm = new CommentForm(
307 "#${form_id}", commitId, pullRequestId, lineNo, true);
307 "#${form_id}", commitId, pullRequestId, lineNo, true);
308
308
309 mainCommentForm.initStatusChangeSelector();
309 mainCommentForm.initStatusChangeSelector();
310
310
311 </script>
311 </script>
312 </%def>
312 </%def>
@@ -1,322 +1,322 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 %if c.compare_home:
5 %if c.compare_home:
6 ${_('%s Compare') % c.repo_name}
6 ${_('%s Compare') % c.repo_name}
7 %else:
7 %else:
8 ${_('%s Compare') % c.repo_name} - ${'%s@%s' % (c.source_repo.repo_name, c.source_ref)} &gt; ${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}
8 ${_('%s Compare') % c.repo_name} - ${'%s@%s' % (c.source_repo.repo_name, c.source_ref)} &gt; ${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}
9 %endif
9 %endif
10 %if c.rhodecode_name:
10 %if c.rhodecode_name:
11 &middot; ${h.branding(c.rhodecode_name)}
11 &middot; ${h.branding(c.rhodecode_name)}
12 %endif
12 %endif
13 </%def>
13 </%def>
14
14
15 <%def name="breadcrumbs_links()">
15 <%def name="breadcrumbs_links()">
16 ${ungettext('%s commit','%s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
16 ${ungettext('%s commit','%s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
17 </%def>
17 </%def>
18
18
19 <%def name="menu_bar_nav()">
19 <%def name="menu_bar_nav()">
20 ${self.menu_items(active='repositories')}
20 ${self.menu_items(active='repositories')}
21 </%def>
21 </%def>
22
22
23 <%def name="menu_bar_subnav()">
23 <%def name="menu_bar_subnav()">
24 ${self.repo_menu(active='compare')}
24 ${self.repo_menu(active='compare')}
25 </%def>
25 </%def>
26
26
27 <%def name="main()">
27 <%def name="main()">
28 <script type="text/javascript">
28 <script type="text/javascript">
29 // set fake commitId on this commit-range page
29 // set fake commitId on this commit-range page
30 templateContext.commit_data.commit_id = "${h.EmptyCommit().raw_id}";
30 templateContext.commit_data.commit_id = "${h.EmptyCommit().raw_id}";
31 </script>
31 </script>
32
32
33 <div class="box">
33 <div class="box">
34 <div class="title">
34 <div class="title">
35 ${self.repo_page_title(c.rhodecode_db_repo)}
35 ${self.repo_page_title(c.rhodecode_db_repo)}
36 <div class="breadcrumbs">
36 <div class="breadcrumbs">
37 ${_('Compare Commits')}
37 ${_('Compare Commits')}
38 </div>
38 </div>
39 </div>
39 </div>
40
40
41 <div class="table">
41 <div class="table">
42 <div id="codeblock" class="diffblock">
42 <div id="codeblock" class="diffblock">
43 <div class="code-header" >
43 <div class="code-header" >
44 <div class="compare_header">
44 <div class="compare_header">
45 ## The hidden elements are replaced with a select2 widget
45 ## The hidden elements are replaced with a select2 widget
46 <div class="compare-label">${_('Target')}</div>${h.hidden('compare_source')}
46 <div class="compare-label">${_('Target')}</div>${h.hidden('compare_source')}
47 <div class="compare-label">${_('Source')}</div>${h.hidden('compare_target')}
47 <div class="compare-label">${_('Source')}</div>${h.hidden('compare_target')}
48
48
49 %if not c.preview_mode:
49 %if not c.preview_mode:
50 <div class="compare-label"></div>
50 <div class="compare-label"></div>
51 <div class="compare-buttons">
51 <div class="compare-buttons">
52 %if not c.compare_home:
52 %if not c.compare_home:
53 <a id="btn-swap" class="btn btn-primary" href="${c.swap_url}"><i class="icon-refresh"></i> ${_('Swap')}</a>
53 <a id="btn-swap" class="btn btn-primary" href="${c.swap_url}"><i class="icon-refresh"></i> ${_('Swap')}</a>
54 %endif
54 %endif
55 <div id="compare_revs" class="btn btn-primary"><i class ="icon-loop"></i> ${_('Compare Commits')}</div>
55 <div id="compare_revs" class="btn btn-primary"><i class ="icon-loop"></i> ${_('Compare Commits')}</div>
56 %if c.files:
56 %if c.files:
57 <div id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</div>
57 <div id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</div>
58 %endif
58 %endif
59 </div>
59 </div>
60 %endif
60 %endif
61 </div>
61 </div>
62 </div>
62 </div>
63 </div>
63 </div>
64 ## use JS script to load it quickly before potentially large diffs render long time
64 ## use JS script to load it quickly before potentially large diffs render long time
65 ## this prevents from situation when large diffs block rendering of select2 fields
65 ## this prevents from situation when large diffs block rendering of select2 fields
66 <script type="text/javascript">
66 <script type="text/javascript">
67
67
68 var cache = {};
68 var cache = {};
69
69
70 var formatSelection = function(repoName){
70 var formatSelection = function(repoName){
71 return function(data, container, escapeMarkup) {
71 return function(data, container, escapeMarkup) {
72 var selection = data ? this.text(data) : "";
72 var selection = data ? this.text(data) : "";
73 return escapeMarkup('{0}@{1}'.format(repoName, selection));
73 return escapeMarkup('{0}@{1}'.format(repoName, selection));
74 }
74 }
75 };
75 };
76
76
77 var feedCompareData = function(query, cachedValue){
77 var feedCompareData = function(query, cachedValue){
78 var data = {results: []};
78 var data = {results: []};
79 //filter results
79 //filter results
80 $.each(cachedValue.results, function() {
80 $.each(cachedValue.results, function() {
81 var section = this.text;
81 var section = this.text;
82 var children = [];
82 var children = [];
83 $.each(this.children, function() {
83 $.each(this.children, function() {
84 if (query.term.length === 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
84 if (query.term.length === 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
85 children.push({
85 children.push({
86 'id': this.id,
86 'id': this.id,
87 'text': this.text,
87 'text': this.text,
88 'type': this.type
88 'type': this.type
89 })
89 })
90 }
90 }
91 });
91 });
92 data.results.push({
92 data.results.push({
93 'text': section,
93 'text': section,
94 'children': children
94 'children': children
95 })
95 })
96 });
96 });
97 //push the typed in changeset
97 //push the typed in changeset
98 data.results.push({
98 data.results.push({
99 'text': _TM['specify commit'],
99 'text': _gettext('specify commit'),
100 'children': [{
100 'children': [{
101 'id': query.term,
101 'id': query.term,
102 'text': query.term,
102 'text': query.term,
103 'type': 'rev'
103 'type': 'rev'
104 }]
104 }]
105 });
105 });
106 query.callback(data);
106 query.callback(data);
107 };
107 };
108
108
109 var loadCompareData = function(repoName, query, cache){
109 var loadCompareData = function(repoName, query, cache){
110 $.ajax({
110 $.ajax({
111 url: pyroutes.url('repo_refs_data', {'repo_name': repoName}),
111 url: pyroutes.url('repo_refs_data', {'repo_name': repoName}),
112 data: {},
112 data: {},
113 dataType: 'json',
113 dataType: 'json',
114 type: 'GET',
114 type: 'GET',
115 success: function(data) {
115 success: function(data) {
116 cache[repoName] = data;
116 cache[repoName] = data;
117 query.callback({results: data.results});
117 query.callback({results: data.results});
118 }
118 }
119 })
119 })
120 };
120 };
121
121
122 var enable_fields = ${"false" if c.preview_mode else "true"};
122 var enable_fields = ${"false" if c.preview_mode else "true"};
123 $("#compare_source").select2({
123 $("#compare_source").select2({
124 placeholder: "${'%s@%s' % (c.source_repo.repo_name, c.source_ref)}",
124 placeholder: "${'%s@%s' % (c.source_repo.repo_name, c.source_ref)}",
125 containerCssClass: "drop-menu",
125 containerCssClass: "drop-menu",
126 dropdownCssClass: "drop-menu-dropdown",
126 dropdownCssClass: "drop-menu-dropdown",
127 formatSelection: formatSelection("${c.source_repo.repo_name}"),
127 formatSelection: formatSelection("${c.source_repo.repo_name}"),
128 dropdownAutoWidth: true,
128 dropdownAutoWidth: true,
129 query: function(query) {
129 query: function(query) {
130 var repoName = '${c.source_repo.repo_name}';
130 var repoName = '${c.source_repo.repo_name}';
131 var cachedValue = cache[repoName];
131 var cachedValue = cache[repoName];
132
132
133 if (cachedValue){
133 if (cachedValue){
134 feedCompareData(query, cachedValue);
134 feedCompareData(query, cachedValue);
135 }
135 }
136 else {
136 else {
137 loadCompareData(repoName, query, cache);
137 loadCompareData(repoName, query, cache);
138 }
138 }
139 }
139 }
140 }).select2("enable", enable_fields);
140 }).select2("enable", enable_fields);
141
141
142 $("#compare_target").select2({
142 $("#compare_target").select2({
143 placeholder: "${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}",
143 placeholder: "${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}",
144 dropdownAutoWidth: true,
144 dropdownAutoWidth: true,
145 containerCssClass: "drop-menu",
145 containerCssClass: "drop-menu",
146 dropdownCssClass: "drop-menu-dropdown",
146 dropdownCssClass: "drop-menu-dropdown",
147 formatSelection: formatSelection("${c.target_repo.repo_name}"),
147 formatSelection: formatSelection("${c.target_repo.repo_name}"),
148 query: function(query) {
148 query: function(query) {
149 var repoName = '${c.target_repo.repo_name}';
149 var repoName = '${c.target_repo.repo_name}';
150 var cachedValue = cache[repoName];
150 var cachedValue = cache[repoName];
151
151
152 if (cachedValue){
152 if (cachedValue){
153 feedCompareData(query, cachedValue);
153 feedCompareData(query, cachedValue);
154 }
154 }
155 else {
155 else {
156 loadCompareData(repoName, query, cache);
156 loadCompareData(repoName, query, cache);
157 }
157 }
158 }
158 }
159 }).select2("enable", enable_fields);
159 }).select2("enable", enable_fields);
160 var initial_compare_source = {id: "${c.source_ref}", type:"${c.source_ref_type}"};
160 var initial_compare_source = {id: "${c.source_ref}", type:"${c.source_ref_type}"};
161 var initial_compare_target = {id: "${c.target_ref}", type:"${c.target_ref_type}"};
161 var initial_compare_target = {id: "${c.target_ref}", type:"${c.target_ref_type}"};
162
162
163 $('#compare_revs').on('click', function(e) {
163 $('#compare_revs').on('click', function(e) {
164 var source = $('#compare_source').select2('data') || initial_compare_source;
164 var source = $('#compare_source').select2('data') || initial_compare_source;
165 var target = $('#compare_target').select2('data') || initial_compare_target;
165 var target = $('#compare_target').select2('data') || initial_compare_target;
166 if (source && target) {
166 if (source && target) {
167 var url_data = {
167 var url_data = {
168 repo_name: "${c.repo_name}",
168 repo_name: "${c.repo_name}",
169 source_ref: source.id,
169 source_ref: source.id,
170 source_ref_type: source.type,
170 source_ref_type: source.type,
171 target_ref: target.id,
171 target_ref: target.id,
172 target_ref_type: target.type
172 target_ref_type: target.type
173 };
173 };
174 window.location = pyroutes.url('compare_url', url_data);
174 window.location = pyroutes.url('compare_url', url_data);
175 }
175 }
176 });
176 });
177 $('#compare_changeset_status_toggle').on('click', function(e) {
177 $('#compare_changeset_status_toggle').on('click', function(e) {
178 $('#compare_changeset_status').toggle();
178 $('#compare_changeset_status').toggle();
179 });
179 });
180
180
181 </script>
181 </script>
182
182
183 ## changeset status form
183 ## changeset status form
184 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
184 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
185 ## main comment form and it status
185 ## main comment form and it status
186 <%
186 <%
187 def revs(_revs):
187 def revs(_revs):
188 form_inputs = []
188 form_inputs = []
189 for cs in _revs:
189 for cs in _revs:
190 tmpl = '<input type="hidden" data-commit-id="%(cid)s" name="commit_ids" value="%(cid)s">' % {'cid': cs.raw_id}
190 tmpl = '<input type="hidden" data-commit-id="%(cid)s" name="commit_ids" value="%(cid)s">' % {'cid': cs.raw_id}
191 form_inputs.append(tmpl)
191 form_inputs.append(tmpl)
192 return form_inputs
192 return form_inputs
193 %>
193 %>
194 <div id="compare_changeset_status" style="display: none;">
194 <div id="compare_changeset_status" style="display: none;">
195 ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision='0'*16), None, is_compare=True, form_extras=revs(c.commit_ranges))}
195 ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision='0'*16), None, is_compare=True, form_extras=revs(c.commit_ranges))}
196 <script type="text/javascript">
196 <script type="text/javascript">
197
197
198 mainCommentForm.setHandleFormSubmit(function(o) {
198 mainCommentForm.setHandleFormSubmit(function(o) {
199 var text = mainCommentForm.cm.getValue();
199 var text = mainCommentForm.cm.getValue();
200 var status = mainCommentForm.getCommentStatus();
200 var status = mainCommentForm.getCommentStatus();
201
201
202 if (text === "" && !status) {
202 if (text === "" && !status) {
203 return;
203 return;
204 }
204 }
205
205
206 // we can pick which commits we want to make the comment by
206 // we can pick which commits we want to make the comment by
207 // selecting them via click on preview pane, this will alter the hidden inputs
207 // selecting them via click on preview pane, this will alter the hidden inputs
208 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
208 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
209
209
210 var commitIds = [];
210 var commitIds = [];
211 $('#changeset_compare_view_content .compare_select').each(function(el) {
211 $('#changeset_compare_view_content .compare_select').each(function(el) {
212 var commitId = this.id.replace('row-', '');
212 var commitId = this.id.replace('row-', '');
213 if ($(this).hasClass('hl') || !cherryPicked) {
213 if ($(this).hasClass('hl') || !cherryPicked) {
214 $("input[data-commit-id='{0}']".format(commitId)).val(commitId)
214 $("input[data-commit-id='{0}']".format(commitId)).val(commitId)
215 commitIds.push(commitId);
215 commitIds.push(commitId);
216 } else {
216 } else {
217 $("input[data-commit-id='{0}']".format(commitId)).val('')
217 $("input[data-commit-id='{0}']".format(commitId)).val('')
218 }
218 }
219 });
219 });
220
220
221 mainCommentForm.setActionButtonsDisabled(true);
221 mainCommentForm.setActionButtonsDisabled(true);
222 mainCommentForm.cm.setOption("readOnly", true);
222 mainCommentForm.cm.setOption("readOnly", true);
223 var postData = {
223 var postData = {
224 'text': text,
224 'text': text,
225 'changeset_status': status,
225 'changeset_status': status,
226 'commit_ids': commitIds,
226 'commit_ids': commitIds,
227 'csrf_token': CSRF_TOKEN
227 'csrf_token': CSRF_TOKEN
228 };
228 };
229
229
230 var submitSuccessCallback = function(o) {
230 var submitSuccessCallback = function(o) {
231 location.reload(true);
231 location.reload(true);
232 };
232 };
233 var submitFailCallback = function(){
233 var submitFailCallback = function(){
234 mainCommentForm.resetCommentFormState(text)
234 mainCommentForm.resetCommentFormState(text)
235 };
235 };
236 mainCommentForm.submitAjaxPOST(
236 mainCommentForm.submitAjaxPOST(
237 mainCommentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
237 mainCommentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
238 });
238 });
239 </script>
239 </script>
240
240
241 </div>
241 </div>
242
242
243 %if c.compare_home:
243 %if c.compare_home:
244 <div id="changeset_compare_view_content">
244 <div id="changeset_compare_view_content">
245 <div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
245 <div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
246 </div>
246 </div>
247 %else:
247 %else:
248 <div id="changeset_compare_view_content">
248 <div id="changeset_compare_view_content">
249 ##CS
249 ##CS
250 <%include file="compare_commits.html"/>
250 <%include file="compare_commits.html"/>
251
251
252 ## FILES
252 ## FILES
253 <div class="cs_files_title">
253 <div class="cs_files_title">
254 <span class="cs_files_expand">
254 <span class="cs_files_expand">
255 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
255 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
256 </span>
256 </span>
257 <h2>
257 <h2>
258 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
258 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
259 </h2>
259 </h2>
260 </div>
260 </div>
261 <div class="cs_files">
261 <div class="cs_files">
262 %if not c.files:
262 %if not c.files:
263 <p class="empty_data">${_('No files')}</p>
263 <p class="empty_data">${_('No files')}</p>
264 %endif
264 %endif
265 <table class="compare_view_files">
265 <table class="compare_view_files">
266 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
266 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
267 %for FID, change, path, stats, file in c.files:
267 %for FID, change, path, stats, file in c.files:
268 <tr class="cs_${change} collapse_file" fid="${FID}">
268 <tr class="cs_${change} collapse_file" fid="${FID}">
269 <td class="cs_icon_td">
269 <td class="cs_icon_td">
270 <span class="collapse_file_icon" fid="${FID}"></span>
270 <span class="collapse_file_icon" fid="${FID}"></span>
271 </td>
271 </td>
272 <td class="cs_icon_td">
272 <td class="cs_icon_td">
273 <div class="flag_status not_reviewed hidden"></div>
273 <div class="flag_status not_reviewed hidden"></div>
274 </td>
274 </td>
275 <td class="cs_${change}" id="a_${FID}">
275 <td class="cs_${change}" id="a_${FID}">
276 <div class="node">
276 <div class="node">
277 <a href="#a_${FID}">
277 <a href="#a_${FID}">
278 <i class="icon-file-${change.lower()}"></i>
278 <i class="icon-file-${change.lower()}"></i>
279 ${h.safe_unicode(path)}
279 ${h.safe_unicode(path)}
280 </a>
280 </a>
281 </div>
281 </div>
282 </td>
282 </td>
283 <td>
283 <td>
284 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
284 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
285 <div class="comment-bubble pull-right" data-path="${path}">
285 <div class="comment-bubble pull-right" data-path="${path}">
286 <i class="icon-comment"></i>
286 <i class="icon-comment"></i>
287 </div>
287 </div>
288 </td>
288 </td>
289 </tr>
289 </tr>
290 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
290 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
291 <td></td>
291 <td></td>
292 <td></td>
292 <td></td>
293 <td class="cs_${change}">
293 <td class="cs_${change}">
294 %if c.target_repo.repo_name == c.repo_name:
294 %if c.target_repo.repo_name == c.repo_name:
295 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.source_ref, c.target_ref, change, file)}
295 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.source_ref, c.target_ref, change, file)}
296 %else:
296 %else:
297 ## this is slightly different case later, since the target repo can have this
297 ## this is slightly different case later, since the target repo can have this
298 ## file in target state than the source repo
298 ## file in target state than the source repo
299 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.source_ref, c.target_ref, change, file)}
299 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.source_ref, c.target_ref, change, file)}
300 %endif
300 %endif
301 </td>
301 </td>
302 <td class="td-actions rc-form">
302 <td class="td-actions rc-form">
303 </td>
303 </td>
304 </tr>
304 </tr>
305 <tr id="tr_${FID}">
305 <tr id="tr_${FID}">
306 <td></td>
306 <td></td>
307 <td></td>
307 <td></td>
308 <td class="injected_diff" colspan="2">
308 <td class="injected_diff" colspan="2">
309 ${diff_block.diff_block_simple([c.changes[FID]])}
309 ${diff_block.diff_block_simple([c.changes[FID]])}
310 </td>
310 </td>
311 </tr>
311 </tr>
312 %endfor
312 %endfor
313 </table>
313 </table>
314 % if c.limited_diff:
314 % if c.limited_diff:
315 ${diff_block.changeset_message()}
315 ${diff_block.changeset_message()}
316 % endif
316 % endif
317 </div>
317 </div>
318 %endif
318 %endif
319 </div>
319 </div>
320 </div>
320 </div>
321 </div>
321 </div>
322 </%def>
322 </%def>
@@ -1,967 +1,967 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/debug_style/index.html"/>
2 <%inherit file="/debug_style/index.html"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 ${h.link_to(_('Style'), h.url('debug_style_home'))}
5 ${h.link_to(_('Style'), h.url('debug_style_home'))}
6 &raquo;
6 &raquo;
7 ${c.active}
7 ${c.active}
8 </%def>
8 </%def>
9
9
10
10
11 <%def name="real_main()">
11 <%def name="real_main()">
12 <div class="box">
12 <div class="box">
13 <div class="title">
13 <div class="title">
14 ${self.breadcrumbs()}
14 ${self.breadcrumbs()}
15 </div>
15 </div>
16
16
17 <div class='sidebar-col-wrapper'>
17 <div class='sidebar-col-wrapper'>
18 ${self.sidebar()}
18 ${self.sidebar()}
19
19
20 <div class="main-content">
20 <div class="main-content">
21
21
22 <h2>Collapsable Content</h2>
22 <h2>Collapsable Content</h2>
23 <p>Where a section may have a very long list of information, it can be desirable to use collapsable content. There is a premade function for showing/hiding elements, though its use may or may not be practical, depending on the situation. Use it, or don't, on a case-by-case basis.</p>
23 <p>Where a section may have a very long list of information, it can be desirable to use collapsable content. There is a premade function for showing/hiding elements, though its use may or may not be practical, depending on the situation. Use it, or don't, on a case-by-case basis.</p>
24
24
25 <p><strong>To use the collapsable-content function:</strong> Create a toggle button using <code>&lt;div class="btn-collapse"&gt;Show More&lt;/div&gt;</code> and a data attribute using <code>data-toggle</code>. Clicking this button will toggle any sibling element(s) containing the class <code>collapsable-content</code> and an identical <code>data-toggle</code> attribute. It will also change the button to read "Show Less"; another click toggles it back to the previous state. Ideally, use pre-existing elements and add the class and attribute; creating a new div around the existing content may lead to unexpected results, as the toggle function will use <code>display:block</code> if no previous display specification was found.
25 <p><strong>To use the collapsable-content function:</strong> Create a toggle button using <code>&lt;div class="btn-collapse"&gt;Show More&lt;/div&gt;</code> and a data attribute using <code>data-toggle</code>. Clicking this button will toggle any sibling element(s) containing the class <code>collapsable-content</code> and an identical <code>data-toggle</code> attribute. It will also change the button to read "Show Less"; another click toggles it back to the previous state. Ideally, use pre-existing elements and add the class and attribute; creating a new div around the existing content may lead to unexpected results, as the toggle function will use <code>display:block</code> if no previous display specification was found.
26 </p>
26 </p>
27 <p>Notes:</p>
27 <p>Notes:</p>
28 <ul>
28 <ul>
29 <li>Changes made to the text of the button will require adjustment to the function, but for the sake of consistency and user experience, this is best avoided. </li>
29 <li>Changes made to the text of the button will require adjustment to the function, but for the sake of consistency and user experience, this is best avoided. </li>
30 <li>Collapsable content inside of a pjax loaded container will require <code>collapsableContent();</code> to be called from within the container. No variables are necessary.</li>
30 <li>Collapsable content inside of a pjax loaded container will require <code>collapsableContent();</code> to be called from within the container. No variables are necessary.</li>
31 </ul>
31 </ul>
32
32
33 </div> <!-- .main-content -->
33 </div> <!-- .main-content -->
34 </div> <!-- .sidebar-col-wrapper -->
34 </div> <!-- .sidebar-col-wrapper -->
35 </div> <!-- .box -->
35 </div> <!-- .box -->
36
36
37 <!-- CONTENT -->
37 <!-- CONTENT -->
38 <div id="content" class="wrapper">
38 <div id="content" class="wrapper">
39
39
40 <div class="main">
40 <div class="main">
41
41
42 <div class="box">
42 <div class="box">
43 <div class="title">
43 <div class="title">
44 <h1>
44 <h1>
45 Diff: enable filename with spaces on diffs
45 Diff: enable filename with spaces on diffs
46 </h1>
46 </h1>
47 <h1>
47 <h1>
48 <i class="icon-hg" ></i>
48 <i class="icon-hg" ></i>
49
49
50 <i class="icon-lock"></i>
50 <i class="icon-lock"></i>
51 <span><a href="/rhodecode-momentum">rhodecode-momentum</a></span>
51 <span><a href="/rhodecode-momentum">rhodecode-momentum</a></span>
52
52
53 </h1>
53 </h1>
54 </div>
54 </div>
55
55
56 <div class="box pr-summary">
56 <div class="box pr-summary">
57 <div class="summary-details block-left">
57 <div class="summary-details block-left">
58
58
59 <div class="pr-details-title">
59 <div class="pr-details-title">
60
60
61 Pull request #720 From Tue, 17 Feb 2015 16:21:38
61 Pull request #720 From Tue, 17 Feb 2015 16:21:38
62 <div class="btn-collapse" data-toggle="description">Show More</div>
62 <div class="btn-collapse" data-toggle="description">Show More</div>
63 </div>
63 </div>
64 <div id="summary" class="fields pr-details-content">
64 <div id="summary" class="fields pr-details-content">
65 <div class="field">
65 <div class="field">
66 <div class="label-summary">
66 <div class="label-summary">
67 <label>Origin:</label>
67 <label>Origin:</label>
68 </div>
68 </div>
69 <div class="input">
69 <div class="input">
70 <div>
70 <div>
71 <span class="tag">
71 <span class="tag">
72 <a href="/andersonsantos/rhodecode-momentum-fork#fix_574">book: fix_574</a>
72 <a href="/andersonsantos/rhodecode-momentum-fork#fix_574">book: fix_574</a>
73 </span>
73 </span>
74 <span class="clone-url">
74 <span class="clone-url">
75 <a href="/andersonsantos/rhodecode-momentum-fork">https://code.rhodecode.com/andersonsantos/rhodecode-momentum-fork</a>
75 <a href="/andersonsantos/rhodecode-momentum-fork">https://code.rhodecode.com/andersonsantos/rhodecode-momentum-fork</a>
76 </span>
76 </span>
77 </div>
77 </div>
78 <div>
78 <div>
79 <br>
79 <br>
80 <input type="text" value="hg pull -r 46b3d50315f0 https://code.rhodecode.com/andersonsantos/rhodecode-momentum-fork" readonly="readonly">
80 <input type="text" value="hg pull -r 46b3d50315f0 https://code.rhodecode.com/andersonsantos/rhodecode-momentum-fork" readonly="readonly">
81 </div>
81 </div>
82 </div>
82 </div>
83 </div>
83 </div>
84 <div class="field">
84 <div class="field">
85 <div class="label-summary">
85 <div class="label-summary">
86 <label>Review:</label>
86 <label>Review:</label>
87 </div>
87 </div>
88 <div class="input">
88 <div class="input">
89 <div class="flag_status under_review tooltip pull-left" title="Pull request status calculated from votes"></div>
89 <div class="flag_status under_review tooltip pull-left" title="Pull request status calculated from votes"></div>
90 <span class="changeset-status-lbl tooltip" title="Pull request status calculated from votes">
90 <span class="changeset-status-lbl tooltip" title="Pull request status calculated from votes">
91 Under Review
91 Under Review
92 </span>
92 </span>
93
93
94 </div>
94 </div>
95 </div>
95 </div>
96 <div class="field collapsable-content" data-toggle="description">
96 <div class="field collapsable-content" data-toggle="description">
97 <div class="label-summary">
97 <div class="label-summary">
98 <label>Description:</label>
98 <label>Description:</label>
99 </div>
99 </div>
100 <div class="input">
100 <div class="input">
101 <div class="pr-description">Fixing issue <a class="issue- tracker-link" href="http://bugs.rhodecode.com/issues/574"># 574</a>, changing regex for capturing filenames</div>
101 <div class="pr-description">Fixing issue <a class="issue- tracker-link" href="http://bugs.rhodecode.com/issues/574"># 574</a>, changing regex for capturing filenames</div>
102 </div>
102 </div>
103 </div>
103 </div>
104 <div class="field collapsable-content" data-toggle="description">
104 <div class="field collapsable-content" data-toggle="description">
105 <div class="label-summary">
105 <div class="label-summary">
106 <label>Comments:</label>
106 <label>Comments:</label>
107 </div>
107 </div>
108 <div class="input">
108 <div class="input">
109 <div>
109 <div>
110 <div class="comments-number">
110 <div class="comments-number">
111 <a href="#inline-comments-container">0 Pull request comments</a>,
111 <a href="#inline-comments-container">0 Pull request comments</a>,
112 0 Inline Comments
112 0 Inline Comments
113 </div>
113 </div>
114 </div>
114 </div>
115 </div>
115 </div>
116 </div>
116 </div>
117 </div>
117 </div>
118 </div>
118 </div>
119 <div>
119 <div>
120 <div class="reviewers-title block-right">
120 <div class="reviewers-title block-right">
121 <div class="pr-details-title">
121 <div class="pr-details-title">
122 Author
122 Author
123 </div>
123 </div>
124 </div>
124 </div>
125 <div class="block-right pr-details-content reviewers">
125 <div class="block-right pr-details-content reviewers">
126 <ul class="group_members">
126 <ul class="group_members">
127 <li>
127 <li>
128 <img class="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=32" height="16" width="16">
128 <img class="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=32" height="16" width="16">
129 <span class="user"> <a href="/_profiles/lolek">lolek (Lolek Santos)</a></span>
129 <span class="user"> <a href="/_profiles/lolek">lolek (Lolek Santos)</a></span>
130 </li>
130 </li>
131 </ul>
131 </ul>
132 </div>
132 </div>
133 <div class="reviewers-title block-right">
133 <div class="reviewers-title block-right">
134 <div class="pr-details-title">
134 <div class="pr-details-title">
135 Pull request reviewers
135 Pull request reviewers
136 <span class="btn-collapse" data-toggle="reviewers">Show More</span>
136 <span class="btn-collapse" data-toggle="reviewers">Show More</span>
137 </div>
137 </div>
138
138
139 </div>
139 </div>
140 <div id="reviewers" class="block-right pr-details-content reviewers">
140 <div id="reviewers" class="block-right pr-details-content reviewers">
141
141
142 <ul id="review_members" class="group_members">
142 <ul id="review_members" class="group_members">
143 <li id="reviewer_70">
143 <li id="reviewer_70">
144 <div class="reviewers_member">
144 <div class="reviewers_member">
145 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
145 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
146 <div class="flag_status rejected pull-left reviewer_member_status"></div>
146 <div class="flag_status rejected pull-left reviewer_member_status"></div>
147 </div>
147 </div>
148 <img class="gravatar" src="https://secure.gravatar.com/avatar/153a0fab13160b3e64a2cbc7c0373506?d=identicon&amp;s=32" height="16" width="16">
148 <img class="gravatar" src="https://secure.gravatar.com/avatar/153a0fab13160b3e64a2cbc7c0373506?d=identicon&amp;s=32" height="16" width="16">
149 <span class="user"> <a href="/_profiles/jenkins-tests">jenkins-tests</a> (reviewer)</span>
149 <span class="user"> <a href="/_profiles/jenkins-tests">jenkins-tests</a> (reviewer)</span>
150 </div>
150 </div>
151 <input id="reviewer_70_input" type="hidden" value="70" name="review_members">
151 <input id="reviewer_70_input" type="hidden" value="70" name="review_members">
152 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(70, true)" style="visibility: hidden;">
152 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(70, true)" style="visibility: hidden;">
153 <i class="icon-remove-sign"></i>
153 <i class="icon-remove-sign"></i>
154 </div>
154 </div>
155 </li>
155 </li>
156 <li id="reviewer_33" class="collapsable-content" data-toggle="reviewers">
156 <li id="reviewer_33" class="collapsable-content" data-toggle="reviewers">
157 <div class="reviewers_member">
157 <div class="reviewers_member">
158 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
158 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
159 <div class="flag_status approved pull-left reviewer_member_status"></div>
159 <div class="flag_status approved pull-left reviewer_member_status"></div>
160 </div>
160 </div>
161 <img class="gravatar" src="https://secure.gravatar.com/avatar/ffd6a317ec2b66be880143cd8459d0d9?d=identicon&amp;s=32" height="16" width="16">
161 <img class="gravatar" src="https://secure.gravatar.com/avatar/ffd6a317ec2b66be880143cd8459d0d9?d=identicon&amp;s=32" height="16" width="16">
162 <span class="user"> <a href="/_profiles/jenkins-tests">garbas (Rok Garbas)</a> (reviewer)</span>
162 <span class="user"> <a href="/_profiles/jenkins-tests">garbas (Rok Garbas)</a> (reviewer)</span>
163 </div>
163 </div>
164 </li>
164 </li>
165 <li id="reviewer_2" class="collapsable-content" data-toggle="reviewers">
165 <li id="reviewer_2" class="collapsable-content" data-toggle="reviewers">
166 <div class="reviewers_member">
166 <div class="reviewers_member">
167 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
167 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
168 <div class="flag_status not_reviewed pull-left reviewer_member_status"></div>
168 <div class="flag_status not_reviewed pull-left reviewer_member_status"></div>
169 </div>
169 </div>
170 <img class="gravatar" src="https://secure.gravatar.com/avatar/aad9d40cac1259ea39b5578554ad9d64?d=identicon&amp;s=32" height="16" width="16">
170 <img class="gravatar" src="https://secure.gravatar.com/avatar/aad9d40cac1259ea39b5578554ad9d64?d=identicon&amp;s=32" height="16" width="16">
171 <span class="user"> <a href="/_profiles/jenkins-tests">marcink (Marcin Kuzminski)</a> (reviewer)</span>
171 <span class="user"> <a href="/_profiles/jenkins-tests">marcink (Marcin Kuzminski)</a> (reviewer)</span>
172 </div>
172 </div>
173 </li>
173 </li>
174 <li id="reviewer_36" class="collapsable-content" data-toggle="reviewers">
174 <li id="reviewer_36" class="collapsable-content" data-toggle="reviewers">
175 <div class="reviewers_member">
175 <div class="reviewers_member">
176 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
176 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
177 <div class="flag_status approved pull-left reviewer_member_status"></div>
177 <div class="flag_status approved pull-left reviewer_member_status"></div>
178 </div>
178 </div>
179 <img class="gravatar" src="https://secure.gravatar.com/avatar/7a4da001a0af0016ed056ab523255db9?d=identicon&amp;s=32" height="16" width="16">
179 <img class="gravatar" src="https://secure.gravatar.com/avatar/7a4da001a0af0016ed056ab523255db9?d=identicon&amp;s=32" height="16" width="16">
180 <span class="user"> <a href="/_profiles/jenkins-tests">johbo (Johannes Bornhold)</a> (reviewer)</span>
180 <span class="user"> <a href="/_profiles/jenkins-tests">johbo (Johannes Bornhold)</a> (reviewer)</span>
181 </div>
181 </div>
182 </li>
182 </li>
183 <li id="reviewer_47" class="collapsable-content" data-toggle="reviewers">
183 <li id="reviewer_47" class="collapsable-content" data-toggle="reviewers">
184 <div class="reviewers_member">
184 <div class="reviewers_member">
185 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
185 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
186 <div class="flag_status under_review pull-left reviewer_member_status"></div>
186 <div class="flag_status under_review pull-left reviewer_member_status"></div>
187 </div>
187 </div>
188 <img class="gravatar" src="https://secure.gravatar.com/avatar/8f6dc00dce79d6bd7d415be5cea6a008?d=identicon&amp;s=32" height="16" width="16">
188 <img class="gravatar" src="https://secure.gravatar.com/avatar/8f6dc00dce79d6bd7d415be5cea6a008?d=identicon&amp;s=32" height="16" width="16">
189 <span class="user"> <a href="/_profiles/jenkins-tests">lisaq (Lisa Quatmann)</a> (reviewer)</span>
189 <span class="user"> <a href="/_profiles/jenkins-tests">lisaq (Lisa Quatmann)</a> (reviewer)</span>
190 </div>
190 </div>
191 </li>
191 </li>
192 <li id="reviewer_49" class="collapsable-content" data-toggle="reviewers">
192 <li id="reviewer_49" class="collapsable-content" data-toggle="reviewers">
193 <div class="reviewers_member">
193 <div class="reviewers_member">
194 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
194 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
195 <div class="flag_status approved pull-left reviewer_member_status"></div>
195 <div class="flag_status approved pull-left reviewer_member_status"></div>
196 </div>
196 </div>
197 <img class="gravatar" src="https://secure.gravatar.com/avatar/89f722927932a8f737a0feafb03a606e?d=identicon&amp;s=32" height="16" width="16">
197 <img class="gravatar" src="https://secure.gravatar.com/avatar/89f722927932a8f737a0feafb03a606e?d=identicon&amp;s=32" height="16" width="16">
198 <span class="user"> <a href="/_profiles/jenkins-tests">paris (Paris Kolios)</a> (reviewer)</span>
198 <span class="user"> <a href="/_profiles/jenkins-tests">paris (Paris Kolios)</a> (reviewer)</span>
199 </div>
199 </div>
200 </li>
200 </li>
201 <li id="reviewer_50" class="collapsable-content" data-toggle="reviewers">
201 <li id="reviewer_50" class="collapsable-content" data-toggle="reviewers">
202 <div class="reviewers_member">
202 <div class="reviewers_member">
203 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
203 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
204 <div class="flag_status approved pull-left reviewer_member_status"></div>
204 <div class="flag_status approved pull-left reviewer_member_status"></div>
205 </div>
205 </div>
206 <img class="gravatar" src="https://secure.gravatar.com/avatar/081322c975e8545ec269372405fbd016?d=identicon&amp;s=32" height="16" width="16">
206 <img class="gravatar" src="https://secure.gravatar.com/avatar/081322c975e8545ec269372405fbd016?d=identicon&amp;s=32" height="16" width="16">
207 <span class="user"> <a href="/_profiles/jenkins-tests">ergo (Marcin Lulek)</a> (reviewer)</span>
207 <span class="user"> <a href="/_profiles/jenkins-tests">ergo (Marcin Lulek)</a> (reviewer)</span>
208 </div>
208 </div>
209 </li>
209 </li>
210 <li id="reviewer_54" class="collapsable-content" data-toggle="reviewers">
210 <li id="reviewer_54" class="collapsable-content" data-toggle="reviewers">
211 <div class="reviewers_member">
211 <div class="reviewers_member">
212 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
212 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
213 <div class="flag_status under_review pull-left reviewer_member_status"></div>
213 <div class="flag_status under_review pull-left reviewer_member_status"></div>
214 </div>
214 </div>
215 <img class="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=32" height="16" width="16">
215 <img class="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=32" height="16" width="16">
216 <span class="user"> <a href="/_profiles/jenkins-tests">anderson (Anderson Santos)</a> (reviewer)</span>
216 <span class="user"> <a href="/_profiles/jenkins-tests">anderson (Anderson Santos)</a> (reviewer)</span>
217 </div>
217 </div>
218 </li>
218 </li>
219 <li id="reviewer_57" class="collapsable-content" data-toggle="reviewers">
219 <li id="reviewer_57" class="collapsable-content" data-toggle="reviewers">
220 <div class="reviewers_member">
220 <div class="reviewers_member">
221 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
221 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
222 <div class="flag_status approved pull-left reviewer_member_status"></div>
222 <div class="flag_status approved pull-left reviewer_member_status"></div>
223 </div>
223 </div>
224 <img class="gravatar" src="https://secure.gravatar.com/avatar/23e2ee8f5fd462cba8129a40cc1e896c?d=identicon&amp;s=32" height="16" width="16">
224 <img class="gravatar" src="https://secure.gravatar.com/avatar/23e2ee8f5fd462cba8129a40cc1e896c?d=identicon&amp;s=32" height="16" width="16">
225 <span class="user"> <a href="/_profiles/jenkins-tests">gmgauthier (Greg Gauthier)</a> (reviewer)</span>
225 <span class="user"> <a href="/_profiles/jenkins-tests">gmgauthier (Greg Gauthier)</a> (reviewer)</span>
226 </div>
226 </div>
227 </li>
227 </li>
228 <li id="reviewer_31" class="collapsable-content" data-toggle="reviewers">
228 <li id="reviewer_31" class="collapsable-content" data-toggle="reviewers">
229 <div class="reviewers_member">
229 <div class="reviewers_member">
230 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
230 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
231 <div class="flag_status under_review pull-left reviewer_member_status"></div>
231 <div class="flag_status under_review pull-left reviewer_member_status"></div>
232 </div>
232 </div>
233 <img class="gravatar" src="https://secure.gravatar.com/avatar/0c9a7e6674b6f0b35d98dbe073e3f0ab?d=identicon&amp;s=32" height="16" width="16">
233 <img class="gravatar" src="https://secure.gravatar.com/avatar/0c9a7e6674b6f0b35d98dbe073e3f0ab?d=identicon&amp;s=32" height="16" width="16">
234 <span class="user"> <a href="/_profiles/jenkins-tests">ostrobel (Oliver Strobel)</a> (reviewer)</span>
234 <span class="user"> <a href="/_profiles/jenkins-tests">ostrobel (Oliver Strobel)</a> (reviewer)</span>
235 </div>
235 </div>
236 </li>
236 </li>
237 </ul>
237 </ul>
238 <div id="add_reviewer_input" class="ac" style="display: none;">
238 <div id="add_reviewer_input" class="ac" style="display: none;">
239 </div>
239 </div>
240 </div>
240 </div>
241 </div>
241 </div>
242 </div>
242 </div>
243 </div>
243 </div>
244 <div class="box">
244 <div class="box">
245 <div class="table" >
245 <div class="table" >
246 <div id="changeset_compare_view_content">
246 <div id="changeset_compare_view_content">
247 <div class="compare_view_commits_title">
247 <div class="compare_view_commits_title">
248 <h2>Compare View: 6 commits<span class="btn-collapse" data-toggle="commits">Show More</span></h2>
248 <h2>Compare View: 6 commits<span class="btn-collapse" data-toggle="commits">Show More</span></h2>
249
249
250 </div>
250 </div>
251 <div class="container">
251 <div class="container">
252
252
253
253
254 <table class="rctable compare_view_commits">
254 <table class="rctable compare_view_commits">
255 <tr>
255 <tr>
256 <th>Time</th>
256 <th>Time</th>
257 <th>Author</th>
257 <th>Author</th>
258 <th>Commit</th>
258 <th>Commit</th>
259 <th></th>
259 <th></th>
260 <th>Title</th>
260 <th>Title</th>
261 </tr>
261 </tr>
262 <tr id="row-7e83e5cd7812dd9e055ce30e77c65cdc08154b43" commit_id="7e83e5cd7812dd9e055ce30e77c65cdc08154b43" class="compare_select">
262 <tr id="row-7e83e5cd7812dd9e055ce30e77c65cdc08154b43" commit_id="7e83e5cd7812dd9e055ce30e77c65cdc08154b43" class="compare_select">
263 <td class="td-time">
263 <td class="td-time">
264 <span class="tooltip" title="3 hours and 23 minutes ago" tt_title="3 hours and 23 minutes ago">2015-02-18 10:13:34</span>
264 <span class="tooltip" title="3 hours and 23 minutes ago" tt_title="3 hours and 23 minutes ago">2015-02-18 10:13:34</span>
265 </td>
265 </td>
266 <td class="td-user">
266 <td class="td-user">
267 <div class="gravatar_with_user">
267 <div class="gravatar_with_user">
268 <img class="gravatar" alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=16">
268 <img class="gravatar" alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=16">
269 <span title="Lolek Santos <lolek@rhodecode.com>" class="user">brian (Brian Butler)</span>
269 <span title="Lolek Santos <lolek@rhodecode.com>" class="user">brian (Brian Butler)</span>
270 </div>
270 </div>
271 </td>
271 </td>
272 <td class="td-hash">
272 <td class="td-hash">
273 <code>
273 <code>
274 <a href="/brian/documentation-rep/changeset/7e83e5cd7812dd9e055ce30e77c65cdc08154b43">r395:7e83e5cd7812</a>
274 <a href="/brian/documentation-rep/changeset/7e83e5cd7812dd9e055ce30e77c65cdc08154b43">r395:7e83e5cd7812</a>
275 </code>
275 </code>
276 </td>
276 </td>
277 <td class="expand_commit" data-commit-id="7e83e5cd7812dd9e055ce30e77c65cdc08154b43" title="Expand commit message">
277 <td class="expand_commit" data-commit-id="7e83e5cd7812dd9e055ce30e77c65cdc08154b43" title="Expand commit message">
278 <div class="show_more_col">
278 <div class="show_more_col">
279 <i class="show_more"></i>
279 <i class="show_more"></i>
280 </div>
280 </div>
281 </td>
281 </td>
282 <td class="mid td-description">
282 <td class="mid td-description">
283 <div class="log-container truncate-wrap">
283 <div class="log-container truncate-wrap">
284 <div id="c-7e83e5cd7812dd9e055ce30e77c65cdc08154b43" class="message truncate">rep: added how we doc to guide</div>
284 <div id="c-7e83e5cd7812dd9e055ce30e77c65cdc08154b43" class="message truncate">rep: added how we doc to guide</div>
285 </div>
285 </div>
286 </td>
286 </td>
287 </tr>
287 </tr>
288 <tr id="row-48ce1581bdb3aa7679c246cbdd3fb030623f5c87" commit_id="48ce1581bdb3aa7679c246cbdd3fb030623f5c87" class="compare_select">
288 <tr id="row-48ce1581bdb3aa7679c246cbdd3fb030623f5c87" commit_id="48ce1581bdb3aa7679c246cbdd3fb030623f5c87" class="compare_select">
289 <td class="td-time">
289 <td class="td-time">
290 <span class="tooltip" title="4 hours and 18 minutes ago">2015-02-18 09:18:31</span>
290 <span class="tooltip" title="4 hours and 18 minutes ago">2015-02-18 09:18:31</span>
291 </td>
291 </td>
292 <td class="td-user">
292 <td class="td-user">
293 <div class="gravatar_with_user">
293 <div class="gravatar_with_user">
294 <img class="gravatar" alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=16">
294 <img class="gravatar" alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=16">
295 <span title="Lolek Santos <lolek@rhodecode.com>" class="user">brian (Brian Butler)</span>
295 <span title="Lolek Santos <lolek@rhodecode.com>" class="user">brian (Brian Butler)</span>
296 </div>
296 </div>
297 </td>
297 </td>
298 <td class="td-hash">
298 <td class="td-hash">
299 <code>
299 <code>
300 <a href="/brian/documentation-rep/changeset/48ce1581bdb3aa7679c246cbdd3fb030623f5c87">r394:48ce1581bdb3</a>
300 <a href="/brian/documentation-rep/changeset/48ce1581bdb3aa7679c246cbdd3fb030623f5c87">r394:48ce1581bdb3</a>
301 </code>
301 </code>
302 </td>
302 </td>
303 <td class="expand_commit" data-commit-id="48ce1581bdb3aa7679c246cbdd3fb030623f5c87" title="Expand commit message">
303 <td class="expand_commit" data-commit-id="48ce1581bdb3aa7679c246cbdd3fb030623f5c87" title="Expand commit message">
304 <div class="show_more_col">
304 <div class="show_more_col">
305 <i class="show_more"></i>
305 <i class="show_more"></i>
306 </div>
306 </div>
307 </td>
307 </td>
308 <td class="mid td-description">
308 <td class="mid td-description">
309 <div class="log-container truncate-wrap">
309 <div class="log-container truncate-wrap">
310 <div id="c-48ce1581bdb3aa7679c246cbdd3fb030623f5c87" class="message truncate">repo 0004 - typo</div>
310 <div id="c-48ce1581bdb3aa7679c246cbdd3fb030623f5c87" class="message truncate">repo 0004 - typo</div>
311 </div>
311 </div>
312 </td>
312 </td>
313 </tr>
313 </tr>
314 <tr id="row-982d857aafb4c71e7686e419c32b71c9a837257d" commit_id="982d857aafb4c71e7686e419c32b71c9a837257d" class="compare_select collapsable-content" data-toggle="commits">
314 <tr id="row-982d857aafb4c71e7686e419c32b71c9a837257d" commit_id="982d857aafb4c71e7686e419c32b71c9a837257d" class="compare_select collapsable-content" data-toggle="commits">
315 <td class="td-time">
315 <td class="td-time">
316 <span class="tooltip" title="4 hours and 22 minutes ago">2015-02-18 09:14:45</span>
316 <span class="tooltip" title="4 hours and 22 minutes ago">2015-02-18 09:14:45</span>
317 </td>
317 </td>
318 <td class="td-user">
318 <td class="td-user">
319 <span class="gravatar" commit_id="982d857aafb4c71e7686e419c32b71c9a837257d">
319 <span class="gravatar" commit_id="982d857aafb4c71e7686e419c32b71c9a837257d">
320 <img alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=28" height="14" width="14">
320 <img alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=28" height="14" width="14">
321 </span>
321 </span>
322 <span class="author">brian (Brian Butler)</span>
322 <span class="author">brian (Brian Butler)</span>
323 </td>
323 </td>
324 <td class="td-hash">
324 <td class="td-hash">
325 <code>
325 <code>
326 <a href="/brian/documentation-rep/changeset/982d857aafb4c71e7686e419c32b71c9a837257d">r393:982d857aafb4</a>
326 <a href="/brian/documentation-rep/changeset/982d857aafb4c71e7686e419c32b71c9a837257d">r393:982d857aafb4</a>
327 </code>
327 </code>
328 </td>
328 </td>
329 <td class="expand_commit" data-commit-id="982d857aafb4c71e7686e419c32b71c9a837257d" title="Expand commit message">
329 <td class="expand_commit" data-commit-id="982d857aafb4c71e7686e419c32b71c9a837257d" title="Expand commit message">
330 <div class="show_more_col">
330 <div class="show_more_col">
331 <i class="show_more"></i>
331 <i class="show_more"></i>
332 </div>
332 </div>
333 </td>
333 </td>
334 <td class="mid td-description">
334 <td class="mid td-description">
335 <div class="log-container truncate-wrap">
335 <div class="log-container truncate-wrap">
336 <div id="c-982d857aafb4c71e7686e419c32b71c9a837257d" class="message truncate">internals: how to doc section added</div>
336 <div id="c-982d857aafb4c71e7686e419c32b71c9a837257d" class="message truncate">internals: how to doc section added</div>
337 </div>
337 </div>
338 </td>
338 </td>
339 </tr>
339 </tr>
340 <tr id="row-4c7258ad1af6dae91bbaf87a933e3597e676fab8" commit_id="4c7258ad1af6dae91bbaf87a933e3597e676fab8" class="compare_select collapsable-content" data-toggle="commits">
340 <tr id="row-4c7258ad1af6dae91bbaf87a933e3597e676fab8" commit_id="4c7258ad1af6dae91bbaf87a933e3597e676fab8" class="compare_select collapsable-content" data-toggle="commits">
341 <td class="td-time">
341 <td class="td-time">
342 <span class="tooltip" title="20 hours and 16 minutes ago">2015-02-17 17:20:44</span>
342 <span class="tooltip" title="20 hours and 16 minutes ago">2015-02-17 17:20:44</span>
343 </td>
343 </td>
344 <td class="td-user">
344 <td class="td-user">
345 <span class="gravatar" commit_id="4c7258ad1af6dae91bbaf87a933e3597e676fab8">
345 <span class="gravatar" commit_id="4c7258ad1af6dae91bbaf87a933e3597e676fab8">
346 <img alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=28" height="14" width="14">
346 <img alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=28" height="14" width="14">
347 </span>
347 </span>
348 <span class="author">brian (Brian Butler)</span>
348 <span class="author">brian (Brian Butler)</span>
349 </td>
349 </td>
350 <td class="td-hash">
350 <td class="td-hash">
351 <code>
351 <code>
352 <a href="/brian/documentation-rep/changeset/4c7258ad1af6dae91bbaf87a933e3597e676fab8">r392:4c7258ad1af6</a>
352 <a href="/brian/documentation-rep/changeset/4c7258ad1af6dae91bbaf87a933e3597e676fab8">r392:4c7258ad1af6</a>
353 </code>
353 </code>
354 </td>
354 </td>
355 <td class="expand_commit" data-commit-id="4c7258ad1af6dae91bbaf87a933e3597e676fab8" title="Expand commit message">
355 <td class="expand_commit" data-commit-id="4c7258ad1af6dae91bbaf87a933e3597e676fab8" title="Expand commit message">
356 <div class="show_more_col">
356 <div class="show_more_col">
357 <i class="show_more"></i>
357 <i class="show_more"></i>
358 </div>
358 </div>
359 </td>
359 </td>
360 <td class="mid td-description">
360 <td class="mid td-description">
361 <div class="log-container truncate-wrap">
361 <div class="log-container truncate-wrap">
362 <div id="c-4c7258ad1af6dae91bbaf87a933e3597e676fab8" class="message truncate">REP: 0004 Documentation standards</div>
362 <div id="c-4c7258ad1af6dae91bbaf87a933e3597e676fab8" class="message truncate">REP: 0004 Documentation standards</div>
363 </div>
363 </div>
364 </td>
364 </td>
365 </tr>
365 </tr>
366 <tr id="row-46b3d50315f0f2b1f64485ac95af4f384948f9cb" commit_id="46b3d50315f0f2b1f64485ac95af4f384948f9cb" class="compare_select collapsable-content" data-toggle="commits">
366 <tr id="row-46b3d50315f0f2b1f64485ac95af4f384948f9cb" commit_id="46b3d50315f0f2b1f64485ac95af4f384948f9cb" class="compare_select collapsable-content" data-toggle="commits">
367 <td class="td-time">
367 <td class="td-time">
368 <span class="tooltip" title="18 hours and 19 minutes ago">2015-02-17 16:18:49</span>
368 <span class="tooltip" title="18 hours and 19 minutes ago">2015-02-17 16:18:49</span>
369 </td>
369 </td>
370 <td class="td-user">
370 <td class="td-user">
371 <span class="gravatar" commit_id="46b3d50315f0f2b1f64485ac95af4f384948f9cb">
371 <span class="gravatar" commit_id="46b3d50315f0f2b1f64485ac95af4f384948f9cb">
372 <img alt="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=28" height="14" width="14">
372 <img alt="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=28" height="14" width="14">
373 </span>
373 </span>
374 <span class="author">anderson (Anderson Santos)</span>
374 <span class="author">anderson (Anderson Santos)</span>
375 </td>
375 </td>
376 <td class="td-hash">
376 <td class="td-hash">
377 <code>
377 <code>
378 <a href="/andersonsantos/rhodecode-momentum-fork/changeset/46b3d50315f0f2b1f64485ac95af4f384948f9cb">r8743:46b3d50315f0</a>
378 <a href="/andersonsantos/rhodecode-momentum-fork/changeset/46b3d50315f0f2b1f64485ac95af4f384948f9cb">r8743:46b3d50315f0</a>
379 </code>
379 </code>
380 </td>
380 </td>
381 <td class="expand_commit" data-commit-id="46b3d50315f0f2b1f64485ac95af4f384948f9cb" title="Expand commit message">
381 <td class="expand_commit" data-commit-id="46b3d50315f0f2b1f64485ac95af4f384948f9cb" title="Expand commit message">
382 <div class="show_more_col">
382 <div class="show_more_col">
383 <i class="show_more" ></i>
383 <i class="show_more" ></i>
384 </div>
384 </div>
385 </td>
385 </td>
386 <td class="mid td-description">
386 <td class="mid td-description">
387 <div class="log-container truncate-wrap">
387 <div class="log-container truncate-wrap">
388 <div id="c-46b3d50315f0f2b1f64485ac95af4f384948f9cb" class="message truncate">Diff: created tests for the diff with filenames with spaces</div>
388 <div id="c-46b3d50315f0f2b1f64485ac95af4f384948f9cb" class="message truncate">Diff: created tests for the diff with filenames with spaces</div>
389
389
390 </div>
390 </div>
391 </td>
391 </td>
392 </tr>
392 </tr>
393 <tr id="row-1e57d2549bd6c34798075bf05ac39f708bb33b90" commit_id="1e57d2549bd6c34798075bf05ac39f708bb33b90" class="compare_select collapsable-content" data-toggle="commits">
393 <tr id="row-1e57d2549bd6c34798075bf05ac39f708bb33b90" commit_id="1e57d2549bd6c34798075bf05ac39f708bb33b90" class="compare_select collapsable-content" data-toggle="commits">
394 <td class="td-time">
394 <td class="td-time">
395 <span class="tooltip" title="2 days ago">2015-02-16 10:06:08</span>
395 <span class="tooltip" title="2 days ago">2015-02-16 10:06:08</span>
396 </td>
396 </td>
397 <td class="td-user">
397 <td class="td-user">
398 <span class="gravatar" commit_id="1e57d2549bd6c34798075bf05ac39f708bb33b90">
398 <span class="gravatar" commit_id="1e57d2549bd6c34798075bf05ac39f708bb33b90">
399 <img alt="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=28" height="14" width="14">
399 <img alt="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=28" height="14" width="14">
400 </span>
400 </span>
401 <span class="author">anderson (Anderson Santos)</span>
401 <span class="author">anderson (Anderson Santos)</span>
402 </td>
402 </td>
403 <td class="td-hash">
403 <td class="td-hash">
404 <code>
404 <code>
405 <a href="/andersonsantos/rhodecode-momentum-fork/changeset/1e57d2549bd6c34798075bf05ac39f708bb33b90">r8742:1e57d2549bd6</a>
405 <a href="/andersonsantos/rhodecode-momentum-fork/changeset/1e57d2549bd6c34798075bf05ac39f708bb33b90">r8742:1e57d2549bd6</a>
406 </code>
406 </code>
407 </td>
407 </td>
408 <td class="expand_commit" data-commit-id="1e57d2549bd6c34798075bf05ac39f708bb33b90" title="Expand commit message">
408 <td class="expand_commit" data-commit-id="1e57d2549bd6c34798075bf05ac39f708bb33b90" title="Expand commit message">
409 <div class="show_more_col">
409 <div class="show_more_col">
410 <i class="show_more" ></i>
410 <i class="show_more" ></i>
411 </div>
411 </div>
412 </td>
412 </td>
413 <td class="mid td-description">
413 <td class="mid td-description">
414 <div class="log-container truncate-wrap">
414 <div class="log-container truncate-wrap">
415 <div id="c-1e57d2549bd6c34798075bf05ac39f708bb33b90" class="message truncate">Diff: fix renaming files with spaces <a class="issue-tracker-link" href="http://bugs.rhodecode.com/issues/574">#574</a></div>
415 <div id="c-1e57d2549bd6c34798075bf05ac39f708bb33b90" class="message truncate">Diff: fix renaming files with spaces <a class="issue-tracker-link" href="http://bugs.rhodecode.com/issues/574">#574</a></div>
416
416
417 </div>
417 </div>
418 </td>
418 </td>
419 </tr>
419 </tr>
420 </table>
420 </table>
421 </div>
421 </div>
422
422
423 <script>
423 <script>
424 $('.expand_commit').on('click',function(e){
424 $('.expand_commit').on('click',function(e){
425 $(this).children('i').hide();
425 $(this).children('i').hide();
426 var cid = $(this).data('commitId');
426 var cid = $(this).data('commitId');
427 $('#c-'+cid).css({'height': 'auto', 'margin': '.65em 1em .65em 0','white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'})
427 $('#c-'+cid).css({'height': 'auto', 'margin': '.65em 1em .65em 0','white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'})
428 $('#t-'+cid).css({'height': 'auto', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'})
428 $('#t-'+cid).css({'height': 'auto', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'})
429 });
429 });
430 $('.compare_select').on('click',function(e){
430 $('.compare_select').on('click',function(e){
431 var cid = $(this).attr('commit_id');
431 var cid = $(this).attr('commit_id');
432 $('#row-'+cid).toggleClass('hl', !$('#row-'+cid).hasClass('hl'));
432 $('#row-'+cid).toggleClass('hl', !$('#row-'+cid).hasClass('hl'));
433 });
433 });
434 </script>
434 </script>
435 <div class="cs_files_title">
435 <div class="cs_files_title">
436 <span class="cs_files_expand">
436 <span class="cs_files_expand">
437 <span id="expand_all_files">Expand All</span> | <span id="collapse_all_files">Collapse All</span>
437 <span id="expand_all_files">Expand All</span> | <span id="collapse_all_files">Collapse All</span>
438 </span>
438 </span>
439 <h2>
439 <h2>
440 7 files changed: 55 inserted, 9 deleted
440 7 files changed: 55 inserted, 9 deleted
441 </h2>
441 </h2>
442 </div>
442 </div>
443 <div class="cs_files">
443 <div class="cs_files">
444 <table class="compare_view_files">
444 <table class="compare_view_files">
445
445
446 <tr class="cs_A expand_file" fid="c--efbe5b7a3f13">
446 <tr class="cs_A expand_file" fid="c--efbe5b7a3f13">
447 <td class="cs_icon_td">
447 <td class="cs_icon_td">
448 <span class="expand_file_icon" fid="c--efbe5b7a3f13"></span>
448 <span class="expand_file_icon" fid="c--efbe5b7a3f13"></span>
449 </td>
449 </td>
450 <td class="cs_icon_td">
450 <td class="cs_icon_td">
451 <div class="flag_status not_reviewed hidden"></div>
451 <div class="flag_status not_reviewed hidden"></div>
452 </td>
452 </td>
453 <td id="a_c--efbe5b7a3f13">
453 <td id="a_c--efbe5b7a3f13">
454 <a class="compare_view_filepath" href="#a_c--efbe5b7a3f13">
454 <a class="compare_view_filepath" href="#a_c--efbe5b7a3f13">
455 rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff
455 rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff
456 </a>
456 </a>
457 <span id="diff_c--efbe5b7a3f13" class="diff_links" style="display: none;">
457 <span id="diff_c--efbe5b7a3f13" class="diff_links" style="display: none;">
458 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
458 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
459 Unified Diff
459 Unified Diff
460 </a>
460 </a>
461 |
461 |
462 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
462 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
463 Side-by-side Diff
463 Side-by-side Diff
464 </a>
464 </a>
465 </span>
465 </span>
466 </td>
466 </td>
467 <td>
467 <td>
468 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">4</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
468 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">4</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
469 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff">
469 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff">
470 <i class="icon-comment"></i>
470 <i class="icon-comment"></i>
471 </div>
471 </div>
472 </td>
472 </td>
473 </tr>
473 </tr>
474 <tr id="tr_c--efbe5b7a3f13">
474 <tr id="tr_c--efbe5b7a3f13">
475 <td></td>
475 <td></td>
476 <td></td>
476 <td></td>
477 <td class="injected_diff" colspan="2">
477 <td class="injected_diff" colspan="2">
478
478
479 <div class="diff-container" id="diff-container-140716195039928">
479 <div class="diff-container" id="diff-container-140716195039928">
480 <div id="c--efbe5b7a3f13_target" ></div>
480 <div id="c--efbe5b7a3f13_target" ></div>
481 <div id="c--efbe5b7a3f13" class="diffblock margined comm" >
481 <div id="c--efbe5b7a3f13" class="diffblock margined comm" >
482 <div class="code-body">
482 <div class="code-body">
483 <div class="full_f_path" path="rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff" style="display: none;"></div>
483 <div class="full_f_path" path="rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff" style="display: none;"></div>
484 <table class="code-difftable">
484 <table class="code-difftable">
485 <tr class="line context">
485 <tr class="line context">
486 <td class="add-comment-line"><span class="add-comment-content"></span></td>
486 <td class="add-comment-line"><span class="add-comment-content"></span></td>
487 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
487 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
488 <td class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n"></a></td>
488 <td class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n"></a></td>
489 <td class="code no-comment">
489 <td class="code no-comment">
490 <pre>new file 100644</pre>
490 <pre>new file 100644</pre>
491 </td>
491 </td>
492 </tr>
492 </tr>
493 <tr class="line add">
493 <tr class="line add">
494 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
494 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
495 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
495 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
496 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n1">1</a></td>
496 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n1">1</a></td>
497 <td class="code">
497 <td class="code">
498 <pre>diff --git a/file_with_ spaces.txt b/file_with_ two spaces.txt
498 <pre>diff --git a/file_with_ spaces.txt b/file_with_ two spaces.txt
499 </pre>
499 </pre>
500 </td>
500 </td>
501 </tr>
501 </tr>
502 <tr class="line add">
502 <tr class="line add">
503 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
503 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
504 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
504 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
505 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n2">2</a></td>
505 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n2">2</a></td>
506 <td class="code">
506 <td class="code">
507 <pre>similarity index 100%
507 <pre>similarity index 100%
508 </pre>
508 </pre>
509 </td>
509 </td>
510 </tr>
510 </tr>
511 <tr class="line add">
511 <tr class="line add">
512 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
512 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
513 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
513 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
514 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n3">3</a></td>
514 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n3">3</a></td>
515 <td class="code">
515 <td class="code">
516 <pre>rename from file_with_ spaces.txt
516 <pre>rename from file_with_ spaces.txt
517 </pre>
517 </pre>
518 </td>
518 </td>
519 </tr>
519 </tr>
520 <tr class="line add">
520 <tr class="line add">
521 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
521 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
522 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
522 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
523 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n4" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n4">4</a></td>
523 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n4" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n4">4</a></td>
524 <td class="code">
524 <td class="code">
525 <pre>rename to file_with_ two spaces.txt
525 <pre>rename to file_with_ two spaces.txt
526 </pre>
526 </pre>
527 </td>
527 </td>
528 </tr>
528 </tr>
529 <tr class="line context">
529 <tr class="line context">
530 <td class="add-comment-line"><span class="add-comment-content"></span></td>
530 <td class="add-comment-line"><span class="add-comment-content"></span></td>
531 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o...">...</a></td>
531 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o...">...</a></td>
532 <td class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n...">...</a></td>
532 <td class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n...">...</a></td>
533 <td class="code no-comment">
533 <td class="code no-comment">
534 <pre> No newline at end of file</pre>
534 <pre> No newline at end of file</pre>
535 </td>
535 </td>
536 </tr>
536 </tr>
537 </table>
537 </table>
538 </div>
538 </div>
539 </div>
539 </div>
540 </div>
540 </div>
541
541
542 </td>
542 </td>
543 </tr>
543 </tr>
544 <tr class="cs_A expand_file" fid="c--c21377f778f9">
544 <tr class="cs_A expand_file" fid="c--c21377f778f9">
545 <td class="cs_icon_td">
545 <td class="cs_icon_td">
546 <span class="expand_file_icon" fid="c--c21377f778f9"></span>
546 <span class="expand_file_icon" fid="c--c21377f778f9"></span>
547 </td>
547 </td>
548 <td class="cs_icon_td">
548 <td class="cs_icon_td">
549 <div class="flag_status not_reviewed hidden"></div>
549 <div class="flag_status not_reviewed hidden"></div>
550 </td>
550 </td>
551 <td id="a_c--c21377f778f9">
551 <td id="a_c--c21377f778f9">
552 <a class="compare_view_filepath" href="#a_c--c21377f778f9">
552 <a class="compare_view_filepath" href="#a_c--c21377f778f9">
553 rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff
553 rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff
554 </a>
554 </a>
555 <span id="diff_c--c21377f778f9" class="diff_links" style="display: none;">
555 <span id="diff_c--c21377f778f9" class="diff_links" style="display: none;">
556 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
556 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
557 Unified Diff
557 Unified Diff
558 </a>
558 </a>
559 |
559 |
560 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
560 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
561 Side-by-side Diff
561 Side-by-side Diff
562 </a>
562 </a>
563 </span>
563 </span>
564 </td>
564 </td>
565 <td>
565 <td>
566 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">3</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
566 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">3</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
567 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff">
567 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff">
568 <i class="icon-comment"></i>
568 <i class="icon-comment"></i>
569 </div>
569 </div>
570 </td>
570 </td>
571 </tr>
571 </tr>
572 <tr id="tr_c--c21377f778f9">
572 <tr id="tr_c--c21377f778f9">
573 <td></td>
573 <td></td>
574 <td></td>
574 <td></td>
575 <td class="injected_diff" colspan="2">
575 <td class="injected_diff" colspan="2">
576
576
577 <div class="diff-container" id="diff-container-140716195038344">
577 <div class="diff-container" id="diff-container-140716195038344">
578 <div id="c--c21377f778f9_target" ></div>
578 <div id="c--c21377f778f9_target" ></div>
579 <div id="c--c21377f778f9" class="diffblock margined comm" >
579 <div id="c--c21377f778f9" class="diffblock margined comm" >
580 <div class="code-body">
580 <div class="code-body">
581 <div class="full_f_path" path="rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff" style="display: none;"></div>
581 <div class="full_f_path" path="rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff" style="display: none;"></div>
582 <table class="code-difftable">
582 <table class="code-difftable">
583 <tr class="line context">
583 <tr class="line context">
584 <td class="add-comment-line"><span class="add-comment-content"></span></td>
584 <td class="add-comment-line"><span class="add-comment-content"></span></td>
585 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
585 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
586 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n"></a></td>
586 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n"></a></td>
587 <td class="code no-comment">
587 <td class="code no-comment">
588 <pre>new file 100644</pre>
588 <pre>new file 100644</pre>
589 </td>
589 </td>
590 </tr>
590 </tr>
591 <tr class="line add">
591 <tr class="line add">
592 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
592 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
593 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
593 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
594 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n1">1</a></td>
594 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n1">1</a></td>
595 <td class="code">
595 <td class="code">
596 <pre>diff --git a/file_changed_without_spaces.txt b/file_copied_ with spaces.txt
596 <pre>diff --git a/file_changed_without_spaces.txt b/file_copied_ with spaces.txt
597 </pre>
597 </pre>
598 </td>
598 </td>
599 </tr>
599 </tr>
600 <tr class="line add">
600 <tr class="line add">
601 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
601 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
602 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
602 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
603 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n2">2</a></td>
603 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n2">2</a></td>
604 <td class="code">
604 <td class="code">
605 <pre>copy from file_changed_without_spaces.txt
605 <pre>copy from file_changed_without_spaces.txt
606 </pre>
606 </pre>
607 </td>
607 </td>
608 </tr>
608 </tr>
609 <tr class="line add">
609 <tr class="line add">
610 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
610 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
611 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
611 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
612 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n3">3</a></td>
612 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n3">3</a></td>
613 <td class="code">
613 <td class="code">
614 <pre>copy to file_copied_ with spaces.txt
614 <pre>copy to file_copied_ with spaces.txt
615 </pre>
615 </pre>
616 </td>
616 </td>
617 </tr>
617 </tr>
618 <tr class="line context">
618 <tr class="line context">
619 <td class="add-comment-line"><span class="add-comment-content"></span></td>
619 <td class="add-comment-line"><span class="add-comment-content"></span></td>
620 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o...">...</a></td>
620 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o...">...</a></td>
621 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n...">...</a></td>
621 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n...">...</a></td>
622 <td class="code no-comment">
622 <td class="code no-comment">
623 <pre> No newline at end of file</pre>
623 <pre> No newline at end of file</pre>
624 </td>
624 </td>
625 </tr>
625 </tr>
626 </table>
626 </table>
627 </div>
627 </div>
628 </div>
628 </div>
629 </div>
629 </div>
630
630
631 </td>
631 </td>
632 </tr>
632 </tr>
633 <tr class="cs_A expand_file" fid="c--ee62085ad7a8">
633 <tr class="cs_A expand_file" fid="c--ee62085ad7a8">
634 <td class="cs_icon_td">
634 <td class="cs_icon_td">
635 <span class="expand_file_icon" fid="c--ee62085ad7a8"></span>
635 <span class="expand_file_icon" fid="c--ee62085ad7a8"></span>
636 </td>
636 </td>
637 <td class="cs_icon_td">
637 <td class="cs_icon_td">
638 <div class="flag_status not_reviewed hidden"></div>
638 <div class="flag_status not_reviewed hidden"></div>
639 </td>
639 </td>
640 <td id="a_c--ee62085ad7a8">
640 <td id="a_c--ee62085ad7a8">
641 <a class="compare_view_filepath" href="#a_c--ee62085ad7a8">
641 <a class="compare_view_filepath" href="#a_c--ee62085ad7a8">
642 rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff
642 rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff
643 </a>
643 </a>
644 <span id="diff_c--ee62085ad7a8" class="diff_links" style="display: none;">
644 <span id="diff_c--ee62085ad7a8" class="diff_links" style="display: none;">
645 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
645 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
646 Unified Diff
646 Unified Diff
647 </a>
647 </a>
648 |
648 |
649 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
649 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
650 Side-by-side Diff
650 Side-by-side Diff
651 </a>
651 </a>
652 </span>
652 </span>
653 </td>
653 </td>
654 <td>
654 <td>
655 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">3</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
655 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">3</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
656 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff">
656 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff">
657 <i class="icon-comment"></i>
657 <i class="icon-comment"></i>
658 </div>
658 </div>
659 </td>
659 </td>
660 </tr>
660 </tr>
661 <tr id="tr_c--ee62085ad7a8">
661 <tr id="tr_c--ee62085ad7a8">
662 <td></td>
662 <td></td>
663 <td></td>
663 <td></td>
664 <td class="injected_diff" colspan="2">
664 <td class="injected_diff" colspan="2">
665
665
666 <div class="diff-container" id="diff-container-140716195039496">
666 <div class="diff-container" id="diff-container-140716195039496">
667 <div id="c--ee62085ad7a8_target" ></div>
667 <div id="c--ee62085ad7a8_target" ></div>
668 <div id="c--ee62085ad7a8" class="diffblock margined comm" >
668 <div id="c--ee62085ad7a8" class="diffblock margined comm" >
669 <div class="code-body">
669 <div class="code-body">
670 <div class="full_f_path" path="rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff" style="display: none;"></div>
670 <div class="full_f_path" path="rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff" style="display: none;"></div>
671 <table class="code-difftable">
671 <table class="code-difftable">
672 <tr class="line context">
672 <tr class="line context">
673 <td class="add-comment-line"><span class="add-comment-content"></span></td>
673 <td class="add-comment-line"><span class="add-comment-content"></span></td>
674 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
674 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
675 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n"></a></td>
675 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n"></a></td>
676 <td class="code no-comment">
676 <td class="code no-comment">
677 <pre>new file 100644</pre>
677 <pre>new file 100644</pre>
678 </td>
678 </td>
679 </tr>
679 </tr>
680 <tr class="line add">
680 <tr class="line add">
681 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
681 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
682 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
682 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
683 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n1">1</a></td>
683 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n1">1</a></td>
684 <td class="code">
684 <td class="code">
685 <pre>diff --git a/file_ with update.txt b/file_changed _.txt
685 <pre>diff --git a/file_ with update.txt b/file_changed _.txt
686 </pre>
686 </pre>
687 </td>
687 </td>
688 </tr>
688 </tr>
689 <tr class="line add">
689 <tr class="line add">
690 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
690 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
691 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
691 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
692 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n2">2</a></td>
692 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n2">2</a></td>
693 <td class="code">
693 <td class="code">
694 <pre>rename from file_ with update.txt
694 <pre>rename from file_ with update.txt
695 </pre>
695 </pre>
696 </td>
696 </td>
697 </tr>
697 </tr>
698 <tr class="line add">
698 <tr class="line add">
699 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
699 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
700 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
700 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
701 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n3">3</a></td>
701 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n3">3</a></td>
702 <td class="code">
702 <td class="code">
703 <pre>rename to file_changed _.txt</pre>
703 <pre>rename to file_changed _.txt</pre>
704 </td>
704 </td>
705 </tr>
705 </tr>
706 </table>
706 </table>
707 </div>
707 </div>
708 </div>
708 </div>
709 </div>
709 </div>
710
710
711 </td>
711 </td>
712 </tr>
712 </tr>
713
713
714 </table>
714 </table>
715 </div>
715 </div>
716 </div>
716 </div>
717 </div>
717 </div>
718
718
719 </td>
719 </td>
720 </tr>
720 </tr>
721 </table>
721 </table>
722 </div>
722 </div>
723 </div>
723 </div>
724 </div>
724 </div>
725
725
726
726
727
727
728
728
729 <div id="comment-inline-form-template" style="display: none;">
729 <div id="comment-inline-form-template" style="display: none;">
730 <div class="comment-inline-form ac">
730 <div class="comment-inline-form ac">
731 <div class="overlay"><div class="overlay-text">Submitting...</div></div>
731 <div class="overlay"><div class="overlay-text">Submitting...</div></div>
732 <form action="#" class="inline-form" method="get">
732 <form action="#" class="inline-form" method="get">
733 <div id="edit-container_{1}" class="clearfix">
733 <div id="edit-container_{1}" class="clearfix">
734 <div class="comment-title pull-left">
734 <div class="comment-title pull-left">
735 Commenting on line {1}.
735 Commenting on line {1}.
736 </div>
736 </div>
737 <div class="comment-help pull-right">
737 <div class="comment-help pull-right">
738 Comments parsed using <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">RST</a> syntax with <span class="tooltip" title="Use @username inside this text to send notification to this RhodeCode user">@mention</span> support.
738 Comments parsed using <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">RST</a> syntax with <span class="tooltip" title="Use @username inside this text to send notification to this RhodeCode user">@mention</span> support.
739 </div>
739 </div>
740 <div style="clear: both"></div>
740 <div style="clear: both"></div>
741 <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea>
741 <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea>
742 </div>
742 </div>
743 <div id="preview-container_{1}" class="clearfix" style="display: none;">
743 <div id="preview-container_{1}" class="clearfix" style="display: none;">
744 <div class="comment-help">
744 <div class="comment-help">
745 Comment preview
745 Comment preview
746 </div>
746 </div>
747 <div id="preview-box_{1}" class="preview-box"></div>
747 <div id="preview-box_{1}" class="preview-box"></div>
748 </div>
748 </div>
749 <div class="comment-button pull-right">
749 <div class="comment-button pull-right">
750 <input type="hidden" name="f_path" value="{0}">
750 <input type="hidden" name="f_path" value="{0}">
751 <input type="hidden" name="line" value="{1}">
751 <input type="hidden" name="line" value="{1}">
752 <div id="preview-btn_{1}" class="btn btn-default">Preview</div>
752 <div id="preview-btn_{1}" class="btn btn-default">Preview</div>
753 <div id="edit-btn_{1}" class="btn" style="display: none;">Edit</div>
753 <div id="edit-btn_{1}" class="btn" style="display: none;">Edit</div>
754 <input class="btn btn-success save-inline-form" id="save" name="save" type="submit" value="Comment" />
754 <input class="btn btn-success save-inline-form" id="save" name="save" type="submit" value="Comment" />
755 </div>
755 </div>
756 <div class="comment-button hide-inline-form-button">
756 <div class="comment-button hide-inline-form-button">
757 <input class="btn hide-inline-form" id="hide-inline-form" name="hide-inline-form" type="reset" value="Cancel" />
757 <input class="btn hide-inline-form" id="hide-inline-form" name="hide-inline-form" type="reset" value="Cancel" />
758 </div>
758 </div>
759 </form>
759 </form>
760 </div>
760 </div>
761 </div>
761 </div>
762
762
763
763
764
764
765 <div class="comments">
765 <div class="comments">
766 <div id="inline-comments-container">
766 <div id="inline-comments-container">
767
767
768 <h2>0 Pull Request Comments</h2>
768 <h2>0 Pull Request Comments</h2>
769
769
770
770
771 </div>
771 </div>
772
772
773 </div>
773 </div>
774
774
775
775
776
776
777
777
778 <div class="pull-request-merge">
778 <div class="pull-request-merge">
779 </div>
779 </div>
780 <div class="comments">
780 <div class="comments">
781 <div class="comment-form ac">
781 <div class="comment-form ac">
782 <form action="/rhodecode-momentum/pull-request-comment/720" id="comments_form" method="POST">
782 <form action="/rhodecode-momentum/pull-request-comment/720" id="comments_form" method="POST">
783 <div style="display: none;"><input id="csrf_token" name="csrf_token" type="hidden" value="6dbc0b19ac65237df65d57202a3e1f2df4153e38" /></div>
783 <div style="display: none;"><input id="csrf_token" name="csrf_token" type="hidden" value="6dbc0b19ac65237df65d57202a3e1f2df4153e38" /></div>
784 <div id="edit-container" class="clearfix">
784 <div id="edit-container" class="clearfix">
785 <div class="comment-title pull-left">
785 <div class="comment-title pull-left">
786 Create a comment on this Pull Request.
786 Create a comment on this Pull Request.
787 </div>
787 </div>
788 <div class="comment-help pull-right">
788 <div class="comment-help pull-right">
789 Comments parsed using <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">RST</a> syntax with <span class="tooltip" title="Use @username inside this text to send notification to this RhodeCode user">@mention</span> support.
789 Comments parsed using <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">RST</a> syntax with <span class="tooltip" title="Use @username inside this text to send notification to this RhodeCode user">@mention</span> support.
790 </div>
790 </div>
791 <div style="clear: both"></div>
791 <div style="clear: both"></div>
792 <textarea class="comment-block-ta" id="text" name="text"></textarea>
792 <textarea class="comment-block-ta" id="text" name="text"></textarea>
793 </div>
793 </div>
794
794
795 <div id="preview-container" class="clearfix" style="display: none;">
795 <div id="preview-container" class="clearfix" style="display: none;">
796 <div class="comment-title">
796 <div class="comment-title">
797 Comment preview
797 Comment preview
798 </div>
798 </div>
799 <div id="preview-box" class="preview-box"></div>
799 <div id="preview-box" class="preview-box"></div>
800 </div>
800 </div>
801
801
802 <div id="comment_form_extras">
802 <div id="comment_form_extras">
803 </div>
803 </div>
804 <div class="action-button pull-right">
804 <div class="action-button pull-right">
805 <div id="preview-btn" class="btn">
805 <div id="preview-btn" class="btn">
806 Preview
806 Preview
807 </div>
807 </div>
808 <div id="edit-btn" class="btn" style="display: none;">
808 <div id="edit-btn" class="btn" style="display: none;">
809 Edit
809 Edit
810 </div>
810 </div>
811 <div class="comment-button">
811 <div class="comment-button">
812 <input class="btn btn-small btn-success comment-button-input" id="save" name="save" type="submit" value="Comment" />
812 <input class="btn btn-small btn-success comment-button-input" id="save" name="save" type="submit" value="Comment" />
813 </div>
813 </div>
814 </div>
814 </div>
815 </form>
815 </form>
816 </div>
816 </div>
817 </div>
817 </div>
818 <script>
818 <script>
819
819
820 $(document).ready(function() {
820 $(document).ready(function() {
821
821
822 var cm = initCommentBoxCodeMirror('#text');
822 var cm = initCommentBoxCodeMirror('#text');
823
823
824 // main form preview
824 // main form preview
825 $('#preview-btn').on('click', function(e) {
825 $('#preview-btn').on('click', function(e) {
826 $('#preview-btn').hide();
826 $('#preview-btn').hide();
827 $('#edit-btn').show();
827 $('#edit-btn').show();
828 var _text = cm.getValue();
828 var _text = cm.getValue();
829 if (!_text) {
829 if (!_text) {
830 return;
830 return;
831 }
831 }
832 var post_data = {
832 var post_data = {
833 'text': _text,
833 'text': _text,
834 'renderer': DEFAULT_RENDERER,
834 'renderer': DEFAULT_RENDERER,
835 'csrf_token': CSRF_TOKEN
835 'csrf_token': CSRF_TOKEN
836 };
836 };
837 var previewbox = $('#preview-box');
837 var previewbox = $('#preview-box');
838 previewbox.addClass('unloaded');
838 previewbox.addClass('unloaded');
839 previewbox.html(_TM['Loading ...']);
839 previewbox.html(_gettext('Loading ...'));
840 $('#edit-container').hide();
840 $('#edit-container').hide();
841 $('#preview-container').show();
841 $('#preview-container').show();
842
842
843 var url = pyroutes.url('changeset_comment_preview', {'repo_name': 'rhodecode-momentum'});
843 var url = pyroutes.url('changeset_comment_preview', {'repo_name': 'rhodecode-momentum'});
844
844
845 ajaxPOST(url, post_data, function(o) {
845 ajaxPOST(url, post_data, function(o) {
846 previewbox.html(o);
846 previewbox.html(o);
847 previewbox.removeClass('unloaded');
847 previewbox.removeClass('unloaded');
848 });
848 });
849 });
849 });
850 $('#edit-btn').on('click', function(e) {
850 $('#edit-btn').on('click', function(e) {
851 $('#preview-btn').show();
851 $('#preview-btn').show();
852 $('#edit-btn').hide();
852 $('#edit-btn').hide();
853 $('#edit-container').show();
853 $('#edit-container').show();
854 $('#preview-container').hide();
854 $('#preview-container').hide();
855 });
855 });
856
856
857 var formatChangeStatus = function(state, escapeMarkup) {
857 var formatChangeStatus = function(state, escapeMarkup) {
858 var originalOption = state.element;
858 var originalOption = state.element;
859 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
859 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
860 '<span>' + escapeMarkup(state.text) + '</span>';
860 '<span>' + escapeMarkup(state.text) + '</span>';
861 };
861 };
862
862
863 var formatResult = function(result, container, query, escapeMarkup) {
863 var formatResult = function(result, container, query, escapeMarkup) {
864 return formatChangeStatus(result, escapeMarkup);
864 return formatChangeStatus(result, escapeMarkup);
865 };
865 };
866
866
867 var formatSelection = function(data, container, escapeMarkup) {
867 var formatSelection = function(data, container, escapeMarkup) {
868 return formatChangeStatus(data, escapeMarkup);
868 return formatChangeStatus(data, escapeMarkup);
869 };
869 };
870
870
871 $('#change_status').select2({
871 $('#change_status').select2({
872 placeholder: "Status Review",
872 placeholder: "Status Review",
873 formatResult: formatResult,
873 formatResult: formatResult,
874 formatSelection: formatSelection,
874 formatSelection: formatSelection,
875 containerCssClass: "drop-menu status_box_menu",
875 containerCssClass: "drop-menu status_box_menu",
876 dropdownCssClass: "drop-menu-dropdown",
876 dropdownCssClass: "drop-menu-dropdown",
877 dropdownAutoWidth: true,
877 dropdownAutoWidth: true,
878 minimumResultsForSearch: -1
878 minimumResultsForSearch: -1
879 });
879 });
880 });
880 });
881 </script>
881 </script>
882
882
883
883
884 <script type="text/javascript">
884 <script type="text/javascript">
885 // TODO: switch this to pyroutes
885 // TODO: switch this to pyroutes
886 AJAX_COMMENT_DELETE_URL = "/rhodecode-momentum/pull-request-comment/__COMMENT_ID__/delete";
886 AJAX_COMMENT_DELETE_URL = "/rhodecode-momentum/pull-request-comment/__COMMENT_ID__/delete";
887
887
888 $(function(){
888 $(function(){
889 ReviewerAutoComplete('user');
889 ReviewerAutoComplete('user');
890
890
891 $('#open_edit_reviewers').on('click', function(e){
891 $('#open_edit_reviewers').on('click', function(e){
892 $('#open_edit_reviewers').hide();
892 $('#open_edit_reviewers').hide();
893 $('#close_edit_reviewers').show();
893 $('#close_edit_reviewers').show();
894 $('#add_reviewer_input').show();
894 $('#add_reviewer_input').show();
895 $('.reviewer_member_remove').css('visibility', 'visible');
895 $('.reviewer_member_remove').css('visibility', 'visible');
896 });
896 });
897
897
898 $('#close_edit_reviewers').on('click', function(e){
898 $('#close_edit_reviewers').on('click', function(e){
899 $('#open_edit_reviewers').show();
899 $('#open_edit_reviewers').show();
900 $('#close_edit_reviewers').hide();
900 $('#close_edit_reviewers').hide();
901 $('#add_reviewer_input').hide();
901 $('#add_reviewer_input').hide();
902 $('.reviewer_member_remove').css('visibility', 'hidden');
902 $('.reviewer_member_remove').css('visibility', 'hidden');
903 });
903 });
904
904
905 $('.show-inline-comments').on('change', function(e){
905 $('.show-inline-comments').on('change', function(e){
906 var show = 'none';
906 var show = 'none';
907 var target = e.currentTarget;
907 var target = e.currentTarget;
908 if(target.checked){
908 if(target.checked){
909 show = ''
909 show = ''
910 }
910 }
911 var boxid = $(target).attr('id_for');
911 var boxid = $(target).attr('id_for');
912 var comments = $('#{0} .inline-comments'.format(boxid));
912 var comments = $('#{0} .inline-comments'.format(boxid));
913 var fn_display = function(idx){
913 var fn_display = function(idx){
914 $(this).css('display', show);
914 $(this).css('display', show);
915 };
915 };
916 $(comments).each(fn_display);
916 $(comments).each(fn_display);
917 var btns = $('#{0} .inline-comments-button'.format(boxid));
917 var btns = $('#{0} .inline-comments-button'.format(boxid));
918 $(btns).each(fn_display);
918 $(btns).each(fn_display);
919 });
919 });
920
920
921 // inject comments into they proper positions
921 // inject comments into they proper positions
922 var file_comments = $('.inline-comment-placeholder');
922 var file_comments = $('.inline-comment-placeholder');
923 renderInlineComments(file_comments);
923 renderInlineComments(file_comments);
924 var commentTotals = {};
924 var commentTotals = {};
925 $.each(file_comments, function(i, comment) {
925 $.each(file_comments, function(i, comment) {
926 var path = $(comment).attr('path');
926 var path = $(comment).attr('path');
927 var comms = $(comment).children().length;
927 var comms = $(comment).children().length;
928 if (path in commentTotals) {
928 if (path in commentTotals) {
929 commentTotals[path] += comms;
929 commentTotals[path] += comms;
930 } else {
930 } else {
931 commentTotals[path] = comms;
931 commentTotals[path] = comms;
932 }
932 }
933 });
933 });
934 $.each(commentTotals, function(path, total) {
934 $.each(commentTotals, function(path, total) {
935 var elem = $('.comment-bubble[data-path="'+ path +'"]')
935 var elem = $('.comment-bubble[data-path="'+ path +'"]')
936 elem.css('visibility', 'visible');
936 elem.css('visibility', 'visible');
937 elem.html(elem.html() + ' ' + total );
937 elem.html(elem.html() + ' ' + total );
938 });
938 });
939
939
940 $('#merge_pull_request_form').submit(function() {
940 $('#merge_pull_request_form').submit(function() {
941 if (!$('#merge_pull_request').attr('disabled')) {
941 if (!$('#merge_pull_request').attr('disabled')) {
942 $('#merge_pull_request').attr('disabled', 'disabled');
942 $('#merge_pull_request').attr('disabled', 'disabled');
943 }
943 }
944 return true;
944 return true;
945 });
945 });
946
946
947 $('#update_pull_request').on('click', function(e){
947 $('#update_pull_request').on('click', function(e){
948 updateReviewers(undefined, "rhodecode-momentum", "720");
948 updateReviewers(undefined, "rhodecode-momentum", "720");
949 });
949 });
950
950
951 $('#update_commits').on('click', function(e){
951 $('#update_commits').on('click', function(e){
952 updateCommits("rhodecode-momentum", "720");
952 updateCommits("rhodecode-momentum", "720");
953 });
953 });
954
954
955 $('#close_pull_request').on('click', function(e){
955 $('#close_pull_request').on('click', function(e){
956 closePullRequest("rhodecode-momentum", "720");
956 closePullRequest("rhodecode-momentum", "720");
957 });
957 });
958 })
958 })
959 </script>
959 </script>
960
960
961 </div>
961 </div>
962 </div></div>
962 </div></div>
963
963
964 </div>
964 </div>
965
965
966
966
967 </%def>
967 </%def>
@@ -1,336 +1,336 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title(*args)">
3 <%def name="title(*args)">
4 ${_('%s Files') % c.repo_name}
4 ${_('%s Files') % c.repo_name}
5 %if hasattr(c,'file'):
5 %if hasattr(c,'file'):
6 &middot; ${h.safe_unicode(c.file.path) or '\\'}
6 &middot; ${h.safe_unicode(c.file.path) or '\\'}
7 %endif
7 %endif
8
8
9 %if c.rhodecode_name:
9 %if c.rhodecode_name:
10 &middot; ${h.branding(c.rhodecode_name)}
10 &middot; ${h.branding(c.rhodecode_name)}
11 %endif
11 %endif
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Files')}
15 ${_('Files')}
16 %if c.file:
16 %if c.file:
17 @ ${h.show_id(c.commit)}
17 @ ${h.show_id(c.commit)}
18 %endif
18 %endif
19 </%def>
19 </%def>
20
20
21 <%def name="menu_bar_nav()">
21 <%def name="menu_bar_nav()">
22 ${self.menu_items(active='repositories')}
22 ${self.menu_items(active='repositories')}
23 </%def>
23 </%def>
24
24
25 <%def name="menu_bar_subnav()">
25 <%def name="menu_bar_subnav()">
26 ${self.repo_menu(active='files')}
26 ${self.repo_menu(active='files')}
27 </%def>
27 </%def>
28
28
29 <%def name="main()">
29 <%def name="main()">
30 <div class="title">
30 <div class="title">
31 ${self.repo_page_title(c.rhodecode_db_repo)}
31 ${self.repo_page_title(c.rhodecode_db_repo)}
32 </div>
32 </div>
33
33
34 <div id="pjax-container" class="summary">
34 <div id="pjax-container" class="summary">
35 <div id="files_data">
35 <div id="files_data">
36 <%include file='files_pjax.html'/>
36 <%include file='files_pjax.html'/>
37 </div>
37 </div>
38 </div>
38 </div>
39 <script>
39 <script>
40 var curState = {
40 var curState = {
41 commit_id: "${c.commit.raw_id}"
41 commit_id: "${c.commit.raw_id}"
42 };
42 };
43
43
44 var getState = function(context) {
44 var getState = function(context) {
45 var url = $(location).attr('href');
45 var url = $(location).attr('href');
46 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
46 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
47 var _annotate_url = '${h.url("files_annotate_home",repo_name=c.repo_name,revision='',f_path='')}';
47 var _annotate_url = '${h.url("files_annotate_home",repo_name=c.repo_name,revision='',f_path='')}';
48 _base_url = _base_url.replace('//', '/');
48 _base_url = _base_url.replace('//', '/');
49 _annotate_url = _annotate_url.replace('//', '/');
49 _annotate_url = _annotate_url.replace('//', '/');
50
50
51 //extract f_path from url.
51 //extract f_path from url.
52 var parts = url.split(_base_url);
52 var parts = url.split(_base_url);
53 if (parts.length != 2) {
53 if (parts.length != 2) {
54 parts = url.split(_annotate_url);
54 parts = url.split(_annotate_url);
55 if (parts.length != 2) {
55 if (parts.length != 2) {
56 var rev = "tip";
56 var rev = "tip";
57 var f_path = "";
57 var f_path = "";
58 } else {
58 } else {
59 var parts2 = parts[1].split('/');
59 var parts2 = parts[1].split('/');
60 var rev = parts2.shift(); // pop the first element which is the revision
60 var rev = parts2.shift(); // pop the first element which is the revision
61 var f_path = parts2.join('/');
61 var f_path = parts2.join('/');
62 }
62 }
63
63
64 } else {
64 } else {
65 var parts2 = parts[1].split('/');
65 var parts2 = parts[1].split('/');
66 var rev = parts2.shift(); // pop the first element which is the revision
66 var rev = parts2.shift(); // pop the first element which is the revision
67 var f_path = parts2.join('/');
67 var f_path = parts2.join('/');
68 }
68 }
69
69
70 var _node_list_url = pyroutes.url('files_nodelist_home',
70 var _node_list_url = pyroutes.url('files_nodelist_home',
71 {repo_name: templateContext.repo_name,
71 {repo_name: templateContext.repo_name,
72 revision: rev, f_path: f_path});
72 revision: rev, f_path: f_path});
73 var _url_base = pyroutes.url('files_home',
73 var _url_base = pyroutes.url('files_home',
74 {repo_name: templateContext.repo_name,
74 {repo_name: templateContext.repo_name,
75 revision: rev, f_path:'__FPATH__'});
75 revision: rev, f_path:'__FPATH__'});
76 return {
76 return {
77 url: url,
77 url: url,
78 f_path: f_path,
78 f_path: f_path,
79 rev: rev,
79 rev: rev,
80 commit_id: curState.commit_id,
80 commit_id: curState.commit_id,
81 node_list_url: _node_list_url,
81 node_list_url: _node_list_url,
82 url_base: _url_base
82 url_base: _url_base
83 };
83 };
84 };
84 };
85
85
86 var metadataRequest = null;
86 var metadataRequest = null;
87 var getFilesMetadata = function() {
87 var getFilesMetadata = function() {
88 if (metadataRequest && metadataRequest.readyState != 4) {
88 if (metadataRequest && metadataRequest.readyState != 4) {
89 metadataRequest.abort();
89 metadataRequest.abort();
90 }
90 }
91 if (source_page) {
91 if (source_page) {
92 return false;
92 return false;
93 }
93 }
94 var state = getState('metadata');
94 var state = getState('metadata');
95 var url_data = {
95 var url_data = {
96 'repo_name': templateContext.repo_name,
96 'repo_name': templateContext.repo_name,
97 'revision': state.commit_id,
97 'revision': state.commit_id,
98 'f_path': state.f_path
98 'f_path': state.f_path
99 };
99 };
100
100
101 var url = pyroutes.url('files_metadata_list_home', url_data);
101 var url = pyroutes.url('files_metadata_list_home', url_data);
102
102
103 metadataRequest = $.ajax({url: url});
103 metadataRequest = $.ajax({url: url});
104
104
105 metadataRequest.done(function(data) {
105 metadataRequest.done(function(data) {
106 var data = data.metadata;
106 var data = data.metadata;
107 var dataLength = data.length;
107 var dataLength = data.length;
108 for (var i = 0; i < dataLength; i++) {
108 for (var i = 0; i < dataLength; i++) {
109 var rowData = data[i];
109 var rowData = data[i];
110 var name = rowData.name.replace('\\', '\\\\');
110 var name = rowData.name.replace('\\', '\\\\');
111
111
112 $('td[title="size-' + name + '"]').html(rowData.size);
112 $('td[title="size-' + name + '"]').html(rowData.size);
113 var timeComponent = AgeModule.createTimeComponent(
113 var timeComponent = AgeModule.createTimeComponent(
114 rowData.modified_ts, rowData.modified_at);
114 rowData.modified_ts, rowData.modified_at);
115 $('td[title="modified_at-' + name + '"]').html(timeComponent);
115 $('td[title="modified_at-' + name + '"]').html(timeComponent);
116
116
117 $('td[title="revision-' + name + '"]').html(
117 $('td[title="revision-' + name + '"]').html(
118 '<div class="tooltip" title="{0}"><pre>r{1}:{2}</pre></div>'.format(
118 '<div class="tooltip" title="{0}"><pre>r{1}:{2}</pre></div>'.format(
119 data[i].message, data[i].revision, data[i].short_id));
119 data[i].message, data[i].revision, data[i].short_id));
120 $('td[title="author-' + name + '"]').html(
120 $('td[title="author-' + name + '"]').html(
121 '<span title="{0}">{1}</span>'.format(
121 '<span title="{0}">{1}</span>'.format(
122 data[i].author, data[i].user_profile));
122 data[i].author, data[i].user_profile));
123 }
123 }
124 tooltip_activate();
124 tooltip_activate();
125 timeagoActivate();
125 timeagoActivate();
126 });
126 });
127 metadataRequest.fail(function (data, textStatus, errorThrown) {
127 metadataRequest.fail(function (data, textStatus, errorThrown) {
128 console.log(data);
128 console.log(data);
129 if (data.status != 0) {
129 if (data.status != 0) {
130 alert("Error while fetching metadata.\nError code {0} ({1}).Please consider reloading the page".format(data.status,data.statusText));
130 alert("Error while fetching metadata.\nError code {0} ({1}).Please consider reloading the page".format(data.status,data.statusText));
131 }
131 }
132 });
132 });
133 };
133 };
134
134
135 var callbacks = function() {
135 var callbacks = function() {
136 var state = getState('callbacks');
136 var state = getState('callbacks');
137 tooltip_activate();
137 tooltip_activate();
138 timeagoActivate();
138 timeagoActivate();
139
139
140 // used for history, and switch to
140 // used for history, and switch to
141 var initialCommitData = {
141 var initialCommitData = {
142 id: null,
142 id: null,
143 text: "${_("Switch To Commit")}",
143 text: "${_("Switch To Commit")}",
144 type: 'sha',
144 type: 'sha',
145 raw_id: null,
145 raw_id: null,
146 files_url: null
146 files_url: null
147 };
147 };
148
148
149 if ($('#trimmed_message_box').height() < 50) {
149 if ($('#trimmed_message_box').height() < 50) {
150 $('#message_expand').hide();
150 $('#message_expand').hide();
151 }
151 }
152
152
153 $('#message_expand').on('click', function(e) {
153 $('#message_expand').on('click', function(e) {
154 $('#trimmed_message_box').css('max-height', 'none');
154 $('#trimmed_message_box').css('max-height', 'none');
155 $(this).hide();
155 $(this).hide();
156 });
156 });
157
157
158
158
159 if (source_page) {
159 if (source_page) {
160 // variants for with source code, not tree view
160 // variants for with source code, not tree view
161
161
162 if (location.href.indexOf('#') != -1) {
162 if (location.href.indexOf('#') != -1) {
163 page_highlights = location.href.substring(location.href.indexOf('#') + 1).split('L');
163 page_highlights = location.href.substring(location.href.indexOf('#') + 1).split('L');
164 if (page_highlights.length == 2) {
164 if (page_highlights.length == 2) {
165 highlight_ranges = page_highlights[1].split(",");
165 highlight_ranges = page_highlights[1].split(",");
166
166
167 var h_lines = [];
167 var h_lines = [];
168 for (pos in highlight_ranges) {
168 for (pos in highlight_ranges) {
169 var _range = highlight_ranges[pos].split('-');
169 var _range = highlight_ranges[pos].split('-');
170 if (_range.length == 2) {
170 if (_range.length == 2) {
171 var start = parseInt(_range[0]);
171 var start = parseInt(_range[0]);
172 var end = parseInt(_range[1]);
172 var end = parseInt(_range[1]);
173 if (start < end) {
173 if (start < end) {
174 for (var i = start; i <= end; i++) {
174 for (var i = start; i <= end; i++) {
175 h_lines.push(i);
175 h_lines.push(i);
176 }
176 }
177 }
177 }
178 }
178 }
179 else {
179 else {
180 h_lines.push(parseInt(highlight_ranges[pos]));
180 h_lines.push(parseInt(highlight_ranges[pos]));
181 }
181 }
182 }
182 }
183
183
184 for (pos in h_lines) {
184 for (pos in h_lines) {
185 // @comment-highlight-color
185 // @comment-highlight-color
186 $('#L' + h_lines[pos]).css('background-color', '#ffd887');
186 $('#L' + h_lines[pos]).css('background-color', '#ffd887');
187 }
187 }
188
188
189 var _first_line = $('#L' + h_lines[0]).get(0);
189 var _first_line = $('#L' + h_lines[0]).get(0);
190 if (_first_line) {
190 if (_first_line) {
191 var line = $('#L' + h_lines[0]);
191 var line = $('#L' + h_lines[0]);
192 offsetScroll(line, 70);
192 offsetScroll(line, 70);
193 }
193 }
194 }
194 }
195 }
195 }
196
196
197 // select code link event
197 // select code link event
198 $("#hlcode").mouseup(getSelectionLink);
198 $("#hlcode").mouseup(getSelectionLink);
199
199
200 // file history select2
200 // file history select2
201 select2FileHistorySwitcher('#diff1', initialCommitData, state);
201 select2FileHistorySwitcher('#diff1', initialCommitData, state);
202 $('#diff1').on('change', function(e) {
202 $('#diff1').on('change', function(e) {
203 $('#diff').removeClass('disabled').removeAttr("disabled");
203 $('#diff').removeClass('disabled').removeAttr("disabled");
204 $('#show_rev').removeClass('disabled').removeAttr("disabled");
204 $('#show_rev').removeClass('disabled').removeAttr("disabled");
205 });
205 });
206
206
207 // show more authors
207 // show more authors
208 $('#show_authors').on('click', function(e) {
208 $('#show_authors').on('click', function(e) {
209 e.preventDefault();
209 e.preventDefault();
210 var url = pyroutes.url('files_authors_home',
210 var url = pyroutes.url('files_authors_home',
211 {'repo_name': templateContext.repo_name,
211 {'repo_name': templateContext.repo_name,
212 'revision': state.rev, 'f_path': state.f_path});
212 'revision': state.rev, 'f_path': state.f_path});
213
213
214 $.pjax({
214 $.pjax({
215 url: url,
215 url: url,
216 data: 'annotate=${"1" if c.annotate else "0"}',
216 data: 'annotate=${"1" if c.annotate else "0"}',
217 container: '#file_authors',
217 container: '#file_authors',
218 push: false,
218 push: false,
219 timeout: pjaxTimeout
219 timeout: pjaxTimeout
220 }).complete(function(){
220 }).complete(function(){
221 $('#show_authors').hide();
221 $('#show_authors').hide();
222 })
222 })
223 });
223 });
224
224
225 // load file short history
225 // load file short history
226 $('#file_history_overview').on('click', function(e) {
226 $('#file_history_overview').on('click', function(e) {
227 e.preventDefault();
227 e.preventDefault();
228 path = state.f_path;
228 path = state.f_path;
229 if (path.indexOf("#") >= 0) {
229 if (path.indexOf("#") >= 0) {
230 path = path.slice(0, path.indexOf("#"));
230 path = path.slice(0, path.indexOf("#"));
231 }
231 }
232 var url = pyroutes.url('changelog_file_home',
232 var url = pyroutes.url('changelog_file_home',
233 {'repo_name': templateContext.repo_name,
233 {'repo_name': templateContext.repo_name,
234 'revision': state.rev, 'f_path': path, 'limit': 6});
234 'revision': state.rev, 'f_path': path, 'limit': 6});
235 $('#file_history_container').show();
235 $('#file_history_container').show();
236 $('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_TM['Loading ...']));
236 $('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_gettext('Loading ...')));
237
237
238 $.pjax({
238 $.pjax({
239 url: url,
239 url: url,
240 container: '#file_history_container',
240 container: '#file_history_container',
241 push: false,
241 push: false,
242 timeout: pjaxTimeout
242 timeout: pjaxTimeout
243 })
243 })
244 });
244 });
245
245
246 }
246 }
247 else {
247 else {
248 getFilesMetadata();
248 getFilesMetadata();
249
249
250 // fuzzy file filter
250 // fuzzy file filter
251 fileBrowserListeners(state.node_list_url, state.url_base);
251 fileBrowserListeners(state.node_list_url, state.url_base);
252
252
253 // switch to widget
253 // switch to widget
254 select2RefSwitcher('#refs_filter', initialCommitData);
254 select2RefSwitcher('#refs_filter', initialCommitData);
255 $('#refs_filter').on('change', function(e) {
255 $('#refs_filter').on('change', function(e) {
256 var data = $('#refs_filter').select2('data');
256 var data = $('#refs_filter').select2('data');
257 curState.commit_id = data.raw_id;
257 curState.commit_id = data.raw_id;
258 $.pjax({url: data.files_url, container: '#pjax-container', timeout: pjaxTimeout});
258 $.pjax({url: data.files_url, container: '#pjax-container', timeout: pjaxTimeout});
259 });
259 });
260
260
261 $("#prev_commit_link").on('click', function(e) {
261 $("#prev_commit_link").on('click', function(e) {
262 var data = $(this).data();
262 var data = $(this).data();
263 curState.commit_id = data.commitId;
263 curState.commit_id = data.commitId;
264 });
264 });
265
265
266 $("#next_commit_link").on('click', function(e) {
266 $("#next_commit_link").on('click', function(e) {
267 var data = $(this).data();
267 var data = $(this).data();
268 curState.commit_id = data.commitId;
268 curState.commit_id = data.commitId;
269 });
269 });
270
270
271 $('#at_rev').on("keypress", function(e) {
271 $('#at_rev').on("keypress", function(e) {
272 /* ENTER PRESSED */
272 /* ENTER PRESSED */
273 if (e.keyCode === 13) {
273 if (e.keyCode === 13) {
274 var rev = $('#at_rev').val();
274 var rev = $('#at_rev').val();
275 // explicit reload page here. with pjax entering bad input
275 // explicit reload page here. with pjax entering bad input
276 // produces not so nice results
276 // produces not so nice results
277 window.location = pyroutes.url('files_home',
277 window.location = pyroutes.url('files_home',
278 {'repo_name': templateContext.repo_name,
278 {'repo_name': templateContext.repo_name,
279 'revision': rev, 'f_path': state.f_path});
279 'revision': rev, 'f_path': state.f_path});
280 }
280 }
281 });
281 });
282 }
282 }
283 };
283 };
284
284
285 var pjaxTimeout = 5000;
285 var pjaxTimeout = 5000;
286
286
287 $(document).pjax(".pjax-link", "#pjax-container", {
287 $(document).pjax(".pjax-link", "#pjax-container", {
288 "fragment": "#pjax-content",
288 "fragment": "#pjax-content",
289 "maxCacheLength": 1000,
289 "maxCacheLength": 1000,
290 "timeout": pjaxTimeout
290 "timeout": pjaxTimeout
291 });
291 });
292
292
293 // define global back/forward states
293 // define global back/forward states
294 var isPjaxPopState = false;
294 var isPjaxPopState = false;
295 $(document).on('pjax:popstate', function() {
295 $(document).on('pjax:popstate', function() {
296 isPjaxPopState = true;
296 isPjaxPopState = true;
297 });
297 });
298
298
299 $(document).on('pjax:end', function(xhr, options) {
299 $(document).on('pjax:end', function(xhr, options) {
300 if (isPjaxPopState) {
300 if (isPjaxPopState) {
301 isPjaxPopState = false;
301 isPjaxPopState = false;
302 callbacks();
302 callbacks();
303 _NODEFILTER.resetFilter();
303 _NODEFILTER.resetFilter();
304 }
304 }
305
305
306 // run callback for tracking if defined for google analytics etc.
306 // run callback for tracking if defined for google analytics etc.
307 // this is used to trigger tracking on pjax
307 // this is used to trigger tracking on pjax
308 if (typeof window.rhodecode_statechange_callback !== 'undefined') {
308 if (typeof window.rhodecode_statechange_callback !== 'undefined') {
309 var state = getState('statechange');
309 var state = getState('statechange');
310 rhodecode_statechange_callback(state.url, null)
310 rhodecode_statechange_callback(state.url, null)
311 }
311 }
312 });
312 });
313
313
314 $(document).on('pjax:success', function(event, xhr, options) {
314 $(document).on('pjax:success', function(event, xhr, options) {
315 if (event.target.id == "file_history_container") {
315 if (event.target.id == "file_history_container") {
316 $('#file_history_overview').hide();
316 $('#file_history_overview').hide();
317 $('#file_history_overview_full').show();
317 $('#file_history_overview_full').show();
318 timeagoActivate();
318 timeagoActivate();
319 tooltip_activate();
319 tooltip_activate();
320 } else {
320 } else {
321 callbacks();
321 callbacks();
322 }
322 }
323 });
323 });
324
324
325 $(document).ready(function() {
325 $(document).ready(function() {
326 callbacks();
326 callbacks();
327 var search_GET = "${request.GET.get('search','')}";
327 var search_GET = "${request.GET.get('search','')}";
328 if (search_GET == "1") {
328 if (search_GET == "1") {
329 _NODEFILTER.initFilter();
329 _NODEFILTER.initFilter();
330 }
330 }
331 });
331 });
332
332
333 </script>
333 </script>
334
334
335
335
336 </%def>
336 </%def>
@@ -1,232 +1,232 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Files Add') % c.repo_name}
4 ${_('%s Files Add') % c.repo_name}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="menu_bar_nav()">
10 <%def name="menu_bar_nav()">
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Add new file')} @ ${h.show_id(c.commit)}
15 ${_('Add new file')} @ ${h.show_id(c.commit)}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_subnav()">
18 <%def name="menu_bar_subnav()">
19 ${self.repo_menu(active='files')}
19 ${self.repo_menu(active='files')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <div class="box">
23 <div class="box">
24 <div class="title">
24 <div class="title">
25 ${self.repo_page_title(c.rhodecode_db_repo)}
25 ${self.repo_page_title(c.rhodecode_db_repo)}
26 </div>
26 </div>
27 <div class="edit-file-title">
27 <div class="edit-file-title">
28 ${self.breadcrumbs()}
28 ${self.breadcrumbs()}
29 </div>
29 </div>
30 ${h.secure_form(h.url.current(),method='post',id='eform',enctype="multipart/form-data", class_="form-horizontal")}
30 ${h.secure_form(h.url.current(),method='post',id='eform',enctype="multipart/form-data", class_="form-horizontal")}
31 <div class="edit-file-fieldset">
31 <div class="edit-file-fieldset">
32 <div class="fieldset">
32 <div class="fieldset">
33 <div id="destination-label" class="left-label">
33 <div id="destination-label" class="left-label">
34 ${_('Path')}:
34 ${_('Path')}:
35 </div>
35 </div>
36 <div class="right-content">
36 <div class="right-content">
37 <div id="specify-custom-path-container">
37 <div id="specify-custom-path-container">
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
39 <a class="custom-path-link" id="specify-custom-path" href="#">${_('Specify Custom Path')}</a>
39 <a class="custom-path-link" id="specify-custom-path" href="#">${_('Specify Custom Path')}</a>
40 </div>
40 </div>
41 <div id="remove-custom-path-container" style="display: none;">
41 <div id="remove-custom-path-container" style="display: none;">
42 ${c.repo_name}/
42 ${c.repo_name}/
43 <input type="input-small" value="${c.f_path}" size="46" name="location" id="location">
43 <input type="input-small" value="${c.f_path}" size="46" name="location" id="location">
44 <a class="custom-path-link" id="remove-custom-path" href="#">${_('Remove Custom Path')}</a>
44 <a class="custom-path-link" id="remove-custom-path" href="#">${_('Remove Custom Path')}</a>
45 </div>
45 </div>
46 </div>
46 </div>
47 </div>
47 </div>
48 <div id="filename_container" class="fieldset">
48 <div id="filename_container" class="fieldset">
49 <div class="filename-label left-label">
49 <div class="filename-label left-label">
50 ${_('Filename')}:
50 ${_('Filename')}:
51 </div>
51 </div>
52 <div class="right-content">
52 <div class="right-content">
53 <input class="input-small" type="text" value="" size="46" name="filename" id="filename">
53 <input class="input-small" type="text" value="" size="46" name="filename" id="filename">
54 <p>${_('or')} <a id="upload_file_enable" href="#">${_('Upload File')}</a></p>
54 <p>${_('or')} <a id="upload_file_enable" href="#">${_('Upload File')}</a></p>
55 </div>
55 </div>
56 </div>
56 </div>
57 <div id="upload_file_container" class="fieldset" style="display: none;">
57 <div id="upload_file_container" class="fieldset" style="display: none;">
58 <div class="filename-label left-label">
58 <div class="filename-label left-label">
59 ${_('Upload file')}:
59 ${_('Upload file')}:
60 </div>
60 </div>
61 <div class="right-content file-upload-input">
61 <div class="right-content file-upload-input">
62 <label for="upload_file" class="btn btn-default">Browse</label>
62 <label for="upload_file" class="btn btn-default">Browse</label>
63 <span id="selected-file">${_('No file selected')}</span>
63 <span id="selected-file">${_('No file selected')}</span>
64 <input type="file" name="upload_file" id="upload_file">
64 <input type="file" name="upload_file" id="upload_file">
65 <p>${_('or')} <a id="file_enable" href="#">${_('Create New File')}</a></p>
65 <p>${_('or')} <a id="file_enable" href="#">${_('Create New File')}</a></p>
66 </div>
66 </div>
67 </div>
67 </div>
68 </div>
68 </div>
69 <div class="table">
69 <div class="table">
70 <div id="files_data">
70 <div id="files_data">
71 <div id="codeblock" class="codeblock">
71 <div id="codeblock" class="codeblock">
72 <div class="code-header form" id="set_mode_header">
72 <div class="code-header form" id="set_mode_header">
73 <div class="fields">
73 <div class="fields">
74 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
74 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
75 <label for="line_wrap">${_('line wraps')}</label>
75 <label for="line_wrap">${_('line wraps')}</label>
76 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
76 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
77
77
78 <div id="render_preview" class="btn btn-small preview hidden" >${_('Preview')}</div>
78 <div id="render_preview" class="btn btn-small preview hidden" >${_('Preview')}</div>
79 </div>
79 </div>
80 </div>
80 </div>
81 <div id="editor_container">
81 <div id="editor_container">
82 <pre id="editor_pre"></pre>
82 <pre id="editor_pre"></pre>
83 <textarea id="editor" name="content" ></textarea>
83 <textarea id="editor" name="content" ></textarea>
84 <div id="editor_preview"></div>
84 <div id="editor_preview"></div>
85 </div>
85 </div>
86 </div>
86 </div>
87 </div>
87 </div>
88 </div>
88 </div>
89
89
90 <div class="edit-file-fieldset">
90 <div class="edit-file-fieldset">
91 <div class="fieldset">
91 <div class="fieldset">
92 <div id="commit-message-label" class="commit-message-label left-label">
92 <div id="commit-message-label" class="commit-message-label left-label">
93 ${_('Commit Message')}:
93 ${_('Commit Message')}:
94 </div>
94 </div>
95 <div class="right-content">
95 <div class="right-content">
96 <div class="message">
96 <div class="message">
97 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
97 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
98 </div>
98 </div>
99 </div>
99 </div>
100 </div>
100 </div>
101 <div class="pull-right">
101 <div class="pull-right">
102 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
102 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
103 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-success")}
103 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-success")}
104 </div>
104 </div>
105 </div>
105 </div>
106 ${h.end_form()}
106 ${h.end_form()}
107 </div>
107 </div>
108 <script type="text/javascript">
108 <script type="text/javascript">
109
109
110 $('#commit_btn').on('click', function() {
110 $('#commit_btn').on('click', function() {
111 var button = $(this);
111 var button = $(this);
112 if (button.hasClass('clicked')) {
112 if (button.hasClass('clicked')) {
113 button.attr('disabled', true);
113 button.attr('disabled', true);
114 } else {
114 } else {
115 button.addClass('clicked');
115 button.addClass('clicked');
116 }
116 }
117 });
117 });
118
118
119 $('#specify-custom-path').on('click', function(e){
119 $('#specify-custom-path').on('click', function(e){
120 e.preventDefault();
120 e.preventDefault();
121 $('#specify-custom-path-container').hide();
121 $('#specify-custom-path-container').hide();
122 $('#remove-custom-path-container').show();
122 $('#remove-custom-path-container').show();
123 $('#destination-label').css('margin-top', '13px');
123 $('#destination-label').css('margin-top', '13px');
124 });
124 });
125
125
126 $('#remove-custom-path').on('click', function(e){
126 $('#remove-custom-path').on('click', function(e){
127 e.preventDefault();
127 e.preventDefault();
128 $('#specify-custom-path-container').show();
128 $('#specify-custom-path-container').show();
129 $('#remove-custom-path-container').hide();
129 $('#remove-custom-path-container').hide();
130 $('#location').val('${c.f_path}');
130 $('#location').val('${c.f_path}');
131 $('#destination-label').css('margin-top', '0');
131 $('#destination-label').css('margin-top', '0');
132 });
132 });
133
133
134 var hide_upload = function(){
134 var hide_upload = function(){
135 $('#files_data').show();
135 $('#files_data').show();
136 $('#upload_file_container').hide();
136 $('#upload_file_container').hide();
137 $('#filename_container').show();
137 $('#filename_container').show();
138 };
138 };
139
139
140 $('#file_enable').on('click', function(e){
140 $('#file_enable').on('click', function(e){
141 e.preventDefault();
141 e.preventDefault();
142 hide_upload();
142 hide_upload();
143 });
143 });
144
144
145 $('#upload_file_enable').on('click', function(e){
145 $('#upload_file_enable').on('click', function(e){
146 e.preventDefault();
146 e.preventDefault();
147 $('#files_data').hide();
147 $('#files_data').hide();
148 $('#upload_file_container').show();
148 $('#upload_file_container').show();
149 $('#filename_container').hide();
149 $('#filename_container').hide();
150 if (detectIE() && detectIE() <= 9) {
150 if (detectIE() && detectIE() <= 9) {
151 $('#upload_file_container .file-upload-input label').hide();
151 $('#upload_file_container .file-upload-input label').hide();
152 $('#upload_file_container .file-upload-input span').hide();
152 $('#upload_file_container .file-upload-input span').hide();
153 $('#upload_file_container .file-upload-input input').show();
153 $('#upload_file_container .file-upload-input input').show();
154 }
154 }
155 });
155 });
156
156
157 $('#upload_file').on('change', function() {
157 $('#upload_file').on('change', function() {
158 if (detectIE() && detectIE() <= 9) {
158 if (detectIE() && detectIE() <= 9) {
159 if (this.files && this.files[0]) {
159 if (this.files && this.files[0]) {
160 $('#selected-file').html(this.files[0].name);
160 $('#selected-file').html(this.files[0].name);
161 }
161 }
162 }
162 }
163 });
163 });
164
164
165 hide_upload();
165 hide_upload();
166
166
167 var renderer = "";
167 var renderer = "";
168 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}";
168 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}";
169 var myCodeMirror = initCodeMirror('editor', reset_url, false);
169 var myCodeMirror = initCodeMirror('editor', reset_url, false);
170
170
171 var modes_select = $('#set_mode');
171 var modes_select = $('#set_mode');
172 fillCodeMirrorOptions(modes_select);
172 fillCodeMirrorOptions(modes_select);
173
173
174 var filename_selector = '#filename';
174 var filename_selector = '#filename';
175 var callback = function(filename, mimetype, mode){
175 var callback = function(filename, mimetype, mode){
176 CodeMirrorPreviewEnable(mode);
176 CodeMirrorPreviewEnable(mode);
177 };
177 };
178 // on change of select field set mode
178 // on change of select field set mode
179 setCodeMirrorModeFromSelect(
179 setCodeMirrorModeFromSelect(
180 modes_select, filename_selector, myCodeMirror, callback);
180 modes_select, filename_selector, myCodeMirror, callback);
181
181
182 // on entering the new filename set mode, from given extension
182 // on entering the new filename set mode, from given extension
183 setCodeMirrorModeFromInput(
183 setCodeMirrorModeFromInput(
184 modes_select, filename_selector, myCodeMirror, callback);
184 modes_select, filename_selector, myCodeMirror, callback);
185
185
186 // if the file is renderable set line wraps automatically
186 // if the file is renderable set line wraps automatically
187 if (renderer !== ""){
187 if (renderer !== ""){
188 var line_wrap = 'on';
188 var line_wrap = 'on';
189 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
189 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
190 setCodeMirrorLineWrap(myCodeMirror, true);
190 setCodeMirrorLineWrap(myCodeMirror, true);
191 }
191 }
192
192
193 // on select line wraps change the editor
193 // on select line wraps change the editor
194 $('#line_wrap').on('change', function(e){
194 $('#line_wrap').on('change', function(e){
195 var selected = e.currentTarget;
195 var selected = e.currentTarget;
196 var line_wraps = {'on': true, 'off': false}[selected.value];
196 var line_wraps = {'on': true, 'off': false}[selected.value];
197 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
197 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
198 });
198 });
199
199
200 // render preview/edit button
200 // render preview/edit button
201 $('#render_preview').on('click', function(e){
201 $('#render_preview').on('click', function(e){
202 if($(this).hasClass('preview')){
202 if($(this).hasClass('preview')){
203 $(this).removeClass('preview');
203 $(this).removeClass('preview');
204 $(this).html("${_('Edit')}");
204 $(this).html("${_('Edit')}");
205 $('#editor_preview').show();
205 $('#editor_preview').show();
206 $(myCodeMirror.getWrapperElement()).hide();
206 $(myCodeMirror.getWrapperElement()).hide();
207
207
208 var possible_renderer = {
208 var possible_renderer = {
209 'rst':'rst',
209 'rst':'rst',
210 'markdown':'markdown',
210 'markdown':'markdown',
211 'gfm': 'markdown'}[myCodeMirror.getMode().name];
211 'gfm': 'markdown'}[myCodeMirror.getMode().name];
212 var _text = myCodeMirror.getValue();
212 var _text = myCodeMirror.getValue();
213 var _renderer = possible_renderer || DEFAULT_RENDERER;
213 var _renderer = possible_renderer || DEFAULT_RENDERER;
214 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
214 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
215 $('#editor_preview').html(_TM['Loading ...']);
215 $('#editor_preview').html(_gettext('Loading ...'));
216 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
216 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
217
217
218 ajaxPOST(url, post_data, function(o){
218 ajaxPOST(url, post_data, function(o){
219 $('#editor_preview').html(o);
219 $('#editor_preview').html(o);
220 })
220 })
221 }
221 }
222 else{
222 else{
223 $(this).addClass('preview');
223 $(this).addClass('preview');
224 $(this).html("${_('Preview')}");
224 $(this).html("${_('Preview')}");
225 $('#editor_preview').hide();
225 $('#editor_preview').hide();
226 $(myCodeMirror.getWrapperElement()).show();
226 $(myCodeMirror.getWrapperElement()).show();
227 }
227 }
228 });
228 });
229 $('#filename').focus();
229 $('#filename').focus();
230
230
231 </script>
231 </script>
232 </%def>
232 </%def>
@@ -1,193 +1,193 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s File Edit') % c.repo_name}
4 ${_('%s File Edit') % c.repo_name}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="menu_bar_nav()">
10 <%def name="menu_bar_nav()">
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Edit file')} @ ${h.show_id(c.commit)}
15 ${_('Edit file')} @ ${h.show_id(c.commit)}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_subnav()">
18 <%def name="menu_bar_subnav()">
19 ${self.repo_menu(active='files')}
19 ${self.repo_menu(active='files')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <% renderer = h.renderer_from_filename(c.f_path)%>
23 <% renderer = h.renderer_from_filename(c.f_path)%>
24 <div class="box">
24 <div class="box">
25 <div class="title">
25 <div class="title">
26 ${self.repo_page_title(c.rhodecode_db_repo)}
26 ${self.repo_page_title(c.rhodecode_db_repo)}
27 </div>
27 </div>
28 <div class="edit-file-title">
28 <div class="edit-file-title">
29 ${self.breadcrumbs()}
29 ${self.breadcrumbs()}
30 </div>
30 </div>
31 <div class="edit-file-fieldset">
31 <div class="edit-file-fieldset">
32 <div class="fieldset">
32 <div class="fieldset">
33 <div id="destination-label" class="left-label">
33 <div id="destination-label" class="left-label">
34 ${_('Path')}:
34 ${_('Path')}:
35 </div>
35 </div>
36 <div class="right-content">
36 <div class="right-content">
37 <div id="specify-custom-path-container">
37 <div id="specify-custom-path-container">
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
39 </div>
39 </div>
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
43
43
44 <div class="table">
44 <div class="table">
45 ${h.secure_form(h.url.current(),method='post',id='eform')}
45 ${h.secure_form(h.url.current(),method='post',id='eform')}
46 <div id="codeblock" class="codeblock" >
46 <div id="codeblock" class="codeblock" >
47 <div class="code-header">
47 <div class="code-header">
48 <div class="stats">
48 <div class="stats">
49 <i class="icon-file"></i>
49 <i class="icon-file"></i>
50 <span class="item">${h.link_to("r%s:%s" % (c.file.commit.idx,h.short_id(c.file.commit.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.commit.raw_id))}</span>
50 <span class="item">${h.link_to("r%s:%s" % (c.file.commit.idx,h.short_id(c.file.commit.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.commit.raw_id))}</span>
51 <span class="item">${h.format_byte_size_binary(c.file.size)}</span>
51 <span class="item">${h.format_byte_size_binary(c.file.size)}</span>
52 <span class="item last">${c.file.mimetype}</span>
52 <span class="item last">${c.file.mimetype}</span>
53 <div class="buttons">
53 <div class="buttons">
54 <a class="btn btn-mini" href="${h.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
54 <a class="btn btn-mini" href="${h.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
55 <i class="icon-time"></i> ${_('history')}
55 <i class="icon-time"></i> ${_('history')}
56 </a>
56 </a>
57
57
58 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
58 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
59 % if not c.file.is_binary:
59 % if not c.file.is_binary:
60 %if True:
60 %if True:
61 ${h.link_to(_('source'), h.url('files_home', repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
61 ${h.link_to(_('source'), h.url('files_home', repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
62 %else:
62 %else:
63 ${h.link_to(_('annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
63 ${h.link_to(_('annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
64 %endif
64 %endif
65 ${h.link_to(_('raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
65 ${h.link_to(_('raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
66 <a class="btn btn-mini" href="${h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}">
66 <a class="btn btn-mini" href="${h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}">
67 <i class="icon-archive"></i> ${_('download')}
67 <i class="icon-archive"></i> ${_('download')}
68 </a>
68 </a>
69 % endif
69 % endif
70 % endif
70 % endif
71 </div>
71 </div>
72 </div>
72 </div>
73 <div class="form">
73 <div class="form">
74 <label for="set_mode">${_('Editing file')}:</label>
74 <label for="set_mode">${_('Editing file')}:</label>
75 ${'%s /' % c.file.dir_path if c.file.dir_path else c.file.dir_path}
75 ${'%s /' % c.file.dir_path if c.file.dir_path else c.file.dir_path}
76 <input id="filename" type="text" name="filename" value="${c.file.name}">
76 <input id="filename" type="text" name="filename" value="${c.file.name}">
77
77
78 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
78 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
79 <label for="line_wrap">${_('line wraps')}</label>
79 <label for="line_wrap">${_('line wraps')}</label>
80 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
80 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
81
81
82 <div id="render_preview" class="btn btn-small preview hidden">${_('Preview')}</div>
82 <div id="render_preview" class="btn btn-small preview hidden">${_('Preview')}</div>
83 </div>
83 </div>
84 </div>
84 </div>
85 <div id="editor_container">
85 <div id="editor_container">
86 <pre id="editor_pre"></pre>
86 <pre id="editor_pre"></pre>
87 <textarea id="editor" name="content" >${h.escape(c.file.content)|n}</textarea>
87 <textarea id="editor" name="content" >${h.escape(c.file.content)|n}</textarea>
88 <div id="editor_preview" ></div>
88 <div id="editor_preview" ></div>
89 </div>
89 </div>
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 <div class="edit-file-fieldset">
93 <div class="edit-file-fieldset">
94 <div class="fieldset">
94 <div class="fieldset">
95 <div id="commit-message-label" class="commit-message-label left-label">
95 <div id="commit-message-label" class="commit-message-label left-label">
96 ${_('Commit Message')}:
96 ${_('Commit Message')}:
97 </div>
97 </div>
98 <div class="right-content">
98 <div class="right-content">
99 <div class="message">
99 <div class="message">
100 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
100 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
101 </div>
101 </div>
102 </div>
102 </div>
103 </div>
103 </div>
104 <div class="pull-right">
104 <div class="pull-right">
105 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
105 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
106 ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-success")}
106 ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-success")}
107 </div>
107 </div>
108 </div>
108 </div>
109 ${h.end_form()}
109 ${h.end_form()}
110 </div>
110 </div>
111
111
112 <script type="text/javascript">
112 <script type="text/javascript">
113 $(document).ready(function(){
113 $(document).ready(function(){
114 var renderer = "${renderer}";
114 var renderer = "${renderer}";
115 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.file.path)}";
115 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.file.path)}";
116 var myCodeMirror = initCodeMirror('editor', reset_url);
116 var myCodeMirror = initCodeMirror('editor', reset_url);
117
117
118 var modes_select = $('#set_mode');
118 var modes_select = $('#set_mode');
119 fillCodeMirrorOptions(modes_select);
119 fillCodeMirrorOptions(modes_select);
120
120
121 // try to detect the mode based on the file we edit
121 // try to detect the mode based on the file we edit
122 var mimetype = "${c.file.mimetype}";
122 var mimetype = "${c.file.mimetype}";
123 var detected_mode = detectCodeMirrorMode(
123 var detected_mode = detectCodeMirrorMode(
124 "${c.file.name}", mimetype);
124 "${c.file.name}", mimetype);
125
125
126 if(detected_mode){
126 if(detected_mode){
127 setCodeMirrorMode(myCodeMirror, detected_mode);
127 setCodeMirrorMode(myCodeMirror, detected_mode);
128 $(modes_select).select2("val", mimetype);
128 $(modes_select).select2("val", mimetype);
129 $(modes_select).change();
129 $(modes_select).change();
130 setCodeMirrorMode(myCodeMirror, detected_mode);
130 setCodeMirrorMode(myCodeMirror, detected_mode);
131 }
131 }
132
132
133 var filename_selector = '#filename';
133 var filename_selector = '#filename';
134 var callback = function(filename, mimetype, mode){
134 var callback = function(filename, mimetype, mode){
135 CodeMirrorPreviewEnable(mode);
135 CodeMirrorPreviewEnable(mode);
136 };
136 };
137 // on change of select field set mode
137 // on change of select field set mode
138 setCodeMirrorModeFromSelect(
138 setCodeMirrorModeFromSelect(
139 modes_select, filename_selector, myCodeMirror, callback);
139 modes_select, filename_selector, myCodeMirror, callback);
140
140
141 // on entering the new filename set mode, from given extension
141 // on entering the new filename set mode, from given extension
142 setCodeMirrorModeFromInput(
142 setCodeMirrorModeFromInput(
143 modes_select, filename_selector, myCodeMirror, callback);
143 modes_select, filename_selector, myCodeMirror, callback);
144
144
145 // if the file is renderable set line wraps automatically
145 // if the file is renderable set line wraps automatically
146 if (renderer !== ""){
146 if (renderer !== ""){
147 var line_wrap = 'on';
147 var line_wrap = 'on';
148 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
148 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
149 setCodeMirrorLineWrap(myCodeMirror, true);
149 setCodeMirrorLineWrap(myCodeMirror, true);
150 }
150 }
151 // on select line wraps change the editor
151 // on select line wraps change the editor
152 $('#line_wrap').on('change', function(e){
152 $('#line_wrap').on('change', function(e){
153 var selected = e.currentTarget;
153 var selected = e.currentTarget;
154 var line_wraps = {'on': true, 'off': false}[selected.value];
154 var line_wraps = {'on': true, 'off': false}[selected.value];
155 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
155 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
156 });
156 });
157
157
158 // render preview/edit button
158 // render preview/edit button
159 if (mimetype === 'text/x-rst' || mimetype === 'text/plain') {
159 if (mimetype === 'text/x-rst' || mimetype === 'text/plain') {
160 $('#render_preview').removeClass('hidden');
160 $('#render_preview').removeClass('hidden');
161 }
161 }
162 $('#render_preview').on('click', function(e){
162 $('#render_preview').on('click', function(e){
163 if($(this).hasClass('preview')){
163 if($(this).hasClass('preview')){
164 $(this).removeClass('preview');
164 $(this).removeClass('preview');
165 $(this).html("${_('Edit')}");
165 $(this).html("${_('Edit')}");
166 $('#editor_preview').show();
166 $('#editor_preview').show();
167 $(myCodeMirror.getWrapperElement()).hide();
167 $(myCodeMirror.getWrapperElement()).hide();
168
168
169 var possible_renderer = {
169 var possible_renderer = {
170 'rst':'rst',
170 'rst':'rst',
171 'markdown':'markdown',
171 'markdown':'markdown',
172 'gfm': 'markdown'}[myCodeMirror.getMode().name];
172 'gfm': 'markdown'}[myCodeMirror.getMode().name];
173 var _text = myCodeMirror.getValue();
173 var _text = myCodeMirror.getValue();
174 var _renderer = possible_renderer || DEFAULT_RENDERER;
174 var _renderer = possible_renderer || DEFAULT_RENDERER;
175 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
175 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
176 $('#editor_preview').html(_TM['Loading ...']);
176 $('#editor_preview').html(_gettext('Loading ...'));
177 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
177 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
178
178
179 ajaxPOST(url, post_data, function(o){
179 ajaxPOST(url, post_data, function(o){
180 $('#editor_preview').html(o);
180 $('#editor_preview').html(o);
181 })
181 })
182 }
182 }
183 else{
183 else{
184 $(this).addClass('preview');
184 $(this).addClass('preview');
185 $(this).html("${_('Preview')}");
185 $(this).html("${_('Preview')}");
186 $('#editor_preview').hide();
186 $('#editor_preview').hide();
187 $(myCodeMirror.getWrapperElement()).show();
187 $(myCodeMirror.getWrapperElement()).show();
188 }
188 }
189 });
189 });
190
190
191 })
191 })
192 </script>
192 </script>
193 </%def>
193 </%def>
@@ -1,568 +1,568 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
4 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="breadcrumbs_links()">
10 <%def name="breadcrumbs_links()">
11 <span id="pr-title">
11 <span id="pr-title">
12 ${c.pull_request.title}
12 ${c.pull_request.title}
13 %if c.pull_request.is_closed():
13 %if c.pull_request.is_closed():
14 (${_('Closed')})
14 (${_('Closed')})
15 %endif
15 %endif
16 </span>
16 </span>
17 <div id="pr-title-edit" class="input" style="display: none;">
17 <div id="pr-title-edit" class="input" style="display: none;">
18 ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)}
18 ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)}
19 </div>
19 </div>
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_nav()">
22 <%def name="menu_bar_nav()">
23 ${self.menu_items(active='repositories')}
23 ${self.menu_items(active='repositories')}
24 </%def>
24 </%def>
25
25
26 <%def name="menu_bar_subnav()">
26 <%def name="menu_bar_subnav()">
27 ${self.repo_menu(active='showpullrequest')}
27 ${self.repo_menu(active='showpullrequest')}
28 </%def>
28 </%def>
29
29
30 <%def name="main()">
30 <%def name="main()">
31 <script type="text/javascript">
31 <script type="text/javascript">
32 // TODO: marcink switch this to pyroutes
32 // TODO: marcink switch this to pyroutes
33 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
33 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
34 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
34 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
35 </script>
35 </script>
36 <div class="box">
36 <div class="box">
37 <div class="title">
37 <div class="title">
38 ${self.repo_page_title(c.rhodecode_db_repo)}
38 ${self.repo_page_title(c.rhodecode_db_repo)}
39 </div>
39 </div>
40
40
41 ${self.breadcrumbs()}
41 ${self.breadcrumbs()}
42
42
43
43
44 <div class="box pr-summary">
44 <div class="box pr-summary">
45 <div class="summary-details block-left">
45 <div class="summary-details block-left">
46 <%summary = lambda n:{False:'summary-short'}.get(n)%>
46 <%summary = lambda n:{False:'summary-short'}.get(n)%>
47 <div class="pr-details-title">
47 <div class="pr-details-title">
48 ${_('Pull request #%s') % c.pull_request.pull_request_id} ${_('From')} ${h.format_date(c.pull_request.created_on)}
48 ${_('Pull request #%s') % c.pull_request.pull_request_id} ${_('From')} ${h.format_date(c.pull_request.created_on)}
49 %if c.allowed_to_update:
49 %if c.allowed_to_update:
50 <span id="open_edit_pullrequest" class="block-right action_button">${_('Edit')}</span>
50 <span id="open_edit_pullrequest" class="block-right action_button">${_('Edit')}</span>
51 <span id="close_edit_pullrequest" class="block-right action_button" style="display: none;">${_('Close')}</span>
51 <span id="close_edit_pullrequest" class="block-right action_button" style="display: none;">${_('Close')}</span>
52 %endif
52 %endif
53 </div>
53 </div>
54
54
55 <div id="summary" class="fields pr-details-content">
55 <div id="summary" class="fields pr-details-content">
56 <div class="field">
56 <div class="field">
57 <div class="label-summary">
57 <div class="label-summary">
58 <label>${_('Origin')}:</label>
58 <label>${_('Origin')}:</label>
59 </div>
59 </div>
60 <div class="input">
60 <div class="input">
61 <div class="pr-origininfo">
61 <div class="pr-origininfo">
62 ## branch link is only valid if it is a branch
62 ## branch link is only valid if it is a branch
63 <span class="tag">
63 <span class="tag">
64 %if c.pull_request.source_ref_parts.type == 'branch':
64 %if c.pull_request.source_ref_parts.type == 'branch':
65 <a href="${h.url('changelog_home', repo_name=c.pull_request.source_repo.repo_name, branch=c.pull_request.source_ref_parts.name)}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a>
65 <a href="${h.url('changelog_home', repo_name=c.pull_request.source_repo.repo_name, branch=c.pull_request.source_ref_parts.name)}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a>
66 %else:
66 %else:
67 ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}
67 ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}
68 %endif
68 %endif
69 </span>
69 </span>
70 <span class="clone-url">
70 <span class="clone-url">
71 <a href="${h.url('summary_home', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
71 <a href="${h.url('summary_home', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
72 </span>
72 </span>
73 </div>
73 </div>
74 <div class="pr-pullinfo">
74 <div class="pr-pullinfo">
75 %if h.is_hg(c.pull_request.source_repo):
75 %if h.is_hg(c.pull_request.source_repo):
76 <input type="text" value="hg pull -r ${h.short_id(c.source_ref)} ${c.pull_request.source_repo.clone_url()}" readonly="readonly">
76 <input type="text" value="hg pull -r ${h.short_id(c.source_ref)} ${c.pull_request.source_repo.clone_url()}" readonly="readonly">
77 %elif h.is_git(c.pull_request.source_repo):
77 %elif h.is_git(c.pull_request.source_repo):
78 <input type="text" value="git pull ${c.pull_request.source_repo.clone_url()} ${c.pull_request.source_ref_parts.name}" readonly="readonly">
78 <input type="text" value="git pull ${c.pull_request.source_repo.clone_url()} ${c.pull_request.source_ref_parts.name}" readonly="readonly">
79 %endif
79 %endif
80 </div>
80 </div>
81 </div>
81 </div>
82 </div>
82 </div>
83 <div class="field">
83 <div class="field">
84 <div class="label-summary">
84 <div class="label-summary">
85 <label>${_('Target')}:</label>
85 <label>${_('Target')}:</label>
86 </div>
86 </div>
87 <div class="input">
87 <div class="input">
88 <div class="pr-targetinfo">
88 <div class="pr-targetinfo">
89 ## branch link is only valid if it is a branch
89 ## branch link is only valid if it is a branch
90 <span class="tag">
90 <span class="tag">
91 %if c.pull_request.target_ref_parts.type == 'branch':
91 %if c.pull_request.target_ref_parts.type == 'branch':
92 <a href="${h.url('changelog_home', repo_name=c.pull_request.target_repo.repo_name, branch=c.pull_request.target_ref_parts.name)}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a>
92 <a href="${h.url('changelog_home', repo_name=c.pull_request.target_repo.repo_name, branch=c.pull_request.target_ref_parts.name)}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a>
93 %else:
93 %else:
94 ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}
94 ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}
95 %endif
95 %endif
96 </span>
96 </span>
97 <span class="clone-url">
97 <span class="clone-url">
98 <a href="${h.url('summary_home', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
98 <a href="${h.url('summary_home', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
99 </span>
99 </span>
100 </div>
100 </div>
101 </div>
101 </div>
102 </div>
102 </div>
103 <div class="field">
103 <div class="field">
104 <div class="label-summary">
104 <div class="label-summary">
105 <label>${_('Review')}:</label>
105 <label>${_('Review')}:</label>
106 </div>
106 </div>
107 <div class="input">
107 <div class="input">
108 %if c.pull_request_review_status:
108 %if c.pull_request_review_status:
109 <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div>
109 <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div>
110 <span class="changeset-status-lbl tooltip">
110 <span class="changeset-status-lbl tooltip">
111 %if c.pull_request.is_closed():
111 %if c.pull_request.is_closed():
112 ${_('Closed')},
112 ${_('Closed')},
113 %endif
113 %endif
114 ${h.commit_status_lbl(c.pull_request_review_status)}
114 ${h.commit_status_lbl(c.pull_request_review_status)}
115 </span>
115 </span>
116 - ${ungettext('calculated based on %s reviewer vote', 'calculated based on %s reviewers votes', len(c.pull_request_reviewers)) % len(c.pull_request_reviewers)}
116 - ${ungettext('calculated based on %s reviewer vote', 'calculated based on %s reviewers votes', len(c.pull_request_reviewers)) % len(c.pull_request_reviewers)}
117 %endif
117 %endif
118 </div>
118 </div>
119 </div>
119 </div>
120 <div class="field">
120 <div class="field">
121 <div class="pr-description-label label-summary">
121 <div class="pr-description-label label-summary">
122 <label>${_('Description')}:</label>
122 <label>${_('Description')}:</label>
123 </div>
123 </div>
124 <div id="pr-desc" class="input">
124 <div id="pr-desc" class="input">
125 <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div>
125 <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div>
126 </div>
126 </div>
127 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
127 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
128 <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea>
128 <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea>
129 </div>
129 </div>
130 </div>
130 </div>
131 <div class="field">
131 <div class="field">
132 <div class="label-summary">
132 <div class="label-summary">
133 <label>${_('Comments')}:</label>
133 <label>${_('Comments')}:</label>
134 </div>
134 </div>
135 <div class="input">
135 <div class="input">
136 <div>
136 <div>
137 <div class="comments-number">
137 <div class="comments-number">
138 %if c.comments:
138 %if c.comments:
139 <a href="#comments">${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}</a>,
139 <a href="#comments">${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}</a>,
140 %else:
140 %else:
141 ${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}
141 ${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}
142 %endif
142 %endif
143 %if c.inline_cnt:
143 %if c.inline_cnt:
144 ## this is replaced with a proper link to first comment via JS linkifyComments() func
144 ## this is replaced with a proper link to first comment via JS linkifyComments() func
145 <a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
145 <a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
146 %else:
146 %else:
147 ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
147 ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
148 %endif
148 %endif
149
149
150 % if c.outdated_cnt:
150 % if c.outdated_cnt:
151 ,${ungettext("%d Outdated Comment", "%d Outdated Comments", c.outdated_cnt) % c.outdated_cnt} <span id="show-outdated-comments" class="btn btn-link">${_('(Show)')}</span>
151 ,${ungettext("%d Outdated Comment", "%d Outdated Comments", c.outdated_cnt) % c.outdated_cnt} <span id="show-outdated-comments" class="btn btn-link">${_('(Show)')}</span>
152 % endif
152 % endif
153 </div>
153 </div>
154 </div>
154 </div>
155 </div>
155 </div>
156 </div>
156 </div>
157 <div id="pr-save" class="field" style="display: none;">
157 <div id="pr-save" class="field" style="display: none;">
158 <div class="label-summary"></div>
158 <div class="label-summary"></div>
159 <div class="input">
159 <div class="input">
160 <span id="edit_pull_request" class="btn btn-small">${_('Save Changes')}</span>
160 <span id="edit_pull_request" class="btn btn-small">${_('Save Changes')}</span>
161 </div>
161 </div>
162 </div>
162 </div>
163 </div>
163 </div>
164 </div>
164 </div>
165 <div>
165 <div>
166 ## AUTHOR
166 ## AUTHOR
167 <div class="reviewers-title block-right">
167 <div class="reviewers-title block-right">
168 <div class="pr-details-title">
168 <div class="pr-details-title">
169 ${_('Author')}
169 ${_('Author')}
170 </div>
170 </div>
171 </div>
171 </div>
172 <div class="block-right pr-details-content reviewers">
172 <div class="block-right pr-details-content reviewers">
173 <ul class="group_members">
173 <ul class="group_members">
174 <li>
174 <li>
175 ${self.gravatar_with_user(c.pull_request.author.email, 16)}
175 ${self.gravatar_with_user(c.pull_request.author.email, 16)}
176 </li>
176 </li>
177 </ul>
177 </ul>
178 </div>
178 </div>
179 ## REVIEWERS
179 ## REVIEWERS
180 <div class="reviewers-title block-right">
180 <div class="reviewers-title block-right">
181 <div class="pr-details-title">
181 <div class="pr-details-title">
182 ${_('Pull request reviewers')}
182 ${_('Pull request reviewers')}
183 %if c.allowed_to_update:
183 %if c.allowed_to_update:
184 <span id="open_edit_reviewers" class="block-right action_button">${_('Edit')}</span>
184 <span id="open_edit_reviewers" class="block-right action_button">${_('Edit')}</span>
185 <span id="close_edit_reviewers" class="block-right action_button" style="display: none;">${_('Close')}</span>
185 <span id="close_edit_reviewers" class="block-right action_button" style="display: none;">${_('Close')}</span>
186 %endif
186 %endif
187 </div>
187 </div>
188 </div>
188 </div>
189 <div id="reviewers" class="block-right pr-details-content reviewers">
189 <div id="reviewers" class="block-right pr-details-content reviewers">
190 ## members goes here !
190 ## members goes here !
191 <ul id="review_members" class="group_members">
191 <ul id="review_members" class="group_members">
192 %for member,status in c.pull_request_reviewers:
192 %for member,status in c.pull_request_reviewers:
193 <li id="reviewer_${member.user_id}">
193 <li id="reviewer_${member.user_id}">
194 <div class="reviewers_member">
194 <div class="reviewers_member">
195 <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
195 <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
196 <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div>
196 <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div>
197 </div>
197 </div>
198 <div id="reviewer_${member.user_id}_name" class="reviewer_name">
198 <div id="reviewer_${member.user_id}_name" class="reviewer_name">
199 ${self.gravatar_with_user(member.email, 16)} <div class="reviewer">(${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</div>
199 ${self.gravatar_with_user(member.email, 16)} <div class="reviewer">(${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</div>
200 </div>
200 </div>
201 <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="review_members" />
201 <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="review_members" />
202 %if c.allowed_to_update:
202 %if c.allowed_to_update:
203 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;">
203 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;">
204 <i class="icon-remove-sign" ></i>
204 <i class="icon-remove-sign" ></i>
205 </div>
205 </div>
206 %endif
206 %endif
207 </div>
207 </div>
208 </li>
208 </li>
209 %endfor
209 %endfor
210 </ul>
210 </ul>
211 %if not c.pull_request.is_closed():
211 %if not c.pull_request.is_closed():
212 <div id="add_reviewer_input" class='ac' style="display: none;">
212 <div id="add_reviewer_input" class='ac' style="display: none;">
213 %if c.allowed_to_update:
213 %if c.allowed_to_update:
214 <div class="reviewer_ac">
214 <div class="reviewer_ac">
215 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))}
215 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))}
216 <div id="reviewers_container"></div>
216 <div id="reviewers_container"></div>
217 </div>
217 </div>
218 <div>
218 <div>
219 <span id="update_pull_request" class="btn btn-small">${_('Save Changes')}</span>
219 <span id="update_pull_request" class="btn btn-small">${_('Save Changes')}</span>
220 </div>
220 </div>
221 %endif
221 %endif
222 </div>
222 </div>
223 %endif
223 %endif
224 </div>
224 </div>
225 </div>
225 </div>
226 </div>
226 </div>
227 <div class="box">
227 <div class="box">
228 ##DIFF
228 ##DIFF
229 <div class="table" >
229 <div class="table" >
230 <div id="changeset_compare_view_content">
230 <div id="changeset_compare_view_content">
231 ##CS
231 ##CS
232 % if c.missing_requirements:
232 % if c.missing_requirements:
233 <div class="box">
233 <div class="box">
234 <div class="alert alert-warning">
234 <div class="alert alert-warning">
235 <div>
235 <div>
236 <strong>${_('Missing requirements:')}</strong>
236 <strong>${_('Missing requirements:')}</strong>
237 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
237 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
238 </div>
238 </div>
239 </div>
239 </div>
240 </div>
240 </div>
241 % elif c.missing_commits:
241 % elif c.missing_commits:
242 <div class="box">
242 <div class="box">
243 <div class="alert alert-warning">
243 <div class="alert alert-warning">
244 <div>
244 <div>
245 <strong>${_('Missing commits')}:</strong>
245 <strong>${_('Missing commits')}:</strong>
246 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
246 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
247 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
247 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
248 </div>
248 </div>
249 </div>
249 </div>
250 </div>
250 </div>
251 % endif
251 % endif
252 <div class="compare_view_commits_title">
252 <div class="compare_view_commits_title">
253 % if c.allowed_to_update and not c.pull_request.is_closed():
253 % if c.allowed_to_update and not c.pull_request.is_closed():
254 <button id="update_commits" class="btn btn-small">${_('Update commits')}</button>
254 <button id="update_commits" class="btn btn-small">${_('Update commits')}</button>
255 % endif
255 % endif
256 % if len(c.commit_ranges):
256 % if len(c.commit_ranges):
257 <h2>${ungettext('Compare View: %s commit','Compare View: %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}</h2>
257 <h2>${ungettext('Compare View: %s commit','Compare View: %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}</h2>
258 % endif
258 % endif
259 </div>
259 </div>
260 % if not c.missing_commits:
260 % if not c.missing_commits:
261 <%include file="/compare/compare_commits.html" />
261 <%include file="/compare/compare_commits.html" />
262 ## FILES
262 ## FILES
263 <div class="cs_files_title">
263 <div class="cs_files_title">
264 <span class="cs_files_expand">
264 <span class="cs_files_expand">
265 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
265 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
266 </span>
266 </span>
267 <h2>
267 <h2>
268 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
268 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
269 </h2>
269 </h2>
270 </div>
270 </div>
271 % endif
271 % endif
272 <div class="cs_files">
272 <div class="cs_files">
273 %if not c.files and not c.missing_commits:
273 %if not c.files and not c.missing_commits:
274 <span class="empty_data">${_('No files')}</span>
274 <span class="empty_data">${_('No files')}</span>
275 %endif
275 %endif
276 <table class="compare_view_files">
276 <table class="compare_view_files">
277 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
277 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
278 %for FID, change, path, stats in c.files:
278 %for FID, change, path, stats in c.files:
279 <tr class="cs_${change} collapse_file" fid="${FID}">
279 <tr class="cs_${change} collapse_file" fid="${FID}">
280 <td class="cs_icon_td">
280 <td class="cs_icon_td">
281 <span class="collapse_file_icon" fid="${FID}"></span>
281 <span class="collapse_file_icon" fid="${FID}"></span>
282 </td>
282 </td>
283 <td class="cs_icon_td">
283 <td class="cs_icon_td">
284 <div class="flag_status not_reviewed hidden"></div>
284 <div class="flag_status not_reviewed hidden"></div>
285 </td>
285 </td>
286 <td class="cs_${change}" id="a_${FID}">
286 <td class="cs_${change}" id="a_${FID}">
287 <div class="node">
287 <div class="node">
288 <a href="#a_${FID}">
288 <a href="#a_${FID}">
289 <i class="icon-file-${change.lower()}"></i>
289 <i class="icon-file-${change.lower()}"></i>
290 ${h.safe_unicode(path)}
290 ${h.safe_unicode(path)}
291 </a>
291 </a>
292 </div>
292 </div>
293 </td>
293 </td>
294 <td>
294 <td>
295 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
295 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
296 <div class="comment-bubble pull-right" data-path="${path}">
296 <div class="comment-bubble pull-right" data-path="${path}">
297 <i class="icon-comment"></i>
297 <i class="icon-comment"></i>
298 </div>
298 </div>
299 </td>
299 </td>
300 </tr>
300 </tr>
301 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
301 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
302 <td></td>
302 <td></td>
303 <td></td>
303 <td></td>
304 <td class="cs_${change}">
304 <td class="cs_${change}">
305 %if c.target_repo.repo_name == c.repo_name:
305 %if c.target_repo.repo_name == c.repo_name:
306 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
306 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
307 %else:
307 %else:
308 ## this is slightly different case later, since the other repo can have this
308 ## this is slightly different case later, since the other repo can have this
309 ## file in other state than the origin repo
309 ## file in other state than the origin repo
310 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
310 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
311 %endif
311 %endif
312 </td>
312 </td>
313 <td class="td-actions rc-form">
313 <td class="td-actions rc-form">
314 </td>
314 </td>
315 </tr>
315 </tr>
316 <tr id="tr_${FID}">
316 <tr id="tr_${FID}">
317 <td></td>
317 <td></td>
318 <td></td>
318 <td></td>
319 <td class="injected_diff" colspan="2">
319 <td class="injected_diff" colspan="2">
320 ${diff_block.diff_block_simple([c.changes[FID]])}
320 ${diff_block.diff_block_simple([c.changes[FID]])}
321 </td>
321 </td>
322 </tr>
322 </tr>
323
323
324 ## Loop through inline comments
324 ## Loop through inline comments
325 % if c.outdated_comments.get(path,False):
325 % if c.outdated_comments.get(path,False):
326 <tr class="outdated">
326 <tr class="outdated">
327 <td></td>
327 <td></td>
328 <td></td>
328 <td></td>
329 <td colspan="2">
329 <td colspan="2">
330 <p>${_('Outdated Inline Comments')}:</p>
330 <p>${_('Outdated Inline Comments')}:</p>
331 </td>
331 </td>
332 </tr>
332 </tr>
333 <tr class="outdated">
333 <tr class="outdated">
334 <td></td>
334 <td></td>
335 <td></td>
335 <td></td>
336 <td colspan="2" class="outdated_comment_block">
336 <td colspan="2" class="outdated_comment_block">
337 % for line, comments in c.outdated_comments[path].iteritems():
337 % for line, comments in c.outdated_comments[path].iteritems():
338 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
338 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
339 % for co in comments:
339 % for co in comments:
340 ${comment.comment_block_outdated(co)}
340 ${comment.comment_block_outdated(co)}
341 % endfor
341 % endfor
342 </div>
342 </div>
343 % endfor
343 % endfor
344 </td>
344 </td>
345 </tr>
345 </tr>
346 % endif
346 % endif
347 %endfor
347 %endfor
348 ## Loop through inline comments for deleted files
348 ## Loop through inline comments for deleted files
349 %for path in c.deleted_files:
349 %for path in c.deleted_files:
350 <tr class="outdated deleted">
350 <tr class="outdated deleted">
351 <td></td>
351 <td></td>
352 <td></td>
352 <td></td>
353 <td>${path}</td>
353 <td>${path}</td>
354 </tr>
354 </tr>
355 <tr class="outdated deleted">
355 <tr class="outdated deleted">
356 <td></td>
356 <td></td>
357 <td></td>
357 <td></td>
358 <td>(${_('Removed')})</td>
358 <td>(${_('Removed')})</td>
359 </tr>
359 </tr>
360 % if path in c.outdated_comments:
360 % if path in c.outdated_comments:
361 <tr class="outdated deleted">
361 <tr class="outdated deleted">
362 <td></td>
362 <td></td>
363 <td></td>
363 <td></td>
364 <td colspan="2">
364 <td colspan="2">
365 <p>${_('Outdated Inline Comments')}:</p>
365 <p>${_('Outdated Inline Comments')}:</p>
366 </td>
366 </td>
367 </tr>
367 </tr>
368 <tr class="outdated">
368 <tr class="outdated">
369 <td></td>
369 <td></td>
370 <td></td>
370 <td></td>
371 <td colspan="2" class="outdated_comment_block">
371 <td colspan="2" class="outdated_comment_block">
372 % for line, comments in c.outdated_comments[path].iteritems():
372 % for line, comments in c.outdated_comments[path].iteritems():
373 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
373 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
374 % for co in comments:
374 % for co in comments:
375 ${comment.comment_block_outdated(co)}
375 ${comment.comment_block_outdated(co)}
376 % endfor
376 % endfor
377 </div>
377 </div>
378 % endfor
378 % endfor
379 </td>
379 </td>
380 </tr>
380 </tr>
381 % endif
381 % endif
382 %endfor
382 %endfor
383 </table>
383 </table>
384 </div>
384 </div>
385 % if c.limited_diff:
385 % if c.limited_diff:
386 <h5>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></h5>
386 <h5>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></h5>
387 % endif
387 % endif
388 </div>
388 </div>
389 </div>
389 </div>
390
390
391 % if c.limited_diff:
391 % if c.limited_diff:
392 <p>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></p>
392 <p>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></p>
393 % endif
393 % endif
394
394
395 ## template for inline comment form
395 ## template for inline comment form
396 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
396 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
397 ${comment.comment_inline_form()}
397 ${comment.comment_inline_form()}
398
398
399 ## render comments and inlines
399 ## render comments and inlines
400 ${comment.generate_comments(include_pull_request=True, is_pull_request=True)}
400 ${comment.generate_comments(include_pull_request=True, is_pull_request=True)}
401
401
402 % if not c.pull_request.is_closed():
402 % if not c.pull_request.is_closed():
403 ## main comment form and it status
403 ## main comment form and it status
404 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
404 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
405 pull_request_id=c.pull_request.pull_request_id),
405 pull_request_id=c.pull_request.pull_request_id),
406 c.pull_request_review_status,
406 c.pull_request_review_status,
407 is_pull_request=True, change_status=c.allowed_to_change_status)}
407 is_pull_request=True, change_status=c.allowed_to_change_status)}
408 %endif
408 %endif
409
409
410 <script type="text/javascript">
410 <script type="text/javascript">
411 if (location.href.indexOf('#') != -1) {
411 if (location.href.indexOf('#') != -1) {
412 var id = '#'+location.href.substring(location.href.indexOf('#') + 1).split('#');
412 var id = '#'+location.href.substring(location.href.indexOf('#') + 1).split('#');
413 var line = $('html').find(id);
413 var line = $('html').find(id);
414 offsetScroll(line, 70);
414 offsetScroll(line, 70);
415 }
415 }
416 $(function(){
416 $(function(){
417 ReviewerAutoComplete('user');
417 ReviewerAutoComplete('user');
418 // custom code mirror
418 // custom code mirror
419 var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input');
419 var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input');
420
420
421 var PRDetails = {
421 var PRDetails = {
422 editButton: $('#open_edit_pullrequest'),
422 editButton: $('#open_edit_pullrequest'),
423 closeButton: $('#close_edit_pullrequest'),
423 closeButton: $('#close_edit_pullrequest'),
424 viewFields: $('#pr-desc, #pr-title'),
424 viewFields: $('#pr-desc, #pr-title'),
425 editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'),
425 editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'),
426
426
427 init: function() {
427 init: function() {
428 var that = this;
428 var that = this;
429 this.editButton.on('click', function(e) { that.edit(); });
429 this.editButton.on('click', function(e) { that.edit(); });
430 this.closeButton.on('click', function(e) { that.view(); });
430 this.closeButton.on('click', function(e) { that.view(); });
431 },
431 },
432
432
433 edit: function(event) {
433 edit: function(event) {
434 this.viewFields.hide();
434 this.viewFields.hide();
435 this.editButton.hide();
435 this.editButton.hide();
436 this.editFields.show();
436 this.editFields.show();
437 codeMirrorInstance.refresh();
437 codeMirrorInstance.refresh();
438 },
438 },
439
439
440 view: function(event) {
440 view: function(event) {
441 this.editFields.hide();
441 this.editFields.hide();
442 this.closeButton.hide();
442 this.closeButton.hide();
443 this.viewFields.show();
443 this.viewFields.show();
444 }
444 }
445 };
445 };
446
446
447 var ReviewersPanel = {
447 var ReviewersPanel = {
448 editButton: $('#open_edit_reviewers'),
448 editButton: $('#open_edit_reviewers'),
449 closeButton: $('#close_edit_reviewers'),
449 closeButton: $('#close_edit_reviewers'),
450 addButton: $('#add_reviewer_input'),
450 addButton: $('#add_reviewer_input'),
451 removeButtons: $('.reviewer_member_remove'),
451 removeButtons: $('.reviewer_member_remove'),
452
452
453 init: function() {
453 init: function() {
454 var that = this;
454 var that = this;
455 this.editButton.on('click', function(e) { that.edit(); });
455 this.editButton.on('click', function(e) { that.edit(); });
456 this.closeButton.on('click', function(e) { that.close(); });
456 this.closeButton.on('click', function(e) { that.close(); });
457 },
457 },
458
458
459 edit: function(event) {
459 edit: function(event) {
460 this.editButton.hide();
460 this.editButton.hide();
461 this.closeButton.show();
461 this.closeButton.show();
462 this.addButton.show();
462 this.addButton.show();
463 this.removeButtons.css('visibility', 'visible');
463 this.removeButtons.css('visibility', 'visible');
464 },
464 },
465
465
466 close: function(event) {
466 close: function(event) {
467 this.editButton.show();
467 this.editButton.show();
468 this.closeButton.hide();
468 this.closeButton.hide();
469 this.addButton.hide();
469 this.addButton.hide();
470 this.removeButtons.css('visibility', 'hidden');
470 this.removeButtons.css('visibility', 'hidden');
471 }
471 }
472 };
472 };
473
473
474 PRDetails.init();
474 PRDetails.init();
475 ReviewersPanel.init();
475 ReviewersPanel.init();
476
476
477 $('#show-outdated-comments').on('click', function(e){
477 $('#show-outdated-comments').on('click', function(e){
478 var button = $(this);
478 var button = $(this);
479 var outdated = $('.outdated');
479 var outdated = $('.outdated');
480 if (button.html() === "(Show)") {
480 if (button.html() === "(Show)") {
481 button.html("(Hide)");
481 button.html("(Hide)");
482 outdated.show();
482 outdated.show();
483 } else {
483 } else {
484 button.html("(Show)");
484 button.html("(Show)");
485 outdated.hide();
485 outdated.hide();
486 }
486 }
487 });
487 });
488
488
489 $('.show-inline-comments').on('change', function(e){
489 $('.show-inline-comments').on('change', function(e){
490 var show = 'none';
490 var show = 'none';
491 var target = e.currentTarget;
491 var target = e.currentTarget;
492 if(target.checked){
492 if(target.checked){
493 show = ''
493 show = ''
494 }
494 }
495 var boxid = $(target).attr('id_for');
495 var boxid = $(target).attr('id_for');
496 var comments = $('#{0} .inline-comments'.format(boxid));
496 var comments = $('#{0} .inline-comments'.format(boxid));
497 var fn_display = function(idx){
497 var fn_display = function(idx){
498 $(this).css('display', show);
498 $(this).css('display', show);
499 };
499 };
500 $(comments).each(fn_display);
500 $(comments).each(fn_display);
501 var btns = $('#{0} .inline-comments-button'.format(boxid));
501 var btns = $('#{0} .inline-comments-button'.format(boxid));
502 $(btns).each(fn_display);
502 $(btns).each(fn_display);
503 });
503 });
504
504
505 // inject comments into their proper positions
505 // inject comments into their proper positions
506 var file_comments = $('.inline-comment-placeholder');
506 var file_comments = $('.inline-comment-placeholder');
507 %if c.pull_request.is_closed():
507 %if c.pull_request.is_closed():
508 renderInlineComments(file_comments, false);
508 renderInlineComments(file_comments, false);
509 %else:
509 %else:
510 renderInlineComments(file_comments, true);
510 renderInlineComments(file_comments, true);
511 %endif
511 %endif
512 var commentTotals = {};
512 var commentTotals = {};
513 $.each(file_comments, function(i, comment) {
513 $.each(file_comments, function(i, comment) {
514 var path = $(comment).attr('path');
514 var path = $(comment).attr('path');
515 var comms = $(comment).children().length;
515 var comms = $(comment).children().length;
516 if (path in commentTotals) {
516 if (path in commentTotals) {
517 commentTotals[path] += comms;
517 commentTotals[path] += comms;
518 } else {
518 } else {
519 commentTotals[path] = comms;
519 commentTotals[path] = comms;
520 }
520 }
521 });
521 });
522 $.each(commentTotals, function(path, total) {
522 $.each(commentTotals, function(path, total) {
523 var elem = $('.comment-bubble[data-path="'+ path +'"]');
523 var elem = $('.comment-bubble[data-path="'+ path +'"]');
524 elem.css('visibility', 'visible');
524 elem.css('visibility', 'visible');
525 elem.html(elem.html() + ' ' + total );
525 elem.html(elem.html() + ' ' + total );
526 });
526 });
527
527
528 $('#merge_pull_request_form').submit(function() {
528 $('#merge_pull_request_form').submit(function() {
529 if (!$('#merge_pull_request').attr('disabled')) {
529 if (!$('#merge_pull_request').attr('disabled')) {
530 $('#merge_pull_request').attr('disabled', 'disabled');
530 $('#merge_pull_request').attr('disabled', 'disabled');
531 }
531 }
532 return true;
532 return true;
533 });
533 });
534
534
535 $('#edit_pull_request').on('click', function(e){
535 $('#edit_pull_request').on('click', function(e){
536 var title = $('#pr-title-input').val();
536 var title = $('#pr-title-input').val();
537 var description = codeMirrorInstance.getValue();
537 var description = codeMirrorInstance.getValue();
538 editPullRequest(
538 editPullRequest(
539 "${c.repo_name}", "${c.pull_request.pull_request_id}",
539 "${c.repo_name}", "${c.pull_request.pull_request_id}",
540 title, description);
540 title, description);
541 });
541 });
542
542
543 $('#update_pull_request').on('click', function(e){
543 $('#update_pull_request').on('click', function(e){
544 updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}");
544 updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}");
545 });
545 });
546
546
547 $('#update_commits').on('click', function(e){
547 $('#update_commits').on('click', function(e){
548 var isDisabled = !$(e.currentTarget).attr('disabled');
548 var isDisabled = !$(e.currentTarget).attr('disabled');
549 $(e.currentTarget).text(_TM['Updating...']);
549 $(e.currentTarget).text(_gettext('Updating...'));
550 $(e.currentTarget).attr('disabled', 'disabled');
550 $(e.currentTarget).attr('disabled', 'disabled');
551 if(isDisabled){
551 if(isDisabled){
552 updateCommits("${c.repo_name}", "${c.pull_request.pull_request_id}");
552 updateCommits("${c.repo_name}", "${c.pull_request.pull_request_id}");
553 }
553 }
554
554
555 });
555 });
556 // fixing issue with caches on firefox
556 // fixing issue with caches on firefox
557 $('#update_commits').removeAttr("disabled");
557 $('#update_commits').removeAttr("disabled");
558
558
559 $('#close_pull_request').on('click', function(e){
559 $('#close_pull_request').on('click', function(e){
560 closePullRequest("${c.repo_name}", "${c.pull_request.pull_request_id}");
560 closePullRequest("${c.repo_name}", "${c.pull_request.pull_request_id}");
561 });
561 });
562 })
562 })
563 </script>
563 </script>
564
564
565 </div>
565 </div>
566 </div>
566 </div>
567
567
568 </%def>
568 </%def>
@@ -1,39 +1,42 b''
1 [aliases]
1 [aliases]
2 test = pytest
2 test = pytest
3
3
4 [egg_info]
4 [egg_info]
5 tag_build =
5 tag_build =
6 tag_svn_revision = false
6 tag_svn_revision = false
7
7
8 # Babel configuration
8 # Babel configuration
9 [compile_catalog]
9 [compile_catalog]
10 domain = rhodecode
10 domain = rhodecode
11 directory = rhodecode/i18n
11 directory = rhodecode/i18n
12 statistics = true
12 statistics = true
13
13
14 [extract_messages]
14 [extract_messages]
15 add_comments = TRANSLATORS:
15 add_comments = TRANSLATORS:
16 output_file = rhodecode/i18n/rhodecode.pot
16 output_file = rhodecode/i18n/rhodecode.pot
17 msgid-bugs-address = marcin@rhodecode.com
17 msgid-bugs-address = marcin@rhodecode.com
18 copyright-holder = RhodeCode GmbH
18 copyright-holder = RhodeCode GmbH
19 no-wrap = true
19 no-wrap = true
20 keywords = lazy_ugettext
20 keywords =
21 lazy_ugettext
22 _ngettext
23 _gettext
21
24
22 [init_catalog]
25 [init_catalog]
23 domain = rhodecode
26 domain = rhodecode
24 input_file = rhodecode/i18n/rhodecode.pot
27 input_file = rhodecode/i18n/rhodecode.pot
25 output_dir = rhodecode/i18n
28 output_dir = rhodecode/i18n
26
29
27 [update_catalog]
30 [update_catalog]
28 domain = rhodecode
31 domain = rhodecode
29 input_file = rhodecode/i18n/rhodecode.pot
32 input_file = rhodecode/i18n/rhodecode.pot
30 output_dir = rhodecode/i18n
33 output_dir = rhodecode/i18n
31 previous = true
34 previous = true
32
35
33 [build_sphinx]
36 [build_sphinx]
34 source-dir = docs/
37 source-dir = docs/
35 build-dir = docs/_build
38 build-dir = docs/_build
36 all_files = 1
39 all_files = 1
37
40
38 [upload_sphinx]
41 [upload_sphinx]
39 upload-dir = docs/_build/html
42 upload-dir = docs/_build/html
@@ -1,248 +1,249 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Import early to make sure things are patched up properly
3 # Import early to make sure things are patched up properly
4 from setuptools import setup, find_packages
4 from setuptools import setup, find_packages
5
5
6 import os
6 import os
7 import sys
7 import sys
8 import platform
8 import platform
9
9
10 if sys.version_info < (2, 7):
10 if sys.version_info < (2, 7):
11 raise Exception('RhodeCode requires Python 2.7 or later')
11 raise Exception('RhodeCode requires Python 2.7 or later')
12
12
13
13
14 here = os.path.abspath(os.path.dirname(__file__))
14 here = os.path.abspath(os.path.dirname(__file__))
15
15
16
16
17 def _get_meta_var(name, data, callback_handler=None):
17 def _get_meta_var(name, data, callback_handler=None):
18 import re
18 import re
19 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
19 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
20 if matches:
20 if matches:
21 if not callable(callback_handler):
21 if not callable(callback_handler):
22 callback_handler = lambda v: v
22 callback_handler = lambda v: v
23
23
24 return callback_handler(eval(matches.groups()[0]))
24 return callback_handler(eval(matches.groups()[0]))
25
25
26 _meta = open(os.path.join(here, 'rhodecode', '__init__.py'), 'rb')
26 _meta = open(os.path.join(here, 'rhodecode', '__init__.py'), 'rb')
27 _metadata = _meta.read()
27 _metadata = _meta.read()
28 _meta.close()
28 _meta.close()
29
29
30 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
30 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
31 __version__ = open(os.path.join('rhodecode', 'VERSION')).read().strip()
31 __version__ = open(os.path.join('rhodecode', 'VERSION')).read().strip()
32 __license__ = _get_meta_var('__license__', _metadata)
32 __license__ = _get_meta_var('__license__', _metadata)
33 __author__ = _get_meta_var('__author__', _metadata)
33 __author__ = _get_meta_var('__author__', _metadata)
34 __url__ = _get_meta_var('__url__', _metadata)
34 __url__ = _get_meta_var('__url__', _metadata)
35 # defines current platform
35 # defines current platform
36 __platform__ = platform.system()
36 __platform__ = platform.system()
37
37
38 # Cygwin has different platform identifiers, but they all contain the
38 # Cygwin has different platform identifiers, but they all contain the
39 # term "CYGWIN"
39 # term "CYGWIN"
40 is_windows = __platform__ == 'Windows' or 'CYGWIN' in __platform__
40 is_windows = __platform__ == 'Windows' or 'CYGWIN' in __platform__
41
41
42 requirements = [
42 requirements = [
43 'Babel',
43 'Babel',
44 'Beaker',
44 'Beaker',
45 'FormEncode',
45 'FormEncode',
46 'Mako',
46 'Mako',
47 'Markdown',
47 'Markdown',
48 'MarkupSafe',
48 'MarkupSafe',
49 'MySQL-python',
49 'MySQL-python',
50 'Paste',
50 'Paste',
51 'PasteDeploy',
51 'PasteDeploy',
52 'PasteScript',
52 'PasteScript',
53 'Pygments',
53 'Pygments',
54 'Pylons',
54 'Pylons',
55 'Pyro4',
55 'Pyro4',
56 'Routes',
56 'Routes',
57 'SQLAlchemy',
57 'SQLAlchemy',
58 'Tempita',
58 'Tempita',
59 'URLObject',
59 'URLObject',
60 'WebError',
60 'WebError',
61 'WebHelpers',
61 'WebHelpers',
62 'WebHelpers2',
62 'WebHelpers2',
63 'WebOb',
63 'WebOb',
64 'WebTest',
64 'WebTest',
65 'Whoosh',
65 'Whoosh',
66 'alembic',
66 'alembic',
67 'amqplib',
67 'amqplib',
68 'anyjson',
68 'anyjson',
69 'appenlight-client',
69 'appenlight-client',
70 'authomatic',
70 'authomatic',
71 'backport_ipaddress',
71 'backport_ipaddress',
72 'celery',
72 'celery',
73 'colander',
73 'colander',
74 'decorator',
74 'decorator',
75 'docutils',
75 'docutils',
76 'gunicorn',
76 'gunicorn',
77 'infrae.cache',
77 'infrae.cache',
78 'ipython',
78 'ipython',
79 'iso8601',
79 'iso8601',
80 'kombu',
80 'kombu',
81 'msgpack-python',
81 'msgpack-python',
82 'packaging',
82 'packaging',
83 'psycopg2',
83 'psycopg2',
84 'py-gfm',
84 'py-gfm',
85 'pycrypto',
85 'pycrypto',
86 'pycurl',
86 'pycurl',
87 'pyparsing',
87 'pyparsing',
88 'pyramid',
88 'pyramid',
89 'pyramid-debugtoolbar',
89 'pyramid-debugtoolbar',
90 'pyramid-mako',
90 'pyramid-mako',
91 'pyramid-beaker',
91 'pyramid-beaker',
92 'pysqlite',
92 'pysqlite',
93 'python-dateutil',
93 'python-dateutil',
94 'python-ldap',
94 'python-ldap',
95 'python-memcached',
95 'python-memcached',
96 'python-pam',
96 'python-pam',
97 'recaptcha-client',
97 'recaptcha-client',
98 'repoze.lru',
98 'repoze.lru',
99 'requests',
99 'requests',
100 'simplejson',
100 'simplejson',
101 'waitress',
101 'waitress',
102 'zope.cachedescriptors',
102 'zope.cachedescriptors',
103 'dogpile.cache',
103 'dogpile.cache',
104 'dogpile.core'
104 'dogpile.core'
105 ]
105 ]
106
106
107 if is_windows:
107 if is_windows:
108 pass
108 pass
109 else:
109 else:
110 requirements.append('psutil')
110 requirements.append('psutil')
111 requirements.append('py-bcrypt')
111 requirements.append('py-bcrypt')
112
112
113 test_requirements = [
113 test_requirements = [
114 'WebTest',
114 'WebTest',
115 'configobj',
115 'configobj',
116 'cssselect',
116 'cssselect',
117 'flake8',
117 'flake8',
118 'lxml',
118 'lxml',
119 'mock',
119 'mock',
120 'pytest',
120 'pytest',
121 'pytest-cov',
121 'pytest-cov',
122 'pytest-runner',
122 'pytest-runner',
123 ]
123 ]
124
124
125 setup_requirements = [
125 setup_requirements = [
126 'PasteScript',
126 'PasteScript',
127 'pytest-runner',
127 'pytest-runner',
128 ]
128 ]
129
129
130 dependency_links = [
130 dependency_links = [
131 ]
131 ]
132
132
133 classifiers = [
133 classifiers = [
134 'Development Status :: 6 - Mature',
134 'Development Status :: 6 - Mature',
135 'Environment :: Web Environment',
135 'Environment :: Web Environment',
136 'Framework :: Pylons',
136 'Framework :: Pylons',
137 'Intended Audience :: Developers',
137 'Intended Audience :: Developers',
138 'Operating System :: OS Independent',
138 'Operating System :: OS Independent',
139 'Programming Language :: Python',
139 'Programming Language :: Python',
140 'Programming Language :: Python :: 2.7',
140 'Programming Language :: Python :: 2.7',
141 ]
141 ]
142
142
143
143
144 # additional files from project that goes somewhere in the filesystem
144 # additional files from project that goes somewhere in the filesystem
145 # relative to sys.prefix
145 # relative to sys.prefix
146 data_files = []
146 data_files = []
147
147
148 # additional files that goes into package itself
148 # additional files that goes into package itself
149 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
149 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
150
150
151 description = ('RhodeCode is a fast and powerful management tool '
151 description = ('RhodeCode is a fast and powerful management tool '
152 'for Mercurial and GIT with a built in push/pull server, '
152 'for Mercurial and GIT with a built in push/pull server, '
153 'full text search and code-review.')
153 'full text search and code-review.')
154
154
155 keywords = ' '.join([
155 keywords = ' '.join([
156 'rhodecode', 'rhodiumcode', 'mercurial', 'git', 'code review',
156 'rhodecode', 'rhodiumcode', 'mercurial', 'git', 'code review',
157 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
157 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
158 'hgwebdir', 'gitweb replacement', 'serving hgweb',
158 'hgwebdir', 'gitweb replacement', 'serving hgweb',
159 ])
159 ])
160
160
161 # long description
161 # long description
162 README_FILE = 'README.rst'
162 README_FILE = 'README.rst'
163 CHANGELOG_FILE = 'CHANGES.rst'
163 CHANGELOG_FILE = 'CHANGES.rst'
164 try:
164 try:
165 long_description = open(README_FILE).read() + '\n\n' + \
165 long_description = open(README_FILE).read() + '\n\n' + \
166 open(CHANGELOG_FILE).read()
166 open(CHANGELOG_FILE).read()
167
167
168 except IOError, err:
168 except IOError, err:
169 sys.stderr.write(
169 sys.stderr.write(
170 '[WARNING] Cannot find file specified as long_description (%s)\n or '
170 '[WARNING] Cannot find file specified as long_description (%s)\n or '
171 'changelog (%s) skipping that file' % (README_FILE, CHANGELOG_FILE)
171 'changelog (%s) skipping that file' % (README_FILE, CHANGELOG_FILE)
172 )
172 )
173 long_description = description
173 long_description = description
174
174
175 # packages
175 # packages
176 packages = find_packages()
176 packages = find_packages()
177
177
178 paster_commands = [
178 paster_commands = [
179 'make-config=rhodecode.lib.paster_commands.make_config:Command',
179 'make-config=rhodecode.lib.paster_commands.make_config:Command',
180 'setup-rhodecode=rhodecode.lib.paster_commands.setup_rhodecode:Command',
180 'setup-rhodecode=rhodecode.lib.paster_commands.setup_rhodecode:Command',
181 'update-repoinfo=rhodecode.lib.paster_commands.update_repoinfo:Command',
181 'update-repoinfo=rhodecode.lib.paster_commands.update_repoinfo:Command',
182 'cache-keys=rhodecode.lib.paster_commands.cache_keys:Command',
182 'cache-keys=rhodecode.lib.paster_commands.cache_keys:Command',
183 'ishell=rhodecode.lib.paster_commands.ishell:Command',
183 'ishell=rhodecode.lib.paster_commands.ishell:Command',
184 'upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb',
184 'upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb',
185 'celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand',
185 'celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand',
186 ]
186 ]
187
187
188 setup(
188 setup(
189 name='rhodecode-enterprise-ce',
189 name='rhodecode-enterprise-ce',
190 version=__version__,
190 version=__version__,
191 description=description,
191 description=description,
192 long_description=long_description,
192 long_description=long_description,
193 keywords=keywords,
193 keywords=keywords,
194 license=__license__,
194 license=__license__,
195 author=__author__,
195 author=__author__,
196 author_email='marcin@rhodecode.com',
196 author_email='marcin@rhodecode.com',
197 dependency_links=dependency_links,
197 dependency_links=dependency_links,
198 url=__url__,
198 url=__url__,
199 install_requires=requirements,
199 install_requires=requirements,
200 tests_require=test_requirements,
200 tests_require=test_requirements,
201 classifiers=classifiers,
201 classifiers=classifiers,
202 setup_requires=setup_requirements,
202 setup_requires=setup_requirements,
203 data_files=data_files,
203 data_files=data_files,
204 packages=packages,
204 packages=packages,
205 include_package_data=True,
205 include_package_data=True,
206 package_data=package_data,
206 package_data=package_data,
207 message_extractors={
207 message_extractors={
208 'rhodecode': [
208 'rhodecode': [
209 ('**.py', 'python', None),
209 ('**.py', 'python', None),
210 ('**.js', 'javascript', None),
210 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
211 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
211 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
212 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
212 ('public/**', 'ignore', None),
213 ('public/**', 'ignore', None),
213 ]
214 ]
214 },
215 },
215 zip_safe=False,
216 zip_safe=False,
216 paster_plugins=['PasteScript', 'Pylons'],
217 paster_plugins=['PasteScript', 'Pylons'],
217 entry_points={
218 entry_points={
218 'enterprise.plugins1': [
219 'enterprise.plugins1': [
219 'crowd=rhodecode.authentication.plugins.auth_crowd:plugin_factory',
220 'crowd=rhodecode.authentication.plugins.auth_crowd:plugin_factory',
220 'headers=rhodecode.authentication.plugins.auth_headers:plugin_factory',
221 'headers=rhodecode.authentication.plugins.auth_headers:plugin_factory',
221 'jasig_cas=rhodecode.authentication.plugins.auth_jasig_cas:plugin_factory',
222 'jasig_cas=rhodecode.authentication.plugins.auth_jasig_cas:plugin_factory',
222 'ldap=rhodecode.authentication.plugins.auth_ldap:plugin_factory',
223 'ldap=rhodecode.authentication.plugins.auth_ldap:plugin_factory',
223 'pam=rhodecode.authentication.plugins.auth_pam:plugin_factory',
224 'pam=rhodecode.authentication.plugins.auth_pam:plugin_factory',
224 'rhodecode=rhodecode.authentication.plugins.auth_rhodecode:plugin_factory',
225 'rhodecode=rhodecode.authentication.plugins.auth_rhodecode:plugin_factory',
225 'token=rhodecode.authentication.plugins.auth_token:plugin_factory',
226 'token=rhodecode.authentication.plugins.auth_token:plugin_factory',
226 ],
227 ],
227 'paste.app_factory': [
228 'paste.app_factory': [
228 'main=rhodecode.config.middleware:make_pyramid_app',
229 'main=rhodecode.config.middleware:make_pyramid_app',
229 'pylons=rhodecode.config.middleware:make_app',
230 'pylons=rhodecode.config.middleware:make_app',
230 ],
231 ],
231 'paste.app_install': [
232 'paste.app_install': [
232 'main=pylons.util:PylonsInstaller',
233 'main=pylons.util:PylonsInstaller',
233 'pylons=pylons.util:PylonsInstaller',
234 'pylons=pylons.util:PylonsInstaller',
234 ],
235 ],
235 'paste.global_paster_command': paster_commands,
236 'paste.global_paster_command': paster_commands,
236 'pytest11': [
237 'pytest11': [
237 'pylons=rhodecode.tests.pylons_plugin',
238 'pylons=rhodecode.tests.pylons_plugin',
238 'enterprise=rhodecode.tests.plugin',
239 'enterprise=rhodecode.tests.plugin',
239 ],
240 ],
240 'console_scripts': [
241 'console_scripts': [
241 'rcserver=rhodecode.rcserver:main',
242 'rcserver=rhodecode.rcserver:main',
242 ],
243 ],
243 'beaker.backends': [
244 'beaker.backends': [
244 'memorylru_base=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerBase',
245 'memorylru_base=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerBase',
245 'memorylru_debug=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerDebug'
246 'memorylru_debug=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerDebug'
246 ]
247 ]
247 },
248 },
248 )
249 )
General Comments 0
You need to be logged in to leave comments. Login now