##// END OF EJS Templates
pull requests: abort pending ajax requests before starting new one...
Mads Kiilerich -
r4556:dbd2f289 default
parent child Browse files
Show More
@@ -1,2173 +1,2175 b''
1 /**
1 /**
2 Kallithea JS Files
2 Kallithea JS Files
3 **/
3 **/
4 'use strict';
4 'use strict';
5
5
6 if (typeof console == "undefined" || typeof console.log == "undefined"){
6 if (typeof console == "undefined" || typeof console.log == "undefined"){
7 console = { log: function() {} }
7 console = { log: function() {} }
8 }
8 }
9
9
10 /**
10 /**
11 * INJECT .format function into String
11 * INJECT .format function into String
12 * Usage: "My name is {0} {1}".format("Johny","Bravo")
12 * Usage: "My name is {0} {1}".format("Johny","Bravo")
13 * Return "My name is Johny Bravo"
13 * Return "My name is Johny Bravo"
14 * Inspired by https://gist.github.com/1049426
14 * Inspired by https://gist.github.com/1049426
15 */
15 */
16 String.prototype.format = function() {
16 String.prototype.format = function() {
17 function format() {
17 function format() {
18 var str = this;
18 var str = this;
19 var len = arguments.length+1;
19 var len = arguments.length+1;
20 var safe = undefined;
20 var safe = undefined;
21 var arg = undefined;
21 var arg = undefined;
22
22
23 // For each {0} {1} {n...} replace with the argument in that position. If
23 // For each {0} {1} {n...} replace with the argument in that position. If
24 // the argument is an object or an array it will be stringified to JSON.
24 // the argument is an object or an array it will be stringified to JSON.
25 for (var i=0; i < len; arg = arguments[i++]) {
25 for (var i=0; i < len; arg = arguments[i++]) {
26 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
26 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
27 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
27 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
28 }
28 }
29 return str;
29 return str;
30 }
30 }
31
31
32 // Save a reference of what may already exist under the property native.
32 // Save a reference of what may already exist under the property native.
33 // Allows for doing something like: if("".format.native) { /* use native */ }
33 // Allows for doing something like: if("".format.native) { /* use native */ }
34 format.native = String.prototype.format;
34 format.native = String.prototype.format;
35
35
36 // Replace the prototype property
36 // Replace the prototype property
37 return format;
37 return format;
38
38
39 }();
39 }();
40
40
41 String.prototype.strip = function(char) {
41 String.prototype.strip = function(char) {
42 if(char === undefined){
42 if(char === undefined){
43 char = '\\s';
43 char = '\\s';
44 }
44 }
45 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
45 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
46 }
46 }
47
47
48 String.prototype.lstrip = function(char) {
48 String.prototype.lstrip = function(char) {
49 if(char === undefined){
49 if(char === undefined){
50 char = '\\s';
50 char = '\\s';
51 }
51 }
52 return this.replace(new RegExp('^'+char+'+'),'');
52 return this.replace(new RegExp('^'+char+'+'),'');
53 }
53 }
54
54
55 String.prototype.rstrip = function(char) {
55 String.prototype.rstrip = function(char) {
56 if(char === undefined){
56 if(char === undefined){
57 char = '\\s';
57 char = '\\s';
58 }
58 }
59 return this.replace(new RegExp(''+char+'+$'),'');
59 return this.replace(new RegExp(''+char+'+$'),'');
60 }
60 }
61
61
62 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
62 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
63 under MIT license / public domain, see
63 under MIT license / public domain, see
64 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
64 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
65 if(!Array.prototype.indexOf) {
65 if(!Array.prototype.indexOf) {
66 Array.prototype.indexOf = function (searchElement, fromIndex) {
66 Array.prototype.indexOf = function (searchElement, fromIndex) {
67 if ( this === undefined || this === null ) {
67 if ( this === undefined || this === null ) {
68 throw new TypeError( '"this" is null or not defined' );
68 throw new TypeError( '"this" is null or not defined' );
69 }
69 }
70
70
71 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
71 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
72
72
73 fromIndex = +fromIndex || 0;
73 fromIndex = +fromIndex || 0;
74
74
75 if (Math.abs(fromIndex) === Infinity) {
75 if (Math.abs(fromIndex) === Infinity) {
76 fromIndex = 0;
76 fromIndex = 0;
77 }
77 }
78
78
79 if (fromIndex < 0) {
79 if (fromIndex < 0) {
80 fromIndex += length;
80 fromIndex += length;
81 if (fromIndex < 0) {
81 if (fromIndex < 0) {
82 fromIndex = 0;
82 fromIndex = 0;
83 }
83 }
84 }
84 }
85
85
86 for (;fromIndex < length; fromIndex++) {
86 for (;fromIndex < length; fromIndex++) {
87 if (this[fromIndex] === searchElement) {
87 if (this[fromIndex] === searchElement) {
88 return fromIndex;
88 return fromIndex;
89 }
89 }
90 }
90 }
91
91
92 return -1;
92 return -1;
93 };
93 };
94 }
94 }
95
95
96 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
96 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
97 under MIT license / public domain, see
97 under MIT license / public domain, see
98 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
98 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
99 if (!Array.prototype.filter)
99 if (!Array.prototype.filter)
100 {
100 {
101 Array.prototype.filter = function(fun /*, thisArg */)
101 Array.prototype.filter = function(fun /*, thisArg */)
102 {
102 {
103 if (this === void 0 || this === null)
103 if (this === void 0 || this === null)
104 throw new TypeError();
104 throw new TypeError();
105
105
106 var t = Object(this);
106 var t = Object(this);
107 var len = t.length >>> 0;
107 var len = t.length >>> 0;
108 if (typeof fun !== "function")
108 if (typeof fun !== "function")
109 throw new TypeError();
109 throw new TypeError();
110
110
111 var res = [];
111 var res = [];
112 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
112 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
113 for (var i = 0; i < len; i++)
113 for (var i = 0; i < len; i++)
114 {
114 {
115 if (i in t)
115 if (i in t)
116 {
116 {
117 var val = t[i];
117 var val = t[i];
118
118
119 // NOTE: Technically this should Object.defineProperty at
119 // NOTE: Technically this should Object.defineProperty at
120 // the next index, as push can be affected by
120 // the next index, as push can be affected by
121 // properties on Object.prototype and Array.prototype.
121 // properties on Object.prototype and Array.prototype.
122 // But that method's new, and collisions should be
122 // But that method's new, and collisions should be
123 // rare, so use the more-compatible alternative.
123 // rare, so use the more-compatible alternative.
124 if (fun.call(thisArg, val, i, t))
124 if (fun.call(thisArg, val, i, t))
125 res.push(val);
125 res.push(val);
126 }
126 }
127 }
127 }
128
128
129 return res;
129 return res;
130 };
130 };
131 }
131 }
132
132
133 /**
133 /**
134 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
134 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
135 * which is copyright Stephane Klein and was made available under the BSD License.
135 * which is copyright Stephane Klein and was made available under the BSD License.
136 *
136 *
137 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
137 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
138 */
138 */
139 var pyroutes = (function() {
139 var pyroutes = (function() {
140 // access global map defined in special file pyroutes
140 // access global map defined in special file pyroutes
141 var matchlist = PROUTES_MAP;
141 var matchlist = PROUTES_MAP;
142 var sprintf = (function() {
142 var sprintf = (function() {
143 function get_type(variable) {
143 function get_type(variable) {
144 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
144 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
145 }
145 }
146 function str_repeat(input, multiplier) {
146 function str_repeat(input, multiplier) {
147 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
147 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
148 return output.join('');
148 return output.join('');
149 }
149 }
150
150
151 var str_format = function() {
151 var str_format = function() {
152 if (!str_format.cache.hasOwnProperty(arguments[0])) {
152 if (!str_format.cache.hasOwnProperty(arguments[0])) {
153 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
153 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
154 }
154 }
155 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
155 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
156 };
156 };
157
157
158 str_format.format = function(parse_tree, argv) {
158 str_format.format = function(parse_tree, argv) {
159 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
159 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
160 for (i = 0; i < tree_length; i++) {
160 for (i = 0; i < tree_length; i++) {
161 node_type = get_type(parse_tree[i]);
161 node_type = get_type(parse_tree[i]);
162 if (node_type === 'string') {
162 if (node_type === 'string') {
163 output.push(parse_tree[i]);
163 output.push(parse_tree[i]);
164 }
164 }
165 else if (node_type === 'array') {
165 else if (node_type === 'array') {
166 match = parse_tree[i]; // convenience purposes only
166 match = parse_tree[i]; // convenience purposes only
167 if (match[2]) { // keyword argument
167 if (match[2]) { // keyword argument
168 arg = argv[cursor];
168 arg = argv[cursor];
169 for (k = 0; k < match[2].length; k++) {
169 for (k = 0; k < match[2].length; k++) {
170 if (!arg.hasOwnProperty(match[2][k])) {
170 if (!arg.hasOwnProperty(match[2][k])) {
171 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
171 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
172 }
172 }
173 arg = arg[match[2][k]];
173 arg = arg[match[2][k]];
174 }
174 }
175 }
175 }
176 else if (match[1]) { // positional argument (explicit)
176 else if (match[1]) { // positional argument (explicit)
177 arg = argv[match[1]];
177 arg = argv[match[1]];
178 }
178 }
179 else { // positional argument (implicit)
179 else { // positional argument (implicit)
180 arg = argv[cursor++];
180 arg = argv[cursor++];
181 }
181 }
182
182
183 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
183 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
184 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
184 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
185 }
185 }
186 switch (match[8]) {
186 switch (match[8]) {
187 case 'b': arg = arg.toString(2); break;
187 case 'b': arg = arg.toString(2); break;
188 case 'c': arg = String.fromCharCode(arg); break;
188 case 'c': arg = String.fromCharCode(arg); break;
189 case 'd': arg = parseInt(arg, 10); break;
189 case 'd': arg = parseInt(arg, 10); break;
190 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
190 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
191 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
191 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
192 case 'o': arg = arg.toString(8); break;
192 case 'o': arg = arg.toString(8); break;
193 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
193 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
194 case 'u': arg = Math.abs(arg); break;
194 case 'u': arg = Math.abs(arg); break;
195 case 'x': arg = arg.toString(16); break;
195 case 'x': arg = arg.toString(16); break;
196 case 'X': arg = arg.toString(16).toUpperCase(); break;
196 case 'X': arg = arg.toString(16).toUpperCase(); break;
197 }
197 }
198 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
198 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
199 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
199 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
200 pad_length = match[6] - String(arg).length;
200 pad_length = match[6] - String(arg).length;
201 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
201 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
202 output.push(match[5] ? arg + pad : pad + arg);
202 output.push(match[5] ? arg + pad : pad + arg);
203 }
203 }
204 }
204 }
205 return output.join('');
205 return output.join('');
206 };
206 };
207
207
208 str_format.cache = {};
208 str_format.cache = {};
209
209
210 str_format.parse = function(fmt) {
210 str_format.parse = function(fmt) {
211 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
211 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
212 while (_fmt) {
212 while (_fmt) {
213 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
213 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
214 parse_tree.push(match[0]);
214 parse_tree.push(match[0]);
215 }
215 }
216 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
216 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
217 parse_tree.push('%');
217 parse_tree.push('%');
218 }
218 }
219 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
219 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
220 if (match[2]) {
220 if (match[2]) {
221 arg_names |= 1;
221 arg_names |= 1;
222 var field_list = [], replacement_field = match[2], field_match = [];
222 var field_list = [], replacement_field = match[2], field_match = [];
223 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
223 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
224 field_list.push(field_match[1]);
224 field_list.push(field_match[1]);
225 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
225 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
226 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
226 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
227 field_list.push(field_match[1]);
227 field_list.push(field_match[1]);
228 }
228 }
229 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
229 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
230 field_list.push(field_match[1]);
230 field_list.push(field_match[1]);
231 }
231 }
232 else {
232 else {
233 throw('[sprintf] huh?');
233 throw('[sprintf] huh?');
234 }
234 }
235 }
235 }
236 }
236 }
237 else {
237 else {
238 throw('[sprintf] huh?');
238 throw('[sprintf] huh?');
239 }
239 }
240 match[2] = field_list;
240 match[2] = field_list;
241 }
241 }
242 else {
242 else {
243 arg_names |= 2;
243 arg_names |= 2;
244 }
244 }
245 if (arg_names === 3) {
245 if (arg_names === 3) {
246 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
246 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
247 }
247 }
248 parse_tree.push(match);
248 parse_tree.push(match);
249 }
249 }
250 else {
250 else {
251 throw('[sprintf] huh?');
251 throw('[sprintf] huh?');
252 }
252 }
253 _fmt = _fmt.substring(match[0].length);
253 _fmt = _fmt.substring(match[0].length);
254 }
254 }
255 return parse_tree;
255 return parse_tree;
256 };
256 };
257
257
258 return str_format;
258 return str_format;
259 })();
259 })();
260
260
261 var vsprintf = function(fmt, argv) {
261 var vsprintf = function(fmt, argv) {
262 argv.unshift(fmt);
262 argv.unshift(fmt);
263 return sprintf.apply(null, argv);
263 return sprintf.apply(null, argv);
264 };
264 };
265 return {
265 return {
266 'url': function(route_name, params) {
266 'url': function(route_name, params) {
267 var result = route_name;
267 var result = route_name;
268 if (typeof(params) != 'object'){
268 if (typeof(params) != 'object'){
269 params = {};
269 params = {};
270 }
270 }
271 if (matchlist.hasOwnProperty(route_name)) {
271 if (matchlist.hasOwnProperty(route_name)) {
272 var route = matchlist[route_name];
272 var route = matchlist[route_name];
273 // param substitution
273 // param substitution
274 for(var i=0; i < route[1].length; i++) {
274 for(var i=0; i < route[1].length; i++) {
275 if (!params.hasOwnProperty(route[1][i]))
275 if (!params.hasOwnProperty(route[1][i]))
276 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
276 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
277 }
277 }
278 result = sprintf(route[0], params);
278 result = sprintf(route[0], params);
279
279
280 var ret = [];
280 var ret = [];
281 //extra params => GET
281 //extra params => GET
282 for(var param in params){
282 for(var param in params){
283 if (route[1].indexOf(param) == -1){
283 if (route[1].indexOf(param) == -1){
284 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
284 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
285 }
285 }
286 }
286 }
287 var _parts = ret.join("&");
287 var _parts = ret.join("&");
288 if(_parts){
288 if(_parts){
289 result = result +'?'+ _parts
289 result = result +'?'+ _parts
290 }
290 }
291 }
291 }
292
292
293 return result;
293 return result;
294 },
294 },
295 'register': function(route_name, route_tmpl, req_params) {
295 'register': function(route_name, route_tmpl, req_params) {
296 if (typeof(req_params) != 'object') {
296 if (typeof(req_params) != 'object') {
297 req_params = [];
297 req_params = [];
298 }
298 }
299 var keys = [];
299 var keys = [];
300 for (var i=0; i < req_params.length; i++) {
300 for (var i=0; i < req_params.length; i++) {
301 keys.push(req_params[i])
301 keys.push(req_params[i])
302 }
302 }
303 matchlist[route_name] = [
303 matchlist[route_name] = [
304 unescape(route_tmpl),
304 unescape(route_tmpl),
305 keys
305 keys
306 ]
306 ]
307 },
307 },
308 '_routes': function(){
308 '_routes': function(){
309 return matchlist;
309 return matchlist;
310 }
310 }
311 }
311 }
312 })();
312 })();
313
313
314
314
315 /**
315 /**
316 * GLOBAL YUI Shortcuts
316 * GLOBAL YUI Shortcuts
317 */
317 */
318 var YUC = YAHOO.util.Connect;
318 var YUC = YAHOO.util.Connect;
319 var YUD = YAHOO.util.Dom;
319 var YUD = YAHOO.util.Dom;
320 var YUE = YAHOO.util.Event;
320 var YUE = YAHOO.util.Event;
321 var YUQ = YAHOO.util.Selector.query;
321 var YUQ = YAHOO.util.Selector.query;
322
322
323 /* Invoke all functions in callbacks */
323 /* Invoke all functions in callbacks */
324 var _run_callbacks = function(callbacks){
324 var _run_callbacks = function(callbacks){
325 if (callbacks !== undefined){
325 if (callbacks !== undefined){
326 var _l = callbacks.length;
326 var _l = callbacks.length;
327 for (var i=0;i<_l;i++){
327 for (var i=0;i<_l;i++){
328 var func = callbacks[i];
328 var func = callbacks[i];
329 if(typeof(func)=='function'){
329 if(typeof(func)=='function'){
330 try{
330 try{
331 func();
331 func();
332 }catch (err){};
332 }catch (err){};
333 }
333 }
334 }
334 }
335 }
335 }
336 }
336 }
337
337
338 /**
338 /**
339 * turns objects into GET query string
339 * turns objects into GET query string
340 */
340 */
341 var _toQueryString = function(o) {
341 var _toQueryString = function(o) {
342 if(typeof o !== 'object') {
342 if(typeof o !== 'object') {
343 return false;
343 return false;
344 }
344 }
345 var _p, _qs = [];
345 var _p, _qs = [];
346 for(_p in o) {
346 for(_p in o) {
347 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
347 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
348 }
348 }
349 return _qs.join('&');
349 return _qs.join('&');
350 };
350 };
351
351
352 /**
352 /**
353 * Load HTML into DOM using Ajax
353 * Load HTML into DOM using Ajax
354 *
354 *
355 * @param $target: load html async and place it (or an error message) here
355 * @param $target: load html async and place it (or an error message) here
356 * @param success: success callback function
356 * @param success: success callback function
357 * @param args: query parameters to pass to url
357 * @param args: query parameters to pass to url
358 */
358 */
359 function asynchtml(url, $target, success, args){
359 function asynchtml(url, $target, success, args){
360 if(args===undefined){
360 if(args===undefined){
361 args=null;
361 args=null;
362 }
362 }
363 $target.html(_TM['Loading ...']).css('opacity','0.3');
363 $target.html(_TM['Loading ...']).css('opacity','0.3');
364
364
365 $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
365 return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
366 .done(function(html) {
366 .done(function(html) {
367 $target.html(html);
367 $target.html(html);
368 $target.css('opacity','1.0');
368 $target.css('opacity','1.0');
369 //execute the given original callback
369 //execute the given original callback
370 if (success !== undefined && success) {
370 if (success !== undefined && success) {
371 success();
371 success();
372 }
372 }
373 })
373 })
374 .fail(function(jqXHR, textStatus, errorThrown) {
374 .fail(function(jqXHR, textStatus, errorThrown) {
375 if (textStatus == "abort")
376 return;
375 console.log('Ajax failure: ' + textStatus);
377 console.log('Ajax failure: ' + textStatus);
376 $target.html('<span class="error_red">ERROR: {0}</span>'.format(textStatus));
378 $target.html('<span class="error_red">ERROR: {0}</span>'.format(textStatus));
377 $target.css('opacity','1.0');
379 $target.css('opacity','1.0');
378 })
380 })
379 ;
381 ;
380 };
382 };
381
383
382 var ajaxGET = function(url,success) {
384 var ajaxGET = function(url,success) {
383 // Set special header for ajax == HTTP_X_PARTIAL_XHR
385 // Set special header for ajax == HTTP_X_PARTIAL_XHR
384 YUC.initHeader('X-PARTIAL-XHR',true);
386 YUC.initHeader('X-PARTIAL-XHR',true);
385
387
386 var sUrl = url;
388 var sUrl = url;
387 var callback = {
389 var callback = {
388 success: success,
390 success: success,
389 failure: function (o) {
391 failure: function (o) {
390 if (o.status != 0) {
392 if (o.status != 0) {
391 alert("Ajax GET error: " + o.statusText);
393 alert("Ajax GET error: " + o.statusText);
392 };
394 };
393 }
395 }
394 };
396 };
395
397
396 var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
398 var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
397 return request;
399 return request;
398 };
400 };
399
401
400 var ajaxPOST = function(url,postData,success) {
402 var ajaxPOST = function(url,postData,success) {
401 // Set special header for ajax == HTTP_X_PARTIAL_XHR
403 // Set special header for ajax == HTTP_X_PARTIAL_XHR
402 YUC.initHeader('X-PARTIAL-XHR',true);
404 YUC.initHeader('X-PARTIAL-XHR',true);
403
405
404 var sUrl = url;
406 var sUrl = url;
405 var callback = {
407 var callback = {
406 success: success,
408 success: success,
407 failure: function (o) {
409 failure: function (o) {
408 alert("Ajax POST error: " + o.statusText);
410 alert("Ajax POST error: " + o.statusText);
409 }
411 }
410 };
412 };
411 var postData = _toQueryString(postData);
413 var postData = _toQueryString(postData);
412 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
414 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
413 return request;
415 return request;
414 };
416 };
415
417
416
418
417 /**
419 /**
418 * activate .show_more links
420 * activate .show_more links
419 * the .show_more must have an id that is the the id of an element to hide prefixed with _
421 * the .show_more must have an id that is the the id of an element to hide prefixed with _
420 * the parentnode will be displayed
422 * the parentnode will be displayed
421 */
423 */
422 var show_more_event = function(){
424 var show_more_event = function(){
423 $('.show_more').click(function(e){
425 $('.show_more').click(function(e){
424 var el = e.currentTarget;
426 var el = e.currentTarget;
425 $('#' + el.id.substring(1)).hide();
427 $('#' + el.id.substring(1)).hide();
426 $(el.parentNode).show();
428 $(el.parentNode).show();
427 });
429 });
428 };
430 };
429
431
430 /**
432 /**
431 * activate .lazy-cs mouseover for showing changeset tooltip
433 * activate .lazy-cs mouseover for showing changeset tooltip
432 */
434 */
433 var show_changeset_tooltip = function(){
435 var show_changeset_tooltip = function(){
434 $('.lazy-cs').mouseover(function(e){
436 $('.lazy-cs').mouseover(function(e){
435 var $target = $(e.currentTarget);
437 var $target = $(e.currentTarget);
436 var rid = $target.attr('raw_id');
438 var rid = $target.attr('raw_id');
437 var repo_name = $target.attr('repo_name');
439 var repo_name = $target.attr('repo_name');
438 if(rid && !$target.hasClass('tooltip')){
440 if(rid && !$target.hasClass('tooltip')){
439 _show_tooltip(e, _TM['loading ...']);
441 _show_tooltip(e, _TM['loading ...']);
440 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": rid});
442 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": rid});
441 ajaxGET(url, function(o){
443 ajaxGET(url, function(o){
442 var json = JSON.parse(o.responseText);
444 var json = JSON.parse(o.responseText);
443 $target.addClass('tooltip')
445 $target.addClass('tooltip')
444 _show_tooltip(e, json['message']);
446 _show_tooltip(e, json['message']);
445 _activate_tooltip($target);
447 _activate_tooltip($target);
446 });
448 });
447 }
449 }
448 });
450 });
449 };
451 };
450
452
451 var _onSuccessFollow = function(target){
453 var _onSuccessFollow = function(target){
452 var $target = $(target);
454 var $target = $(target);
453 var $f_cnt = $('#current_followers_count');
455 var $f_cnt = $('#current_followers_count');
454 if($target.hasClass('follow')){
456 if($target.hasClass('follow')){
455 $target.attr('class', 'following');
457 $target.attr('class', 'following');
456 $target.attr('title', _TM['Stop following this repository']);
458 $target.attr('title', _TM['Stop following this repository']);
457 if($f_cnt.html()){
459 if($f_cnt.html()){
458 var cnt = Number($f_cnt.html())+1;
460 var cnt = Number($f_cnt.html())+1;
459 $f_cnt.html(cnt);
461 $f_cnt.html(cnt);
460 }
462 }
461 }
463 }
462 else{
464 else{
463 $target.attr('class', 'follow');
465 $target.attr('class', 'follow');
464 $target.attr('title', _TM['Start following this repository']);
466 $target.attr('title', _TM['Start following this repository']);
465 if($f_cnt.html()){
467 if($f_cnt.html()){
466 var cnt = Number($f_cnt.html())-1;
468 var cnt = Number($f_cnt.html())-1;
467 $f_cnt.html(cnt);
469 $f_cnt.html(cnt);
468 }
470 }
469 }
471 }
470 }
472 }
471
473
472 var toggleFollowingRepo = function(target, follows_repo_id, token, user_id){
474 var toggleFollowingRepo = function(target, follows_repo_id, token, user_id){
473 var args = 'follows_repo_id=' + follows_repo_id;
475 var args = 'follows_repo_id=' + follows_repo_id;
474 args += '&amp;auth_token=' + token;
476 args += '&amp;auth_token=' + token;
475 if(user_id != undefined){
477 if(user_id != undefined){
476 args +="&amp;user_id=" + user_id;
478 args +="&amp;user_id=" + user_id;
477 }
479 }
478 $.post(TOGGLE_FOLLOW_URL, args, function(data){
480 $.post(TOGGLE_FOLLOW_URL, args, function(data){
479 _onSuccessFollow(target);
481 _onSuccessFollow(target);
480 });
482 });
481 return false;
483 return false;
482 };
484 };
483
485
484 var showRepoSize = function(target, repo_name, token){
486 var showRepoSize = function(target, repo_name, token){
485 var args = 'auth_token=' + token;
487 var args = 'auth_token=' + token;
486
488
487 if(!$("#" + target).hasClass('loaded')){
489 if(!$("#" + target).hasClass('loaded')){
488 $("#" + target).html(_TM['Loading ...']);
490 $("#" + target).html(_TM['Loading ...']);
489 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
491 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
490 $.post(url, args, function(data) {
492 $.post(url, args, function(data) {
491 $("#" + target).html(data);
493 $("#" + target).html(data);
492 $("#" + target).addClass('loaded');
494 $("#" + target).addClass('loaded');
493 });
495 });
494 }
496 }
495 return false;
497 return false;
496 };
498 };
497
499
498 /**
500 /**
499 * tooltips
501 * tooltips
500 */
502 */
501
503
502 var tooltip_activate = function(){
504 var tooltip_activate = function(){
503 $(document).ready(_init_tooltip);
505 $(document).ready(_init_tooltip);
504 };
506 };
505
507
506 var _activate_tooltip = function($tt){
508 var _activate_tooltip = function($tt){
507 $tt.mouseover(_show_tooltip);
509 $tt.mouseover(_show_tooltip);
508 $tt.mousemove(_move_tooltip);
510 $tt.mousemove(_move_tooltip);
509 $tt.mouseout(_close_tooltip);
511 $tt.mouseout(_close_tooltip);
510 };
512 };
511
513
512 var _init_tooltip = function(){
514 var _init_tooltip = function(){
513 var $tipBox = $('#tip-box');
515 var $tipBox = $('#tip-box');
514 if(!$tipBox.length){
516 if(!$tipBox.length){
515 $tipBox = $('<div id="tip-box"></div>')
517 $tipBox = $('<div id="tip-box"></div>')
516 $(document.body).append($tipBox);
518 $(document.body).append($tipBox);
517 }
519 }
518
520
519 $tipBox.hide();
521 $tipBox.hide();
520 $tipBox.css('position', 'absolute');
522 $tipBox.css('position', 'absolute');
521 $tipBox.css('max-width', '600px');
523 $tipBox.css('max-width', '600px');
522
524
523 _activate_tooltip($('.tooltip'));
525 _activate_tooltip($('.tooltip'));
524 };
526 };
525
527
526 var _show_tooltip = function(e, tipText){
528 var _show_tooltip = function(e, tipText){
527 e.stopImmediatePropagation();
529 e.stopImmediatePropagation();
528 var el = e.currentTarget;
530 var el = e.currentTarget;
529 if(tipText){
531 if(tipText){
530 // just use it
532 // just use it
531 } else if(el.tagName.toLowerCase() === 'img'){
533 } else if(el.tagName.toLowerCase() === 'img'){
532 tipText = el.alt ? el.alt : '';
534 tipText = el.alt ? el.alt : '';
533 } else {
535 } else {
534 tipText = el.title ? el.title : '';
536 tipText = el.title ? el.title : '';
535 }
537 }
536
538
537 if(tipText !== ''){
539 if(tipText !== ''){
538 // save org title
540 // save org title
539 $(el).attr('tt_title', tipText);
541 $(el).attr('tt_title', tipText);
540 // reset title to not show org tooltips
542 // reset title to not show org tooltips
541 $(el).attr('title', '');
543 $(el).attr('title', '');
542
544
543 var $tipBox = $('#tip-box');
545 var $tipBox = $('#tip-box');
544 $tipBox.html(tipText);
546 $tipBox.html(tipText);
545 $tipBox.css('display', 'block');
547 $tipBox.css('display', 'block');
546 }
548 }
547 };
549 };
548
550
549 var _move_tooltip = function(e){
551 var _move_tooltip = function(e){
550 e.stopImmediatePropagation();
552 e.stopImmediatePropagation();
551 var $tipBox = $('#tip-box');
553 var $tipBox = $('#tip-box');
552 $tipBox.css('top', (e.pageY + 15) + 'px');
554 $tipBox.css('top', (e.pageY + 15) + 'px');
553 $tipBox.css('left', (e.pageX + 15) + 'px');
555 $tipBox.css('left', (e.pageX + 15) + 'px');
554 };
556 };
555
557
556 var _close_tooltip = function(e){
558 var _close_tooltip = function(e){
557 e.stopImmediatePropagation();
559 e.stopImmediatePropagation();
558 var $tipBox = $('#tip-box');
560 var $tipBox = $('#tip-box');
559 $tipBox.hide();
561 $tipBox.hide();
560 var el = e.currentTarget;
562 var el = e.currentTarget;
561 $(el).attr('title', $(el).attr('tt_title'));
563 $(el).attr('title', $(el).attr('tt_title'));
562 };
564 };
563
565
564 /**
566 /**
565 * Quick filter widget
567 * Quick filter widget
566 *
568 *
567 * @param target: filter input target
569 * @param target: filter input target
568 * @param nodes: list of nodes in html we want to filter.
570 * @param nodes: list of nodes in html we want to filter.
569 * @param display_element function that takes current node from nodes and
571 * @param display_element function that takes current node from nodes and
570 * does hide or show based on the node
572 * does hide or show based on the node
571 */
573 */
572 var q_filter = function(target, nodes, display_element){
574 var q_filter = function(target, nodes, display_element){
573 var nodes = nodes;
575 var nodes = nodes;
574 var $q_filter_field = $('#' + target);
576 var $q_filter_field = $('#' + target);
575 var F = YAHOO.namespace(target);
577 var F = YAHOO.namespace(target);
576
578
577 $q_filter_field.keyup(function(e){
579 $q_filter_field.keyup(function(e){
578 clearTimeout(F.filterTimeout);
580 clearTimeout(F.filterTimeout);
579 F.filterTimeout = setTimeout(F.updateFilter, 600);
581 F.filterTimeout = setTimeout(F.updateFilter, 600);
580 });
582 });
581
583
582 F.filterTimeout = null;
584 F.filterTimeout = null;
583
585
584 F.updateFilter = function() {
586 F.updateFilter = function() {
585 // Reset timeout
587 // Reset timeout
586 F.filterTimeout = null;
588 F.filterTimeout = null;
587
589
588 var obsolete = [];
590 var obsolete = [];
589
591
590 var req = $q_filter_field.val().toLowerCase();
592 var req = $q_filter_field.val().toLowerCase();
591
593
592 var l = nodes.length;
594 var l = nodes.length;
593 var i;
595 var i;
594 var showing = 0;
596 var showing = 0;
595
597
596 for (i=0; i<l; i++ ){
598 for (i=0; i<l; i++ ){
597 var n = nodes[i];
599 var n = nodes[i];
598 var target_element = display_element(n)
600 var target_element = display_element(n)
599 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
601 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
600 $(target_element).hide();
602 $(target_element).hide();
601 }
603 }
602 else{
604 else{
603 $(target_element).show();
605 $(target_element).show();
604 showing += 1;
606 showing += 1;
605 }
607 }
606 }
608 }
607
609
608 $('#repo_count').html(showing); /* FIXME: don't hardcode */
610 $('#repo_count').html(showing); /* FIXME: don't hardcode */
609 }
611 }
610 };
612 };
611
613
612 /* return jQuery expression with a tr with body in 3rd column and class cls and id named after the body */
614 /* return jQuery expression with a tr with body in 3rd column and class cls and id named after the body */
613 var _table_tr = function(cls, body){
615 var _table_tr = function(cls, body){
614 // like: <div class="comment" id="comment-8" line="o92"><div class="comment-wrapp">...
616 // like: <div class="comment" id="comment-8" line="o92"><div class="comment-wrapp">...
615 // except new inlines which are different ...
617 // except new inlines which are different ...
616 var comment_id = ($(body).attr('id') || 'comment-new').split('comment-')[1];
618 var comment_id = ($(body).attr('id') || 'comment-new').split('comment-')[1];
617 var tr_id = 'comment-tr-{0}'.format(comment_id);
619 var tr_id = 'comment-tr-{0}'.format(comment_id);
618 return $(('<tr id="{0}" class="{1}">'+
620 return $(('<tr id="{0}" class="{1}">'+
619 '<td class="lineno-inline new-inline"></td>'+
621 '<td class="lineno-inline new-inline"></td>'+
620 '<td class="lineno-inline old-inline"></td>'+
622 '<td class="lineno-inline old-inline"></td>'+
621 '<td>{2}</td>'+
623 '<td>{2}</td>'+
622 '</tr>').format(tr_id, cls, body));
624 '</tr>').format(tr_id, cls, body));
623 };
625 };
624
626
625 /** return jQuery expression with new inline form based on template **/
627 /** return jQuery expression with new inline form based on template **/
626 var _createInlineForm = function(parent_tr, f_path, line) {
628 var _createInlineForm = function(parent_tr, f_path, line) {
627 var $tmpl = $('#comment-inline-form-template').html().format(f_path, line);
629 var $tmpl = $('#comment-inline-form-template').html().format(f_path, line);
628 var $form = _table_tr('comment-form-inline', $tmpl)
630 var $form = _table_tr('comment-form-inline', $tmpl)
629
631
630 // create event for hide button
632 // create event for hide button
631 $form.find('.hide-inline-form').click(function(e) {
633 $form.find('.hide-inline-form').click(function(e) {
632 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
634 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
633 if($(newtr).next().hasClass('inline-comments-button')){
635 if($(newtr).next().hasClass('inline-comments-button')){
634 $(newtr).next().show();
636 $(newtr).next().show();
635 }
637 }
636 $(newtr).remove();
638 $(newtr).remove();
637 $(parent_tr).removeClass('form-open');
639 $(parent_tr).removeClass('form-open');
638 $(parent_tr).removeClass('hl-comment');
640 $(parent_tr).removeClass('hl-comment');
639 });
641 });
640
642
641 return $form
643 return $form
642 };
644 };
643
645
644 /**
646 /**
645 * Inject inline comment for an given TR. This tr should always be a .line .
647 * Inject inline comment for an given TR. This tr should always be a .line .
646 * The form will be inject after any comments.
648 * The form will be inject after any comments.
647 */
649 */
648 var injectInlineForm = function(tr){
650 var injectInlineForm = function(tr){
649 var $tr = $(tr);
651 var $tr = $(tr);
650 if(!$tr.hasClass('line')){
652 if(!$tr.hasClass('line')){
651 return
653 return
652 }
654 }
653 var submit_url = AJAX_COMMENT_URL;
655 var submit_url = AJAX_COMMENT_URL;
654 var $td = $tr.find('.code');
656 var $td = $tr.find('.code');
655 if($tr.hasClass('form-open') || $tr.hasClass('context') || $td.hasClass('no-comment')){
657 if($tr.hasClass('form-open') || $tr.hasClass('context') || $td.hasClass('no-comment')){
656 return
658 return
657 }
659 }
658 $tr.addClass('form-open hl-comment');
660 $tr.addClass('form-open hl-comment');
659 var $node = $tr.parent().parent().parent().find('.full_f_path');
661 var $node = $tr.parent().parent().parent().find('.full_f_path');
660 var f_path = $node.attr('path');
662 var f_path = $node.attr('path');
661 var lineno = _getLineNo(tr);
663 var lineno = _getLineNo(tr);
662 var $form = _createInlineForm(tr, f_path, lineno, submit_url);
664 var $form = _createInlineForm(tr, f_path, lineno, submit_url);
663
665
664 var $parent = $tr;
666 var $parent = $tr;
665 while ($parent.next().hasClass('inline-comments')){
667 while ($parent.next().hasClass('inline-comments')){
666 var $parent = $parent.next();
668 var $parent = $parent.next();
667 }
669 }
668 $form.insertAfter($parent);
670 $form.insertAfter($parent);
669 var $overlay = $form.find('.submitting-overlay');
671 var $overlay = $form.find('.submitting-overlay');
670 var $inlineform = $form.find('.inline-form');
672 var $inlineform = $form.find('.inline-form');
671
673
672 $form.submit(function(e){
674 $form.submit(function(e){
673 e.preventDefault();
675 e.preventDefault();
674
676
675 if(lineno === undefined){
677 if(lineno === undefined){
676 alert('Error submitting, line ' + lineno + ' not found.');
678 alert('Error submitting, line ' + lineno + ' not found.');
677 return
679 return
678 }
680 }
679 if(f_path === undefined){
681 if(f_path === undefined){
680 alert('Error submitting, file path ' + f_path + ' not found.');
682 alert('Error submitting, file path ' + f_path + ' not found.');
681 return
683 return
682 }
684 }
683
685
684 var text = $('#text_'+lineno).val();
686 var text = $('#text_'+lineno).val();
685 if(text == ""){
687 if(text == ""){
686 return
688 return
687 }
689 }
688
690
689 $overlay.show();
691 $overlay.show();
690
692
691 var success = function(o){
693 var success = function(o){
692 $tr.removeClass('form-open');
694 $tr.removeClass('form-open');
693 $form.remove();
695 $form.remove();
694 var json_data = JSON.parse(o.responseText);
696 var json_data = JSON.parse(o.responseText);
695 _renderInlineComment(json_data);
697 _renderInlineComment(json_data);
696 };
698 };
697 var postData = {
699 var postData = {
698 'text': text,
700 'text': text,
699 'f_path': f_path,
701 'f_path': f_path,
700 'line': lineno
702 'line': lineno
701 };
703 };
702 ajaxPOST(submit_url, postData, success);
704 ajaxPOST(submit_url, postData, success);
703 });
705 });
704
706
705 $('#preview-btn_'+lineno).click(function(e){
707 $('#preview-btn_'+lineno).click(function(e){
706 var text = $('#text_'+lineno).val();
708 var text = $('#text_'+lineno).val();
707 if(!text){
709 if(!text){
708 return
710 return
709 }
711 }
710 $('#preview-box_'+lineno).addClass('unloaded');
712 $('#preview-box_'+lineno).addClass('unloaded');
711 $('#preview-box_'+lineno).html(_TM['Loading ...']);
713 $('#preview-box_'+lineno).html(_TM['Loading ...']);
712 $('#edit-container_'+lineno).hide();
714 $('#edit-container_'+lineno).hide();
713 $('#edit-btn_'+lineno).show();
715 $('#edit-btn_'+lineno).show();
714 $('#preview-container_'+lineno).show();
716 $('#preview-container_'+lineno).show();
715 $('#preview-btn_'+lineno).hide();
717 $('#preview-btn_'+lineno).hide();
716
718
717 var url = pyroutes.url('changeset_comment_preview', {'repo_name': REPO_NAME});
719 var url = pyroutes.url('changeset_comment_preview', {'repo_name': REPO_NAME});
718 var post_data = {'text': text};
720 var post_data = {'text': text};
719 ajaxPOST(url, post_data, function(o){
721 ajaxPOST(url, post_data, function(o){
720 $('#preview-box_'+lineno).html(o.responseText);
722 $('#preview-box_'+lineno).html(o.responseText);
721 $('#preview-box_'+lineno).removeClass('unloaded');
723 $('#preview-box_'+lineno).removeClass('unloaded');
722 })
724 })
723 })
725 })
724 $('#edit-btn_'+lineno).click(function(e){
726 $('#edit-btn_'+lineno).click(function(e){
725 $('#edit-container_'+lineno).show();
727 $('#edit-container_'+lineno).show();
726 $('#edit-btn_'+lineno).hide();
728 $('#edit-btn_'+lineno).hide();
727 $('#preview-container_'+lineno).hide();
729 $('#preview-container_'+lineno).hide();
728 $('#preview-btn_'+lineno).show();
730 $('#preview-btn_'+lineno).show();
729 })
731 })
730
732
731 setTimeout(function(){
733 setTimeout(function(){
732 // callbacks
734 // callbacks
733 tooltip_activate();
735 tooltip_activate();
734 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
736 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
735 _USERS_AC_DATA, _GROUPS_AC_DATA);
737 _USERS_AC_DATA, _GROUPS_AC_DATA);
736 $('#text_'+lineno).focus();
738 $('#text_'+lineno).focus();
737 },10)
739 },10)
738 };
740 };
739
741
740 var deleteComment = function(comment_id){
742 var deleteComment = function(comment_id){
741 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
743 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
742 var postData = {'_method':'delete'};
744 var postData = {'_method':'delete'};
743 var success = function(o){
745 var success = function(o){
744 var $deleted = $('#comment-tr-'+comment_id);
746 var $deleted = $('#comment-tr-'+comment_id);
745 var $prev = $deleted.prev('tr');
747 var $prev = $deleted.prev('tr');
746 $deleted.remove();
748 $deleted.remove();
747 _placeAddButton($prev);
749 _placeAddButton($prev);
748 }
750 }
749 ajaxPOST(url,postData,success);
751 ajaxPOST(url,postData,success);
750 }
752 }
751
753
752 var _getLineNo = function(tr) {
754 var _getLineNo = function(tr) {
753 var line;
755 var line;
754 var o = $(tr).children()[0].id.split('_');
756 var o = $(tr).children()[0].id.split('_');
755 var n = $(tr).children()[1].id.split('_');
757 var n = $(tr).children()[1].id.split('_');
756
758
757 if (n.length >= 2) {
759 if (n.length >= 2) {
758 line = n[n.length-1];
760 line = n[n.length-1];
759 } else if (o.length >= 2) {
761 } else if (o.length >= 2) {
760 line = o[o.length-1];
762 line = o[o.length-1];
761 }
763 }
762
764
763 return line
765 return line
764 };
766 };
765
767
766 var _placeAddButton = function($line_tr){
768 var _placeAddButton = function($line_tr){
767 var $tr = $line_tr;
769 var $tr = $line_tr;
768 while ($tr.next().hasClass('inline-comments')){
770 while ($tr.next().hasClass('inline-comments')){
769 $tr.find('.add-comment').remove();
771 $tr.find('.add-comment').remove();
770 $tr = $tr.next();
772 $tr = $tr.next();
771 }
773 }
772 $tr.find('.add-comment').remove();
774 $tr.find('.add-comment').remove();
773 var label = TRANSLATION_MAP['Add Another Comment'];
775 var label = TRANSLATION_MAP['Add Another Comment'];
774 var $html_el = $('<div class="add-comment"><span class="btn btn-mini">{0}</span></div>'.format(label));
776 var $html_el = $('<div class="add-comment"><span class="btn btn-mini">{0}</span></div>'.format(label));
775 $html_el.click(function(e) {
777 $html_el.click(function(e) {
776 injectInlineForm($line_tr);
778 injectInlineForm($line_tr);
777 });
779 });
778 $tr.find('.comment').after($html_el);
780 $tr.find('.comment').after($html_el);
779 };
781 };
780
782
781 /**
783 /**
782 * Places the inline comment into the changeset block in proper line position
784 * Places the inline comment into the changeset block in proper line position
783 */
785 */
784 var _placeInline = function(target_id, lineno, html){
786 var _placeInline = function(target_id, lineno, html){
785 var $td = $("#{0}_{1}".format(target_id, lineno));
787 var $td = $("#{0}_{1}".format(target_id, lineno));
786 if (!$td.length){
788 if (!$td.length){
787 return false;
789 return false;
788 }
790 }
789
791
790 // check if there are comments already !
792 // check if there are comments already !
791 var $line_tr = $td.parent(); // the tr
793 var $line_tr = $td.parent(); // the tr
792 var $after_tr = $line_tr;
794 var $after_tr = $line_tr;
793 while ($after_tr.next().hasClass('inline-comments')){
795 while ($after_tr.next().hasClass('inline-comments')){
794 $after_tr = $after_tr.next();
796 $after_tr = $after_tr.next();
795 }
797 }
796 // put in the comment at the bottom
798 // put in the comment at the bottom
797 var $tr = _table_tr('inline-comments', html)
799 var $tr = _table_tr('inline-comments', html)
798 $tr.find('div.comment').addClass('inline-comment');
800 $tr.find('div.comment').addClass('inline-comment');
799 $after_tr.after($tr);
801 $after_tr.after($tr);
800
802
801 // scan nodes, and attach add button to last one
803 // scan nodes, and attach add button to last one
802 _placeAddButton($line_tr);
804 _placeAddButton($line_tr);
803 return true;
805 return true;
804 }
806 }
805
807
806 /**
808 /**
807 * make a single inline comment and place it inside
809 * make a single inline comment and place it inside
808 */
810 */
809 var _renderInlineComment = function(json_data){
811 var _renderInlineComment = function(json_data){
810 var html = json_data['rendered_text'];
812 var html = json_data['rendered_text'];
811 var lineno = json_data['line_no'];
813 var lineno = json_data['line_no'];
812 var target_id = json_data['target_id'];
814 var target_id = json_data['target_id'];
813 return _placeInline(target_id, lineno, html);
815 return _placeInline(target_id, lineno, html);
814 }
816 }
815
817
816 /**
818 /**
817 * Iterates over all the inlines, and places them inside proper blocks of data
819 * Iterates over all the inlines, and places them inside proper blocks of data
818 */
820 */
819 var renderInlineComments = function(file_comments){
821 var renderInlineComments = function(file_comments){
820 for (var f in file_comments){
822 for (var f in file_comments){
821 // holding all comments for a FILE
823 // holding all comments for a FILE
822 var box = file_comments[f];
824 var box = file_comments[f];
823
825
824 var target_id = $(box).attr('target_id');
826 var target_id = $(box).attr('target_id');
825 // actual comments with line numbers
827 // actual comments with line numbers
826 var comments = box.children;
828 var comments = box.children;
827 for(var i=0; i<comments.length; i++){
829 for(var i=0; i<comments.length; i++){
828 var data = {
830 var data = {
829 'rendered_text': comments[i].outerHTML,
831 'rendered_text': comments[i].outerHTML,
830 'line_no': $(comments[i]).attr('line'),
832 'line_no': $(comments[i]).attr('line'),
831 'target_id': target_id
833 'target_id': target_id
832 }
834 }
833 if (_renderInlineComment(data)) {
835 if (_renderInlineComment(data)) {
834 $(comments[i]).hide();
836 $(comments[i]).hide();
835 }else{
837 }else{
836 var txt = document.createTextNode(
838 var txt = document.createTextNode(
837 "Comment to " + YUD.getAttribute(comments[i].parentNode,'path') +
839 "Comment to " + YUD.getAttribute(comments[i].parentNode,'path') +
838 " line " + data.line_no +
840 " line " + data.line_no +
839 " which is outside the diff context:");
841 " which is outside the diff context:");
840 comments[i].insertBefore(txt, comments[i].firstChild);
842 comments[i].insertBefore(txt, comments[i].firstChild);
841 }
843 }
842 }
844 }
843 $(box).show();
845 $(box).show();
844 }
846 }
845 }
847 }
846
848
847 /**
849 /**
848 * Double link comments
850 * Double link comments
849 */
851 */
850 var linkInlineComments = function(firstlinks, comments){
852 var linkInlineComments = function(firstlinks, comments){
851 var $comments = $(comments);
853 var $comments = $(comments);
852 if ($comments.length > 0) {
854 if ($comments.length > 0) {
853 $(firstlinks).html('<a href="#{0}">First comment</a>'.format($comments.attr('id')));
855 $(firstlinks).html('<a href="#{0}">First comment</a>'.format($comments.attr('id')));
854 }
856 }
855 if ($comments.length <= 1) {
857 if ($comments.length <= 1) {
856 return;
858 return;
857 }
859 }
858
860
859 $comments.each(function(i, e){
861 $comments.each(function(i, e){
860 var prev = '';
862 var prev = '';
861 if (i > 0){
863 if (i > 0){
862 var prev_anchor = YUD.getAttribute(comments.item(i-1),'id');
864 var prev_anchor = YUD.getAttribute(comments.item(i-1),'id');
863 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
865 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
864 }
866 }
865 var next = '';
867 var next = '';
866 if (i+1 < comments.length){
868 if (i+1 < comments.length){
867 var next_anchor = YUD.getAttribute(comments.item(i+1),'id');
869 var next_anchor = YUD.getAttribute(comments.item(i+1),'id');
868 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
870 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
869 }
871 }
870 var $div = $(('<div class="prev-next-comment">'+
872 var $div = $(('<div class="prev-next-comment">'+
871 '<div class="prev-comment">{0}</div>'+
873 '<div class="prev-comment">{0}</div>'+
872 '<div class="next-comment">{1}</div>').format(prev, next));
874 '<div class="next-comment">{1}</div>').format(prev, next));
873 $div.prependTo(this);
875 $div.prependTo(this);
874 });
876 });
875 }
877 }
876
878
877 /* activate files.html stuff */
879 /* activate files.html stuff */
878 var fileBrowserListeners = function(current_url, node_list_url, url_base){
880 var fileBrowserListeners = function(current_url, node_list_url, url_base){
879 var current_url_branch = "?branch=__BRANCH__";
881 var current_url_branch = "?branch=__BRANCH__";
880
882
881 $('#stay_at_branch').on('click',function(e){
883 $('#stay_at_branch').on('click',function(e){
882 if(e.currentTarget.checked){
884 if(e.currentTarget.checked){
883 var uri = current_url_branch;
885 var uri = current_url_branch;
884 uri = uri.replace('__BRANCH__',e.currentTarget.value);
886 uri = uri.replace('__BRANCH__',e.currentTarget.value);
885 window.location = uri;
887 window.location = uri;
886 }
888 }
887 else{
889 else{
888 window.location = current_url;
890 window.location = current_url;
889 }
891 }
890 })
892 })
891
893
892 var $node_filter = $('#node_filter');
894 var $node_filter = $('#node_filter');
893
895
894 var filterTimeout = null;
896 var filterTimeout = null;
895 var nodes = null;
897 var nodes = null;
896
898
897 var initFilter = function(){
899 var initFilter = function(){
898 $('#node_filter_box_loading').show();
900 $('#node_filter_box_loading').show();
899 $('#search_activate_id').hide();
901 $('#search_activate_id').hide();
900 $('#add_node_id').hide();
902 $('#add_node_id').hide();
901 YUC.initHeader('X-PARTIAL-XHR',true);
903 YUC.initHeader('X-PARTIAL-XHR',true);
902 YUC.asyncRequest('GET', node_list_url, {
904 YUC.asyncRequest('GET', node_list_url, {
903 success:function(o){
905 success:function(o){
904 nodes = JSON.parse(o.responseText).nodes;
906 nodes = JSON.parse(o.responseText).nodes;
905 $('#node_filter_box_loading').hide();
907 $('#node_filter_box_loading').hide();
906 $('#node_filter_box').show();
908 $('#node_filter_box').show();
907 $node_filter.focus();
909 $node_filter.focus();
908 if($node_filter.hasClass('init')){
910 if($node_filter.hasClass('init')){
909 $node_filter.val('');
911 $node_filter.val('');
910 $node_filter.removeClass('init');
912 $node_filter.removeClass('init');
911 }
913 }
912 },
914 },
913 failure:function(o){
915 failure:function(o){
914 console.log('failed to load');
916 console.log('failed to load');
915 }
917 }
916 },null);
918 },null);
917 }
919 }
918
920
919 var updateFilter = function(e) {
921 var updateFilter = function(e) {
920 return function(){
922 return function(){
921 // Reset timeout
923 // Reset timeout
922 filterTimeout = null;
924 filterTimeout = null;
923 var query = e.currentTarget.value.toLowerCase();
925 var query = e.currentTarget.value.toLowerCase();
924 var match = [];
926 var match = [];
925 var matches = 0;
927 var matches = 0;
926 var matches_max = 20;
928 var matches_max = 20;
927 if (query != ""){
929 if (query != ""){
928 for(var i=0;i<nodes.length;i++){
930 for(var i=0;i<nodes.length;i++){
929 var pos = nodes[i].name.toLowerCase().indexOf(query)
931 var pos = nodes[i].name.toLowerCase().indexOf(query)
930 if(query && pos != -1){
932 if(query && pos != -1){
931 matches++
933 matches++
932 //show only certain amount to not kill browser
934 //show only certain amount to not kill browser
933 if (matches > matches_max){
935 if (matches > matches_max){
934 break;
936 break;
935 }
937 }
936
938
937 var n = nodes[i].name;
939 var n = nodes[i].name;
938 var t = nodes[i].type;
940 var t = nodes[i].type;
939 var n_hl = n.substring(0,pos)
941 var n_hl = n.substring(0,pos)
940 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
942 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
941 +n.substring(pos+query.length)
943 +n.substring(pos+query.length)
942 var new_url = url_base.replace('__FPATH__',n);
944 var new_url = url_base.replace('__FPATH__',n);
943 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
945 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
944 }
946 }
945 if(match.length >= matches_max){
947 if(match.length >= matches_max){
946 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
948 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
947 break;
949 break;
948 }
950 }
949 }
951 }
950 }
952 }
951 if(query != ""){
953 if(query != ""){
952 $('#tbody').hide();
954 $('#tbody').hide();
953 $('#tbody_filtered').show();
955 $('#tbody_filtered').show();
954
956
955 if (match.length==0){
957 if (match.length==0){
956 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
958 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
957 }
959 }
958
960
959 $('#tbody_filtered').html(match.join(""));
961 $('#tbody_filtered').html(match.join(""));
960 }
962 }
961 else{
963 else{
962 $('#tbody').show();
964 $('#tbody').show();
963 $('#tbody_filtered').hide();
965 $('#tbody_filtered').hide();
964 }
966 }
965 }
967 }
966 };
968 };
967
969
968 $('#filter_activate').click(function(){
970 $('#filter_activate').click(function(){
969 initFilter();
971 initFilter();
970 });
972 });
971 $node_filter.click(function(){
973 $node_filter.click(function(){
972 if($node_filter.hasClass('init')){
974 if($node_filter.hasClass('init')){
973 $node_filter.val('');
975 $node_filter.val('');
974 $node_filter.removeClass('init');
976 $node_filter.removeClass('init');
975 }
977 }
976 });
978 });
977 $node_filter.keyup(function(e){
979 $node_filter.keyup(function(e){
978 clearTimeout(filterTimeout);
980 clearTimeout(filterTimeout);
979 filterTimeout = setTimeout(updateFilter(e),600);
981 filterTimeout = setTimeout(updateFilter(e),600);
980 });
982 });
981 };
983 };
982
984
983
985
984 var initCodeMirror = function(textarea_id, resetUrl){
986 var initCodeMirror = function(textarea_id, resetUrl){
985 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
987 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
986 mode: "null",
988 mode: "null",
987 lineNumbers: true,
989 lineNumbers: true,
988 indentUnit: 4,
990 indentUnit: 4,
989 autofocus: true
991 autofocus: true
990 });
992 });
991 $('#reset').click(function(e){
993 $('#reset').click(function(e){
992 window.location=resetUrl;
994 window.location=resetUrl;
993 });
995 });
994
996
995 $('#file_enable').click(function(){
997 $('#file_enable').click(function(){
996 $('#editor_container').show();
998 $('#editor_container').show();
997 $('#upload_file_container').hide();
999 $('#upload_file_container').hide();
998 $('#filename_container').show();
1000 $('#filename_container').show();
999 $('#set_mode_header').show();
1001 $('#set_mode_header').show();
1000 });
1002 });
1001
1003
1002 $('#upload_file_enable').click(function(){
1004 $('#upload_file_enable').click(function(){
1003 $('#editor_container').hide();
1005 $('#editor_container').hide();
1004 $('#upload_file_container').show();
1006 $('#upload_file_container').show();
1005 $('#filename_container').hide();
1007 $('#filename_container').hide();
1006 $('#set_mode_header').hide();
1008 $('#set_mode_header').hide();
1007 });
1009 });
1008
1010
1009 return myCodeMirror
1011 return myCodeMirror
1010 };
1012 };
1011
1013
1012 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
1014 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
1013 codeMirrorInstance.setOption("mode", mode);
1015 codeMirrorInstance.setOption("mode", mode);
1014 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
1016 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
1015 }
1017 }
1016
1018
1017
1019
1018 var _getIdentNode = function(n){
1020 var _getIdentNode = function(n){
1019 //iterate thrugh nodes until matching interesting node
1021 //iterate thrugh nodes until matching interesting node
1020
1022
1021 if (typeof n == 'undefined'){
1023 if (typeof n == 'undefined'){
1022 return -1
1024 return -1
1023 }
1025 }
1024
1026
1025 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
1027 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
1026 return n
1028 return n
1027 }
1029 }
1028 else{
1030 else{
1029 return _getIdentNode(n.parentNode);
1031 return _getIdentNode(n.parentNode);
1030 }
1032 }
1031 };
1033 };
1032
1034
1033 /* generate links for multi line selects that can be shown by files.html page_highlights.
1035 /* generate links for multi line selects that can be shown by files.html page_highlights.
1034 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
1036 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
1035 var getSelectionLink = function(e) {
1037 var getSelectionLink = function(e) {
1036 //get selection from start/to nodes
1038 //get selection from start/to nodes
1037 if (typeof window.getSelection != "undefined") {
1039 if (typeof window.getSelection != "undefined") {
1038 s = window.getSelection();
1040 s = window.getSelection();
1039
1041
1040 var from = _getIdentNode(s.anchorNode);
1042 var from = _getIdentNode(s.anchorNode);
1041 var till = _getIdentNode(s.focusNode);
1043 var till = _getIdentNode(s.focusNode);
1042
1044
1043 var f_int = parseInt(from.id.replace('L',''));
1045 var f_int = parseInt(from.id.replace('L',''));
1044 var t_int = parseInt(till.id.replace('L',''));
1046 var t_int = parseInt(till.id.replace('L',''));
1045
1047
1046 var yoffset = 35;
1048 var yoffset = 35;
1047 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
1049 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
1048 if (ranges[0] > ranges[1]){
1050 if (ranges[0] > ranges[1]){
1049 //highlight from bottom
1051 //highlight from bottom
1050 yoffset = -yoffset;
1052 yoffset = -yoffset;
1051 ranges = [ranges[1], ranges[0]];
1053 ranges = [ranges[1], ranges[0]];
1052 }
1054 }
1053 var $hl_div = $('div#linktt');
1055 var $hl_div = $('div#linktt');
1054 // if we select more than 2 lines
1056 // if we select more than 2 lines
1055 if (ranges[0] != ranges[1]){
1057 if (ranges[0] != ranges[1]){
1056 if ($hl_div.length) {
1058 if ($hl_div.length) {
1057 $hl_div.html('');
1059 $hl_div.html('');
1058 } else {
1060 } else {
1059 $hl_div = $('<div id="linktt" class="hl-tip-box">');
1061 $hl_div = $('<div id="linktt" class="hl-tip-box">');
1060 $('body').prepend($hl_div);
1062 $('body').prepend($hl_div);
1061 }
1063 }
1062
1064
1063 $hl_div.append($('<a>').html(_TM['Selection link']).attr('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
1065 $hl_div.append($('<a>').html(_TM['Selection link']).attr('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
1064 var xy = $(till).offset();
1066 var xy = $(till).offset();
1065 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
1067 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
1066 $hl_div.show();
1068 $hl_div.show();
1067 }
1069 }
1068 else{
1070 else{
1069 $hl_div.hide();
1071 $hl_div.hide();
1070 }
1072 }
1071 }
1073 }
1072 };
1074 };
1073
1075
1074 var deleteNotification = function(url, notification_id, callbacks){
1076 var deleteNotification = function(url, notification_id, callbacks){
1075 var callback = {
1077 var callback = {
1076 success:function(o){
1078 success:function(o){
1077 $("#notification_"+notification_id).remove();
1079 $("#notification_"+notification_id).remove();
1078 _run_callbacks(callbacks);
1080 _run_callbacks(callbacks);
1079 },
1081 },
1080 failure:function(o){
1082 failure:function(o){
1081 alert("deleteNotification failure");
1083 alert("deleteNotification failure");
1082 }
1084 }
1083 };
1085 };
1084 var postData = '_method=delete';
1086 var postData = '_method=delete';
1085 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1087 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1086 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1088 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1087 callback, postData);
1089 callback, postData);
1088 };
1090 };
1089
1091
1090 var readNotification = function(url, notification_id, callbacks){
1092 var readNotification = function(url, notification_id, callbacks){
1091 var callback = {
1093 var callback = {
1092 success:function(o){
1094 success:function(o){
1093 var $obj = $("#notification_"+notification_id);
1095 var $obj = $("#notification_"+notification_id);
1094 $obj.removeClass('unread');
1096 $obj.removeClass('unread');
1095 $obj.find('.read-notification').remove();
1097 $obj.find('.read-notification').remove();
1096 _run_callbacks(callbacks);
1098 _run_callbacks(callbacks);
1097 },
1099 },
1098 failure:function(o){
1100 failure:function(o){
1099 alert("readNotification failure");
1101 alert("readNotification failure");
1100 }
1102 }
1101 };
1103 };
1102 var postData = '_method=put';
1104 var postData = '_method=put';
1103 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1105 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1104 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1106 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1105 callback, postData);
1107 callback, postData);
1106 };
1108 };
1107
1109
1108 /** MEMBERS AUTOCOMPLETE WIDGET **/
1110 /** MEMBERS AUTOCOMPLETE WIDGET **/
1109
1111
1110 var _MembersAutoComplete = function (divid, cont, users_list, groups_list) {
1112 var _MembersAutoComplete = function (divid, cont, users_list, groups_list) {
1111 var myUsers = users_list;
1113 var myUsers = users_list;
1112 var myGroups = groups_list;
1114 var myGroups = groups_list;
1113
1115
1114 // Define a custom search function for the DataSource of users
1116 // Define a custom search function for the DataSource of users
1115 var matchUsers = function (sQuery) {
1117 var matchUsers = function (sQuery) {
1116 // Case insensitive matching
1118 // Case insensitive matching
1117 var query = sQuery.toLowerCase();
1119 var query = sQuery.toLowerCase();
1118 var i = 0;
1120 var i = 0;
1119 var l = myUsers.length;
1121 var l = myUsers.length;
1120 var matches = [];
1122 var matches = [];
1121
1123
1122 // Match against each name of each contact
1124 // Match against each name of each contact
1123 for (; i < l; i++) {
1125 for (; i < l; i++) {
1124 var contact = myUsers[i];
1126 var contact = myUsers[i];
1125 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1127 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1126 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1128 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1127 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1129 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1128 matches[matches.length] = contact;
1130 matches[matches.length] = contact;
1129 }
1131 }
1130 }
1132 }
1131 return matches;
1133 return matches;
1132 };
1134 };
1133
1135
1134 // Define a custom search function for the DataSource of userGroups
1136 // Define a custom search function for the DataSource of userGroups
1135 var matchGroups = function (sQuery) {
1137 var matchGroups = function (sQuery) {
1136 // Case insensitive matching
1138 // Case insensitive matching
1137 var query = sQuery.toLowerCase();
1139 var query = sQuery.toLowerCase();
1138 var i = 0;
1140 var i = 0;
1139 var l = myGroups.length;
1141 var l = myGroups.length;
1140 var matches = [];
1142 var matches = [];
1141
1143
1142 // Match against each name of each contact
1144 // Match against each name of each contact
1143 for (; i < l; i++) {
1145 for (; i < l; i++) {
1144 var matched_group = myGroups[i];
1146 var matched_group = myGroups[i];
1145 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1147 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1146 matches[matches.length] = matched_group;
1148 matches[matches.length] = matched_group;
1147 }
1149 }
1148 }
1150 }
1149 return matches;
1151 return matches;
1150 };
1152 };
1151
1153
1152 //match all
1154 //match all
1153 var matchAll = function (sQuery) {
1155 var matchAll = function (sQuery) {
1154 var u = matchUsers(sQuery);
1156 var u = matchUsers(sQuery);
1155 var g = matchGroups(sQuery);
1157 var g = matchGroups(sQuery);
1156 return u.concat(g);
1158 return u.concat(g);
1157 };
1159 };
1158
1160
1159 // DataScheme for members
1161 // DataScheme for members
1160 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
1162 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
1161 memberDS.responseSchema = {
1163 memberDS.responseSchema = {
1162 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
1164 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
1163 };
1165 };
1164
1166
1165 // DataScheme for owner
1167 // DataScheme for owner
1166 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1168 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1167 ownerDS.responseSchema = {
1169 ownerDS.responseSchema = {
1168 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1170 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1169 };
1171 };
1170
1172
1171 // Instantiate AutoComplete for perms
1173 // Instantiate AutoComplete for perms
1172 var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
1174 var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
1173 membersAC.useShadow = false;
1175 membersAC.useShadow = false;
1174 membersAC.resultTypeList = false;
1176 membersAC.resultTypeList = false;
1175 membersAC.animVert = false;
1177 membersAC.animVert = false;
1176 membersAC.animHoriz = false;
1178 membersAC.animHoriz = false;
1177 membersAC.animSpeed = 0.1;
1179 membersAC.animSpeed = 0.1;
1178
1180
1179 // Instantiate AutoComplete for owner
1181 // Instantiate AutoComplete for owner
1180 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
1182 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
1181 ownerAC.useShadow = false;
1183 ownerAC.useShadow = false;
1182 ownerAC.resultTypeList = false;
1184 ownerAC.resultTypeList = false;
1183 ownerAC.animVert = false;
1185 ownerAC.animVert = false;
1184 ownerAC.animHoriz = false;
1186 ownerAC.animHoriz = false;
1185 ownerAC.animSpeed = 0.1;
1187 ownerAC.animSpeed = 0.1;
1186
1188
1187 // Helper highlight function for the formatter
1189 // Helper highlight function for the formatter
1188 var highlightMatch = function (full, snippet, matchindex) {
1190 var highlightMatch = function (full, snippet, matchindex) {
1189 return full.substring(0, matchindex)
1191 return full.substring(0, matchindex)
1190 + "<span class='match'>"
1192 + "<span class='match'>"
1191 + full.substr(matchindex, snippet.length)
1193 + full.substr(matchindex, snippet.length)
1192 + "</span>" + full.substring(matchindex + snippet.length);
1194 + "</span>" + full.substring(matchindex + snippet.length);
1193 };
1195 };
1194
1196
1195 // Custom formatter to highlight the matching letters
1197 // Custom formatter to highlight the matching letters
1196 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
1198 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
1197 var query = sQuery.toLowerCase();
1199 var query = sQuery.toLowerCase();
1198 var _gravatar = function(res, em, group){
1200 var _gravatar = function(res, em, group){
1199 if (group !== undefined){
1201 if (group !== undefined){
1200 em = '/images/icons/group.png'
1202 em = '/images/icons/group.png'
1201 }
1203 }
1202 var tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1204 var tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1203 return tmpl.format(em,res)
1205 return tmpl.format(em,res)
1204 }
1206 }
1205 // group
1207 // group
1206 if (oResultData.grname != undefined) {
1208 if (oResultData.grname != undefined) {
1207 var grname = oResultData.grname;
1209 var grname = oResultData.grname;
1208 var grmembers = oResultData.grmembers;
1210 var grmembers = oResultData.grmembers;
1209 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1211 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1210 var grprefix = "{0}: ".format(_TM['Group']);
1212 var grprefix = "{0}: ".format(_TM['Group']);
1211 var grsuffix = " (" + grmembers + " )";
1213 var grsuffix = " (" + grmembers + " )";
1212 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1214 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1213
1215
1214 if (grnameMatchIndex > -1) {
1216 if (grnameMatchIndex > -1) {
1215 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
1217 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
1216 }
1218 }
1217 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1219 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1218 // Users
1220 // Users
1219 } else if (oResultData.nname != undefined) {
1221 } else if (oResultData.nname != undefined) {
1220 var fname = oResultData.fname || "";
1222 var fname = oResultData.fname || "";
1221 var lname = oResultData.lname || "";
1223 var lname = oResultData.lname || "";
1222 var nname = oResultData.nname;
1224 var nname = oResultData.nname;
1223
1225
1224 // Guard against null value
1226 // Guard against null value
1225 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1227 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1226 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1228 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1227 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1229 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1228 displayfname, displaylname, displaynname;
1230 displayfname, displaylname, displaynname;
1229
1231
1230 if (fnameMatchIndex > -1) {
1232 if (fnameMatchIndex > -1) {
1231 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1233 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1232 } else {
1234 } else {
1233 displayfname = fname;
1235 displayfname = fname;
1234 }
1236 }
1235
1237
1236 if (lnameMatchIndex > -1) {
1238 if (lnameMatchIndex > -1) {
1237 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1239 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1238 } else {
1240 } else {
1239 displaylname = lname;
1241 displaylname = lname;
1240 }
1242 }
1241
1243
1242 if (nnameMatchIndex > -1) {
1244 if (nnameMatchIndex > -1) {
1243 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1245 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1244 } else {
1246 } else {
1245 displaynname = nname ? "(" + nname + ")" : "";
1247 displaynname = nname ? "(" + nname + ")" : "";
1246 }
1248 }
1247
1249
1248 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1250 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1249 } else {
1251 } else {
1250 return '';
1252 return '';
1251 }
1253 }
1252 };
1254 };
1253 membersAC.formatResult = custom_formatter;
1255 membersAC.formatResult = custom_formatter;
1254 ownerAC.formatResult = custom_formatter;
1256 ownerAC.formatResult = custom_formatter;
1255
1257
1256 var myHandler = function (sType, aArgs) {
1258 var myHandler = function (sType, aArgs) {
1257 var nextId = divid.split('perm_new_member_name_')[1];
1259 var nextId = divid.split('perm_new_member_name_')[1];
1258 var myAC = aArgs[0]; // reference back to the AC instance
1260 var myAC = aArgs[0]; // reference back to the AC instance
1259 var elLI = aArgs[1]; // reference to the selected LI element
1261 var elLI = aArgs[1]; // reference to the selected LI element
1260 var oData = aArgs[2]; // object literal of selected item's result data
1262 var oData = aArgs[2]; // object literal of selected item's result data
1261 //fill the autocomplete with value
1263 //fill the autocomplete with value
1262 if (oData.nname != undefined) {
1264 if (oData.nname != undefined) {
1263 //users
1265 //users
1264 myAC.getInputEl().value = oData.nname;
1266 myAC.getInputEl().value = oData.nname;
1265 $('#perm_new_member_type_'+nextId).val('user');
1267 $('#perm_new_member_type_'+nextId).val('user');
1266 } else {
1268 } else {
1267 //groups
1269 //groups
1268 myAC.getInputEl().value = oData.grname;
1270 myAC.getInputEl().value = oData.grname;
1269 $('#perm_new_member_type_'+nextId).val('users_group');
1271 $('#perm_new_member_type_'+nextId).val('users_group');
1270 }
1272 }
1271 };
1273 };
1272
1274
1273 membersAC.itemSelectEvent.subscribe(myHandler);
1275 membersAC.itemSelectEvent.subscribe(myHandler);
1274 if(ownerAC.itemSelectEvent){
1276 if(ownerAC.itemSelectEvent){
1275 ownerAC.itemSelectEvent.subscribe(myHandler);
1277 ownerAC.itemSelectEvent.subscribe(myHandler);
1276 }
1278 }
1277
1279
1278 return {
1280 return {
1279 memberDS: memberDS,
1281 memberDS: memberDS,
1280 ownerDS: ownerDS,
1282 ownerDS: ownerDS,
1281 membersAC: membersAC,
1283 membersAC: membersAC,
1282 ownerAC: ownerAC
1284 ownerAC: ownerAC
1283 };
1285 };
1284 }
1286 }
1285
1287
1286 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1288 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1287 var myUsers = users_list;
1289 var myUsers = users_list;
1288 var myGroups = groups_list;
1290 var myGroups = groups_list;
1289
1291
1290 // Define a custom search function for the DataSource of users
1292 // Define a custom search function for the DataSource of users
1291 var matchUsers = function (sQuery) {
1293 var matchUsers = function (sQuery) {
1292 var org_sQuery = sQuery;
1294 var org_sQuery = sQuery;
1293 if(this.mentionQuery == null){
1295 if(this.mentionQuery == null){
1294 return []
1296 return []
1295 }
1297 }
1296 sQuery = this.mentionQuery;
1298 sQuery = this.mentionQuery;
1297 // Case insensitive matching
1299 // Case insensitive matching
1298 var query = sQuery.toLowerCase();
1300 var query = sQuery.toLowerCase();
1299 var i = 0;
1301 var i = 0;
1300 var l = myUsers.length;
1302 var l = myUsers.length;
1301 var matches = [];
1303 var matches = [];
1302
1304
1303 // Match against each name of each contact
1305 // Match against each name of each contact
1304 for (; i < l; i++) {
1306 for (; i < l; i++) {
1305 var contact = myUsers[i];
1307 var contact = myUsers[i];
1306 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1308 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1307 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1309 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1308 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1310 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1309 matches[matches.length] = contact;
1311 matches[matches.length] = contact;
1310 }
1312 }
1311 }
1313 }
1312 return matches
1314 return matches
1313 };
1315 };
1314
1316
1315 //match all
1317 //match all
1316 var matchAll = function (sQuery) {
1318 var matchAll = function (sQuery) {
1317 return matchUsers(sQuery);
1319 return matchUsers(sQuery);
1318 };
1320 };
1319
1321
1320 // DataScheme for owner
1322 // DataScheme for owner
1321 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1323 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1322
1324
1323 ownerDS.responseSchema = {
1325 ownerDS.responseSchema = {
1324 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1326 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1325 };
1327 };
1326
1328
1327 // Instantiate AutoComplete for mentions
1329 // Instantiate AutoComplete for mentions
1328 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1330 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1329 ownerAC.useShadow = false;
1331 ownerAC.useShadow = false;
1330 ownerAC.resultTypeList = false;
1332 ownerAC.resultTypeList = false;
1331 ownerAC.suppressInputUpdate = true;
1333 ownerAC.suppressInputUpdate = true;
1332 ownerAC.animVert = false;
1334 ownerAC.animVert = false;
1333 ownerAC.animHoriz = false;
1335 ownerAC.animHoriz = false;
1334 ownerAC.animSpeed = 0.1;
1336 ownerAC.animSpeed = 0.1;
1335
1337
1336 // Helper highlight function for the formatter
1338 // Helper highlight function for the formatter
1337 var highlightMatch = function (full, snippet, matchindex) {
1339 var highlightMatch = function (full, snippet, matchindex) {
1338 return full.substring(0, matchindex)
1340 return full.substring(0, matchindex)
1339 + "<span class='match'>"
1341 + "<span class='match'>"
1340 + full.substr(matchindex, snippet.length)
1342 + full.substr(matchindex, snippet.length)
1341 + "</span>" + full.substring(matchindex + snippet.length);
1343 + "</span>" + full.substring(matchindex + snippet.length);
1342 };
1344 };
1343
1345
1344 // Custom formatter to highlight the matching letters
1346 // Custom formatter to highlight the matching letters
1345 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1347 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1346 var org_sQuery = sQuery;
1348 var org_sQuery = sQuery;
1347 if(this.dataSource.mentionQuery != null){
1349 if(this.dataSource.mentionQuery != null){
1348 sQuery = this.dataSource.mentionQuery;
1350 sQuery = this.dataSource.mentionQuery;
1349 }
1351 }
1350
1352
1351 var query = sQuery.toLowerCase();
1353 var query = sQuery.toLowerCase();
1352 var _gravatar = function(res, em, group){
1354 var _gravatar = function(res, em, group){
1353 if (group !== undefined){
1355 if (group !== undefined){
1354 em = '/images/icons/group.png'
1356 em = '/images/icons/group.png'
1355 }
1357 }
1356 var tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1358 var tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1357 return tmpl.format(em,res)
1359 return tmpl.format(em,res)
1358 }
1360 }
1359 if (oResultData.nname != undefined) {
1361 if (oResultData.nname != undefined) {
1360 var fname = oResultData.fname || "";
1362 var fname = oResultData.fname || "";
1361 var lname = oResultData.lname || "";
1363 var lname = oResultData.lname || "";
1362 var nname = oResultData.nname;
1364 var nname = oResultData.nname;
1363
1365
1364 // Guard against null value
1366 // Guard against null value
1365 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1367 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1366 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1368 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1367 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1369 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1368 displayfname, displaylname, displaynname;
1370 displayfname, displaylname, displaynname;
1369
1371
1370 if (fnameMatchIndex > -1) {
1372 if (fnameMatchIndex > -1) {
1371 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1373 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1372 } else {
1374 } else {
1373 displayfname = fname;
1375 displayfname = fname;
1374 }
1376 }
1375
1377
1376 if (lnameMatchIndex > -1) {
1378 if (lnameMatchIndex > -1) {
1377 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1379 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1378 } else {
1380 } else {
1379 displaylname = lname;
1381 displaylname = lname;
1380 }
1382 }
1381
1383
1382 if (nnameMatchIndex > -1) {
1384 if (nnameMatchIndex > -1) {
1383 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1385 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1384 } else {
1386 } else {
1385 displaynname = nname ? "(" + nname + ")" : "";
1387 displaynname = nname ? "(" + nname + ")" : "";
1386 }
1388 }
1387
1389
1388 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1390 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1389 } else {
1391 } else {
1390 return '';
1392 return '';
1391 }
1393 }
1392 };
1394 };
1393
1395
1394 if(ownerAC.itemSelectEvent){
1396 if(ownerAC.itemSelectEvent){
1395 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1397 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1396 var myAC = aArgs[0]; // reference back to the AC instance
1398 var myAC = aArgs[0]; // reference back to the AC instance
1397 var elLI = aArgs[1]; // reference to the selected LI element
1399 var elLI = aArgs[1]; // reference to the selected LI element
1398 var oData = aArgs[2]; // object literal of selected item's result data
1400 var oData = aArgs[2]; // object literal of selected item's result data
1399 //fill the autocomplete with value
1401 //fill the autocomplete with value
1400 if (oData.nname != undefined) {
1402 if (oData.nname != undefined) {
1401 //users
1403 //users
1402 //Replace the mention name with replaced
1404 //Replace the mention name with replaced
1403 var re = new RegExp();
1405 var re = new RegExp();
1404 var org = myAC.getInputEl().value;
1406 var org = myAC.getInputEl().value;
1405 var chunks = myAC.dataSource.chunks
1407 var chunks = myAC.dataSource.chunks
1406 // replace middle chunk(the search term) with actuall match
1408 // replace middle chunk(the search term) with actuall match
1407 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1409 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1408 '@'+oData.nname+' ');
1410 '@'+oData.nname+' ');
1409 myAC.getInputEl().value = chunks.join('')
1411 myAC.getInputEl().value = chunks.join('')
1410 myAC.getInputEl().focus(); // Y U NO WORK !?
1412 myAC.getInputEl().focus(); // Y U NO WORK !?
1411 } else {
1413 } else {
1412 //groups
1414 //groups
1413 myAC.getInputEl().value = oData.grname;
1415 myAC.getInputEl().value = oData.grname;
1414 $('#perm_new_member_type').val('users_group');
1416 $('#perm_new_member_type').val('users_group');
1415 }
1417 }
1416 });
1418 });
1417 }
1419 }
1418
1420
1419 // in this keybuffer we will gather current value of search !
1421 // in this keybuffer we will gather current value of search !
1420 // since we need to get this just when someone does `@` then we do the
1422 // since we need to get this just when someone does `@` then we do the
1421 // search
1423 // search
1422 ownerAC.dataSource.chunks = [];
1424 ownerAC.dataSource.chunks = [];
1423 ownerAC.dataSource.mentionQuery = null;
1425 ownerAC.dataSource.mentionQuery = null;
1424
1426
1425 ownerAC.get_mention = function(msg, max_pos) {
1427 ownerAC.get_mention = function(msg, max_pos) {
1426 var org = msg;
1428 var org = msg;
1427 // Must match utils2.py MENTIONS_REGEX.
1429 // Must match utils2.py MENTIONS_REGEX.
1428 // Only matching on string up to cursor, so it must end with $
1430 // Only matching on string up to cursor, so it must end with $
1429 var re = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$')
1431 var re = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$')
1430 var chunks = [];
1432 var chunks = [];
1431
1433
1432 // cut first chunk until current pos
1434 // cut first chunk until current pos
1433 var to_max = msg.substr(0, max_pos);
1435 var to_max = msg.substr(0, max_pos);
1434 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1436 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1435 var msg2 = to_max.substr(at_pos);
1437 var msg2 = to_max.substr(at_pos);
1436
1438
1437 chunks.push(org.substr(0,at_pos))// prefix chunk
1439 chunks.push(org.substr(0,at_pos))// prefix chunk
1438 chunks.push(msg2) // search chunk
1440 chunks.push(msg2) // search chunk
1439 chunks.push(org.substr(max_pos)) // postfix chunk
1441 chunks.push(org.substr(max_pos)) // postfix chunk
1440
1442
1441 // clean up msg2 for filtering and regex match
1443 // clean up msg2 for filtering and regex match
1442 var msg2 = msg2.lstrip(' ').lstrip('\n');
1444 var msg2 = msg2.lstrip(' ').lstrip('\n');
1443
1445
1444 if(re.test(msg2)){
1446 if(re.test(msg2)){
1445 var unam = re.exec(msg2)[1];
1447 var unam = re.exec(msg2)[1];
1446 return [unam, chunks];
1448 return [unam, chunks];
1447 }
1449 }
1448 return [null, null];
1450 return [null, null];
1449 };
1451 };
1450
1452
1451 var $divid = $('#'+divid);
1453 var $divid = $('#'+divid);
1452 $divid.keyup(function(e){
1454 $divid.keyup(function(e){
1453 var currentMessage = $divid.val();
1455 var currentMessage = $divid.val();
1454 var currentCaretPosition = $divid[0].selectionStart;
1456 var currentCaretPosition = $divid[0].selectionStart;
1455
1457
1456 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1458 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1457 var curr_search = null;
1459 var curr_search = null;
1458 if(unam[0]){
1460 if(unam[0]){
1459 curr_search = unam[0];
1461 curr_search = unam[0];
1460 }
1462 }
1461
1463
1462 ownerAC.dataSource.chunks = unam[1];
1464 ownerAC.dataSource.chunks = unam[1];
1463 ownerAC.dataSource.mentionQuery = curr_search;
1465 ownerAC.dataSource.mentionQuery = curr_search;
1464 });
1466 });
1465 }
1467 }
1466
1468
1467 var addReviewMember = function(id,fname,lname,nname,gravatar_link){
1469 var addReviewMember = function(id,fname,lname,nname,gravatar_link){
1468 var displayname = "{0} {1} ({2})".format(fname, lname, nname);
1470 var displayname = "{0} {1} ({2})".format(fname, lname, nname);
1469 var element = (
1471 var element = (
1470 ' <li id="reviewer_{2}">\n'+
1472 ' <li id="reviewer_{2}">\n'+
1471 ' <div class="reviewers_member">\n'+
1473 ' <div class="reviewers_member">\n'+
1472 ' <div class="reviewer_status tooltip" title="not_reviewed">\n'+
1474 ' <div class="reviewer_status tooltip" title="not_reviewed">\n'+
1473 ' <img src="/images/icons/flag_status_not_reviewed.png"/>\n'+
1475 ' <img src="/images/icons/flag_status_not_reviewed.png"/>\n'+
1474 ' </div>\n'+
1476 ' </div>\n'+
1475 ' <div class="reviewer_gravatar gravatar"><img alt="gravatar" src="{0}"/> </div>\n'+
1477 ' <div class="reviewer_gravatar gravatar"><img alt="gravatar" src="{0}"/> </div>\n'+
1476 ' <div style="float:left;">{1}</div>\n'+
1478 ' <div style="float:left;">{1}</div>\n'+
1477 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1479 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1478 ' <div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">\n'+
1480 ' <div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">\n'+
1479 ' <i class="icon-remove-sign" style="color: #FF4444;"></i>\n'+
1481 ' <i class="icon-remove-sign" style="color: #FF4444;"></i>\n'+
1480 ' </div> *\n'+
1482 ' </div> *\n'+
1481 ' </div>\n'+
1483 ' </div>\n'+
1482 ' </li>\n'
1484 ' </li>\n'
1483 ).format(gravatar_link, displayname, id);
1485 ).format(gravatar_link, displayname, id);
1484 // check if we don't have this ID already in
1486 // check if we don't have this ID already in
1485 var ids = [];
1487 var ids = [];
1486 $('#review_members').find('li').each(function() {
1488 $('#review_members').find('li').each(function() {
1487 ids.push(this.id);
1489 ids.push(this.id);
1488 });
1490 });
1489 if(ids.indexOf('reviewer_'+id) == -1){
1491 if(ids.indexOf('reviewer_'+id) == -1){
1490 //only add if it's not there
1492 //only add if it's not there
1491 $('#review_members').append(element);
1493 $('#review_members').append(element);
1492 }
1494 }
1493 }
1495 }
1494
1496
1495 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1497 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1496 var $li = $('#reviewer_{0}'.format(reviewer_id));
1498 var $li = $('#reviewer_{0}'.format(reviewer_id));
1497 $li.find('div div').css("text-decoration", "line-through");
1499 $li.find('div div').css("text-decoration", "line-through");
1498 $li.find('input').remove();
1500 $li.find('input').remove();
1499 $li.find('.reviewer_member_remove').remove();
1501 $li.find('.reviewer_member_remove').remove();
1500 }
1502 }
1501
1503
1502 /* handle "Save Changes" of addReviewMember and removeReviewMember on PR */
1504 /* handle "Save Changes" of addReviewMember and removeReviewMember on PR */
1503 var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){
1505 var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){
1504 if (reviewers_ids === undefined){
1506 if (reviewers_ids === undefined){
1505 var reviewers_ids = [];
1507 var reviewers_ids = [];
1506 $('#review_members').find('input').each(function(){
1508 $('#review_members').find('input').each(function(){
1507 reviewers_ids.push(this.value);
1509 reviewers_ids.push(this.value);
1508 });
1510 });
1509 }
1511 }
1510 var url = pyroutes.url('pullrequest_update', {"repo_name":repo_name,
1512 var url = pyroutes.url('pullrequest_update', {"repo_name":repo_name,
1511 "pull_request_id": pull_request_id});
1513 "pull_request_id": pull_request_id});
1512 var postData = {'_method':'put',
1514 var postData = {'_method':'put',
1513 'reviewers_ids': reviewers_ids};
1515 'reviewers_ids': reviewers_ids};
1514 var success = function(o){
1516 var success = function(o){
1515 window.location.reload();
1517 window.location.reload();
1516 }
1518 }
1517 ajaxPOST(url,postData,success);
1519 ajaxPOST(url,postData,success);
1518 }
1520 }
1519
1521
1520 /* activate auto completion of users and groups ... but only used for users as PR reviewers */
1522 /* activate auto completion of users and groups ... but only used for users as PR reviewers */
1521 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1523 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1522 var myUsers = users_list;
1524 var myUsers = users_list;
1523 var myGroups = groups_list;
1525 var myGroups = groups_list;
1524
1526
1525 // Define a custom search function for the DataSource of users
1527 // Define a custom search function for the DataSource of users
1526 var matchUsers = function (sQuery) {
1528 var matchUsers = function (sQuery) {
1527 // Case insensitive matching
1529 // Case insensitive matching
1528 var query = sQuery.toLowerCase();
1530 var query = sQuery.toLowerCase();
1529 var i = 0;
1531 var i = 0;
1530 var l = myUsers.length;
1532 var l = myUsers.length;
1531 var matches = [];
1533 var matches = [];
1532
1534
1533 // Match against each name of each contact
1535 // Match against each name of each contact
1534 for (; i < l; i++) {
1536 for (; i < l; i++) {
1535 var contact = myUsers[i];
1537 var contact = myUsers[i];
1536 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1538 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1537 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1539 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1538 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1540 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1539 matches[matches.length] = contact;
1541 matches[matches.length] = contact;
1540 }
1542 }
1541 }
1543 }
1542 return matches;
1544 return matches;
1543 };
1545 };
1544
1546
1545 // Define a custom search function for the DataSource of userGroups
1547 // Define a custom search function for the DataSource of userGroups
1546 var matchGroups = function (sQuery) {
1548 var matchGroups = function (sQuery) {
1547 // Case insensitive matching
1549 // Case insensitive matching
1548 var query = sQuery.toLowerCase();
1550 var query = sQuery.toLowerCase();
1549 var i = 0;
1551 var i = 0;
1550 var l = myGroups.length;
1552 var l = myGroups.length;
1551 var matches = [];
1553 var matches = [];
1552
1554
1553 // Match against each name of each contact
1555 // Match against each name of each contact
1554 for (; i < l; i++) {
1556 for (; i < l; i++) {
1555 matched_group = myGroups[i];
1557 matched_group = myGroups[i];
1556 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1558 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1557 matches[matches.length] = matched_group;
1559 matches[matches.length] = matched_group;
1558 }
1560 }
1559 }
1561 }
1560 return matches;
1562 return matches;
1561 };
1563 };
1562
1564
1563 //match all
1565 //match all
1564 var matchAll = function (sQuery) {
1566 var matchAll = function (sQuery) {
1565 return matchUsers(sQuery);
1567 return matchUsers(sQuery);
1566 };
1568 };
1567
1569
1568 // DataScheme for owner
1570 // DataScheme for owner
1569 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1571 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1570
1572
1571 ownerDS.responseSchema = {
1573 ownerDS.responseSchema = {
1572 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1574 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1573 };
1575 };
1574
1576
1575 // Instantiate AutoComplete for mentions
1577 // Instantiate AutoComplete for mentions
1576 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1578 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1577 reviewerAC.useShadow = false;
1579 reviewerAC.useShadow = false;
1578 reviewerAC.resultTypeList = false;
1580 reviewerAC.resultTypeList = false;
1579 reviewerAC.suppressInputUpdate = true;
1581 reviewerAC.suppressInputUpdate = true;
1580 reviewerAC.animVert = false;
1582 reviewerAC.animVert = false;
1581 reviewerAC.animHoriz = false;
1583 reviewerAC.animHoriz = false;
1582 reviewerAC.animSpeed = 0.1;
1584 reviewerAC.animSpeed = 0.1;
1583
1585
1584 // Helper highlight function for the formatter
1586 // Helper highlight function for the formatter
1585 var highlightMatch = function (full, snippet, matchindex) {
1587 var highlightMatch = function (full, snippet, matchindex) {
1586 return full.substring(0, matchindex)
1588 return full.substring(0, matchindex)
1587 + "<span class='match'>"
1589 + "<span class='match'>"
1588 + full.substr(matchindex, snippet.length)
1590 + full.substr(matchindex, snippet.length)
1589 + "</span>" + full.substring(matchindex + snippet.length);
1591 + "</span>" + full.substring(matchindex + snippet.length);
1590 };
1592 };
1591
1593
1592 // Custom formatter to highlight the matching letters
1594 // Custom formatter to highlight the matching letters
1593 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1595 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1594 var org_sQuery = sQuery;
1596 var org_sQuery = sQuery;
1595 if(this.dataSource.mentionQuery != null){
1597 if(this.dataSource.mentionQuery != null){
1596 sQuery = this.dataSource.mentionQuery;
1598 sQuery = this.dataSource.mentionQuery;
1597 }
1599 }
1598
1600
1599 var query = sQuery.toLowerCase();
1601 var query = sQuery.toLowerCase();
1600 var _gravatar = function(res, em, group){
1602 var _gravatar = function(res, em, group){
1601 if (group !== undefined){
1603 if (group !== undefined){
1602 em = '/images/icons/group.png'
1604 em = '/images/icons/group.png'
1603 }
1605 }
1604 var tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1606 var tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1605 return tmpl.format(em,res)
1607 return tmpl.format(em,res)
1606 }
1608 }
1607 if (oResultData.nname != undefined) {
1609 if (oResultData.nname != undefined) {
1608 var fname = oResultData.fname || "";
1610 var fname = oResultData.fname || "";
1609 var lname = oResultData.lname || "";
1611 var lname = oResultData.lname || "";
1610 var nname = oResultData.nname;
1612 var nname = oResultData.nname;
1611
1613
1612 // Guard against null value
1614 // Guard against null value
1613 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1615 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1614 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1616 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1615 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1617 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1616 displayfname, displaylname, displaynname;
1618 displayfname, displaylname, displaynname;
1617
1619
1618 if (fnameMatchIndex > -1) {
1620 if (fnameMatchIndex > -1) {
1619 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1621 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1620 } else {
1622 } else {
1621 displayfname = fname;
1623 displayfname = fname;
1622 }
1624 }
1623
1625
1624 if (lnameMatchIndex > -1) {
1626 if (lnameMatchIndex > -1) {
1625 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1627 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1626 } else {
1628 } else {
1627 displaylname = lname;
1629 displaylname = lname;
1628 }
1630 }
1629
1631
1630 if (nnameMatchIndex > -1) {
1632 if (nnameMatchIndex > -1) {
1631 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1633 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1632 } else {
1634 } else {
1633 displaynname = nname ? "(" + nname + ")" : "";
1635 displaynname = nname ? "(" + nname + ")" : "";
1634 }
1636 }
1635
1637
1636 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1638 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1637 } else {
1639 } else {
1638 return '';
1640 return '';
1639 }
1641 }
1640 };
1642 };
1641
1643
1642 //members cache to catch duplicates
1644 //members cache to catch duplicates
1643 reviewerAC.dataSource.cache = [];
1645 reviewerAC.dataSource.cache = [];
1644 // hack into select event
1646 // hack into select event
1645 if(reviewerAC.itemSelectEvent){
1647 if(reviewerAC.itemSelectEvent){
1646 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1648 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1647
1649
1648 var myAC = aArgs[0]; // reference back to the AC instance
1650 var myAC = aArgs[0]; // reference back to the AC instance
1649 var elLI = aArgs[1]; // reference to the selected LI element
1651 var elLI = aArgs[1]; // reference to the selected LI element
1650 var oData = aArgs[2]; // object literal of selected item's result data
1652 var oData = aArgs[2]; // object literal of selected item's result data
1651
1653
1652 //fill the autocomplete with value
1654 //fill the autocomplete with value
1653 if (oData.nname != undefined) {
1655 if (oData.nname != undefined) {
1654 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1656 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1655 oData.gravatar_lnk);
1657 oData.gravatar_lnk);
1656 myAC.dataSource.cache.push(oData.id);
1658 myAC.dataSource.cache.push(oData.id);
1657 $('#user').val('');
1659 $('#user').val('');
1658 }
1660 }
1659 });
1661 });
1660 }
1662 }
1661 }
1663 }
1662
1664
1663 /**
1665 /**
1664 * Activate .quick_repo_menu
1666 * Activate .quick_repo_menu
1665 */
1667 */
1666 var quick_repo_menu = function(){
1668 var quick_repo_menu = function(){
1667 $(".quick_repo_menu").mouseenter(function(e) {
1669 $(".quick_repo_menu").mouseenter(function(e) {
1668 var $menu = $(e.currentTarget).children().first().children().first();
1670 var $menu = $(e.currentTarget).children().first().children().first();
1669 if($menu.hasClass('hidden')){
1671 if($menu.hasClass('hidden')){
1670 $menu.removeClass('hidden').addClass('active');
1672 $menu.removeClass('hidden').addClass('active');
1671 $(e.currentTarget).removeClass('hidden').addClass('active');
1673 $(e.currentTarget).removeClass('hidden').addClass('active');
1672 }
1674 }
1673 })
1675 })
1674 $(".quick_repo_menu").mouseleave(function(e) {
1676 $(".quick_repo_menu").mouseleave(function(e) {
1675 var $menu = $(e.currentTarget).children().first().children().first();
1677 var $menu = $(e.currentTarget).children().first().children().first();
1676 if($menu.hasClass('active')){
1678 if($menu.hasClass('active')){
1677 $menu.removeClass('active').addClass('hidden');
1679 $menu.removeClass('active').addClass('hidden');
1678 $(e.currentTarget).removeClass('active').addClass('hidden');
1680 $(e.currentTarget).removeClass('active').addClass('hidden');
1679 }
1681 }
1680 })
1682 })
1681 };
1683 };
1682
1684
1683
1685
1684 /**
1686 /**
1685 * TABLE SORTING
1687 * TABLE SORTING
1686 */
1688 */
1687
1689
1688 var revisionSort = function(a, b, desc, field) {
1690 var revisionSort = function(a, b, desc, field) {
1689 var a_ = parseInt(a.getData('last_rev_raw') || 0);
1691 var a_ = parseInt(a.getData('last_rev_raw') || 0);
1690 var b_ = parseInt(b.getData('last_rev_raw') || 0);
1692 var b_ = parseInt(b.getData('last_rev_raw') || 0);
1691
1693
1692 return YAHOO.util.Sort.compare(a_, b_, desc);
1694 return YAHOO.util.Sort.compare(a_, b_, desc);
1693 };
1695 };
1694
1696
1695 var ageSort = function(a, b, desc, field) {
1697 var ageSort = function(a, b, desc, field) {
1696 // data is like: <span class="tooltip" date="2014-06-04 18:18:55.325474" title="Wed, 04 Jun 2014 18:18:55">1 day and 23 hours ago</span>
1698 // data is like: <span class="tooltip" date="2014-06-04 18:18:55.325474" title="Wed, 04 Jun 2014 18:18:55">1 day and 23 hours ago</span>
1697 var a_ = $(a.getData(field)).attr('date');
1699 var a_ = $(a.getData(field)).attr('date');
1698 var b_ = $(b.getData(field)).attr('date');
1700 var b_ = $(b.getData(field)).attr('date');
1699
1701
1700 return YAHOO.util.Sort.compare(a_, b_, desc);
1702 return YAHOO.util.Sort.compare(a_, b_, desc);
1701 };
1703 };
1702
1704
1703 var lastLoginSort = function(a, b, desc, field) {
1705 var lastLoginSort = function(a, b, desc, field) {
1704 var a_ = parseFloat(a.getData('last_login_raw') || 0);
1706 var a_ = parseFloat(a.getData('last_login_raw') || 0);
1705 var b_ = parseFloat(b.getData('last_login_raw') || 0);
1707 var b_ = parseFloat(b.getData('last_login_raw') || 0);
1706
1708
1707 return YAHOO.util.Sort.compare(a_, b_, desc);
1709 return YAHOO.util.Sort.compare(a_, b_, desc);
1708 };
1710 };
1709
1711
1710 var nameSort = function(a, b, desc, field) {
1712 var nameSort = function(a, b, desc, field) {
1711 var a_ = a.getData('raw_name') || 0;
1713 var a_ = a.getData('raw_name') || 0;
1712 var b_ = b.getData('raw_name') || 0;
1714 var b_ = b.getData('raw_name') || 0;
1713
1715
1714 return YAHOO.util.Sort.compare(a_, b_, desc);
1716 return YAHOO.util.Sort.compare(a_, b_, desc);
1715 };
1717 };
1716
1718
1717 var dateSort = function(a, b, desc, field) {
1719 var dateSort = function(a, b, desc, field) {
1718 var a_ = parseFloat(a.getData('raw_date') || 0);
1720 var a_ = parseFloat(a.getData('raw_date') || 0);
1719 var b_ = parseFloat(b.getData('raw_date') || 0);
1721 var b_ = parseFloat(b.getData('raw_date') || 0);
1720
1722
1721 return YAHOO.util.Sort.compare(a_, b_, desc);
1723 return YAHOO.util.Sort.compare(a_, b_, desc);
1722 };
1724 };
1723
1725
1724 var addPermAction = function(_html, users_list, groups_list){
1726 var addPermAction = function(_html, users_list, groups_list){
1725 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1727 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1726 var next_id = $('.new_members').length;
1728 var next_id = $('.new_members').length;
1727 $last_node.before($('<tr class="new_members">').append(_html.format(next_id)));
1729 $last_node.before($('<tr class="new_members">').append(_html.format(next_id)));
1728 _MembersAutoComplete("perm_new_member_name_"+next_id,
1730 _MembersAutoComplete("perm_new_member_name_"+next_id,
1729 "perm_container_"+next_id, users_list, groups_list);
1731 "perm_container_"+next_id, users_list, groups_list);
1730 }
1732 }
1731
1733
1732 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1734 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1733 var callback = {
1735 var callback = {
1734 success: function (o) {
1736 success: function (o) {
1735 $('#' + field_id).remove();
1737 $('#' + field_id).remove();
1736 },
1738 },
1737 failure: function (o) {
1739 failure: function (o) {
1738 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1740 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1739 }
1741 }
1740 };
1742 };
1741 var query_params = {
1743 var query_params = {
1742 '_method': 'delete'
1744 '_method': 'delete'
1743 }
1745 }
1744 // put extra data into POST
1746 // put extra data into POST
1745 if (extra_data !== undefined && (typeof extra_data === 'object')){
1747 if (extra_data !== undefined && (typeof extra_data === 'object')){
1746 for(var k in extra_data){
1748 for(var k in extra_data){
1747 query_params[k] = extra_data[k];
1749 query_params[k] = extra_data[k];
1748 }
1750 }
1749 }
1751 }
1750
1752
1751 if (obj_type=='user'){
1753 if (obj_type=='user'){
1752 query_params['user_id'] = obj_id;
1754 query_params['user_id'] = obj_id;
1753 query_params['obj_type'] = 'user';
1755 query_params['obj_type'] = 'user';
1754 }
1756 }
1755 else if (obj_type=='user_group'){
1757 else if (obj_type=='user_group'){
1756 query_params['user_group_id'] = obj_id;
1758 query_params['user_group_id'] = obj_id;
1757 query_params['obj_type'] = 'user_group';
1759 query_params['obj_type'] = 'user_group';
1758 }
1760 }
1759
1761
1760 var request = YAHOO.util.Connect.asyncRequest('POST', url, callback,
1762 var request = YAHOO.util.Connect.asyncRequest('POST', url, callback,
1761 _toQueryString(query_params));
1763 _toQueryString(query_params));
1762 };
1764 };
1763
1765
1764 /* Multi selectors */
1766 /* Multi selectors */
1765
1767
1766 var MultiSelectWidget = function(selected_id, available_id, form_id){
1768 var MultiSelectWidget = function(selected_id, available_id, form_id){
1767 var $availableselect = $('#' + available_id);
1769 var $availableselect = $('#' + available_id);
1768 var $selectedselect = $('#' + selected_id);
1770 var $selectedselect = $('#' + selected_id);
1769
1771
1770 //fill available only with those not in selected
1772 //fill available only with those not in selected
1771 var $selectedoptions = $selectedselect.children('option');
1773 var $selectedoptions = $selectedselect.children('option');
1772 $availableselect.children('option').filter(function(i, e){
1774 $availableselect.children('option').filter(function(i, e){
1773 for(var j = 0, node; node = $selectedoptions[j]; j++){
1775 for(var j = 0, node; node = $selectedoptions[j]; j++){
1774 if(node.value == e.value){
1776 if(node.value == e.value){
1775 return true;
1777 return true;
1776 }
1778 }
1777 }
1779 }
1778 return false;
1780 return false;
1779 }).remove();
1781 }).remove();
1780
1782
1781 $('#add_element').click(function(e){
1783 $('#add_element').click(function(e){
1782 $selectedselect.append($availableselect.children('option:selected'));
1784 $selectedselect.append($availableselect.children('option:selected'));
1783 });
1785 });
1784 $('#remove_element').click(function(e){
1786 $('#remove_element').click(function(e){
1785 $availableselect.append($selectedselect.children('option:selected'));
1787 $availableselect.append($selectedselect.children('option:selected'));
1786 });
1788 });
1787 $('#add_all_elements').click(function(e){
1789 $('#add_all_elements').click(function(e){
1788 $selectedselect.append($availableselect.children('option'));
1790 $selectedselect.append($availableselect.children('option'));
1789 });
1791 });
1790 $('#remove_all_elements').click(function(e){
1792 $('#remove_all_elements').click(function(e){
1791 $availableselect.append($selectedselect.children('option'));
1793 $availableselect.append($selectedselect.children('option'));
1792 });
1794 });
1793
1795
1794 $('#'+form_id).submit(function(){
1796 $('#'+form_id).submit(function(){
1795 $selectedselect.children('option').each(function(i, e){
1797 $selectedselect.children('option').each(function(i, e){
1796 e.selected = 'selected';
1798 e.selected = 'selected';
1797 });
1799 });
1798 });
1800 });
1799 }
1801 }
1800
1802
1801 // custom paginator
1803 // custom paginator
1802 var YUI_paginator = function(links_per_page, containers){
1804 var YUI_paginator = function(links_per_page, containers){
1803
1805
1804 (function () {
1806 (function () {
1805
1807
1806 var Paginator = YAHOO.widget.Paginator,
1808 var Paginator = YAHOO.widget.Paginator,
1807 l = YAHOO.lang,
1809 l = YAHOO.lang,
1808 setId = YAHOO.util.Dom.generateId;
1810 setId = YAHOO.util.Dom.generateId;
1809
1811
1810 Paginator.ui.MyFirstPageLink = function (p) {
1812 Paginator.ui.MyFirstPageLink = function (p) {
1811 this.paginator = p;
1813 this.paginator = p;
1812
1814
1813 p.subscribe('recordOffsetChange',this.update,this,true);
1815 p.subscribe('recordOffsetChange',this.update,this,true);
1814 p.subscribe('rowsPerPageChange',this.update,this,true);
1816 p.subscribe('rowsPerPageChange',this.update,this,true);
1815 p.subscribe('totalRecordsChange',this.update,this,true);
1817 p.subscribe('totalRecordsChange',this.update,this,true);
1816 p.subscribe('destroy',this.destroy,this,true);
1818 p.subscribe('destroy',this.destroy,this,true);
1817
1819
1818 // TODO: make this work
1820 // TODO: make this work
1819 p.subscribe('firstPageLinkLabelChange',this.update,this,true);
1821 p.subscribe('firstPageLinkLabelChange',this.update,this,true);
1820 p.subscribe('firstPageLinkClassChange',this.update,this,true);
1822 p.subscribe('firstPageLinkClassChange',this.update,this,true);
1821 };
1823 };
1822
1824
1823 Paginator.ui.MyFirstPageLink.init = function (p) {
1825 Paginator.ui.MyFirstPageLink.init = function (p) {
1824 p.setAttributeConfig('firstPageLinkLabel', {
1826 p.setAttributeConfig('firstPageLinkLabel', {
1825 value : 1,
1827 value : 1,
1826 validator : l.isString
1828 validator : l.isString
1827 });
1829 });
1828 p.setAttributeConfig('firstPageLinkClass', {
1830 p.setAttributeConfig('firstPageLinkClass', {
1829 value : 'yui-pg-first',
1831 value : 'yui-pg-first',
1830 validator : l.isString
1832 validator : l.isString
1831 });
1833 });
1832 p.setAttributeConfig('firstPageLinkTitle', {
1834 p.setAttributeConfig('firstPageLinkTitle', {
1833 value : 'First Page',
1835 value : 'First Page',
1834 validator : l.isString
1836 validator : l.isString
1835 });
1837 });
1836 };
1838 };
1837
1839
1838 // Instance members and methods
1840 // Instance members and methods
1839 Paginator.ui.MyFirstPageLink.prototype = {
1841 Paginator.ui.MyFirstPageLink.prototype = {
1840 current : null,
1842 current : null,
1841 leftmost_page: null,
1843 leftmost_page: null,
1842 rightmost_page: null,
1844 rightmost_page: null,
1843 link : null,
1845 link : null,
1844 span : null,
1846 span : null,
1845 dotdot : null,
1847 dotdot : null,
1846 getPos : function(cur_page, max_page, items){
1848 getPos : function(cur_page, max_page, items){
1847 var edge = parseInt(items / 2) + 1;
1849 var edge = parseInt(items / 2) + 1;
1848 if (cur_page <= edge){
1850 if (cur_page <= edge){
1849 var radius = Math.max(parseInt(items / 2), items - cur_page);
1851 var radius = Math.max(parseInt(items / 2), items - cur_page);
1850 }
1852 }
1851 else if ((max_page - cur_page) < edge) {
1853 else if ((max_page - cur_page) < edge) {
1852 var radius = (items - 1) - (max_page - cur_page);
1854 var radius = (items - 1) - (max_page - cur_page);
1853 }
1855 }
1854 else{
1856 else{
1855 var radius = parseInt(items / 2);
1857 var radius = parseInt(items / 2);
1856 }
1858 }
1857
1859
1858 var left = Math.max(1, (cur_page - (radius)))
1860 var left = Math.max(1, (cur_page - (radius)))
1859 var right = Math.min(max_page, cur_page + (radius))
1861 var right = Math.min(max_page, cur_page + (radius))
1860 return [left, cur_page, right]
1862 return [left, cur_page, right]
1861 },
1863 },
1862 render : function (id_base) {
1864 render : function (id_base) {
1863 var p = this.paginator,
1865 var p = this.paginator,
1864 c = p.get('firstPageLinkClass'),
1866 c = p.get('firstPageLinkClass'),
1865 label = p.get('firstPageLinkLabel'),
1867 label = p.get('firstPageLinkLabel'),
1866 title = p.get('firstPageLinkTitle');
1868 title = p.get('firstPageLinkTitle');
1867
1869
1868 this.link = document.createElement('a');
1870 this.link = document.createElement('a');
1869 this.span = document.createElement('span');
1871 this.span = document.createElement('span');
1870 $(this.span).hide();
1872 $(this.span).hide();
1871
1873
1872 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
1874 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
1873 this.leftmost_page = _pos[0];
1875 this.leftmost_page = _pos[0];
1874 this.rightmost_page = _pos[2];
1876 this.rightmost_page = _pos[2];
1875
1877
1876 setId(this.link, id_base + '-first-link');
1878 setId(this.link, id_base + '-first-link');
1877 this.link.href = '#';
1879 this.link.href = '#';
1878 this.link.className = c;
1880 this.link.className = c;
1879 this.link.innerHTML = label;
1881 this.link.innerHTML = label;
1880 this.link.title = title;
1882 this.link.title = title;
1881 YUE.on(this.link,'click',this.onClick,this,true);
1883 YUE.on(this.link,'click',this.onClick,this,true);
1882
1884
1883 setId(this.span, id_base + '-first-span');
1885 setId(this.span, id_base + '-first-span');
1884 this.span.className = c;
1886 this.span.className = c;
1885 this.span.innerHTML = label;
1887 this.span.innerHTML = label;
1886
1888
1887 this.current = p.getCurrentPage() > 1 ? this.link : this.span;
1889 this.current = p.getCurrentPage() > 1 ? this.link : this.span;
1888 return this.current;
1890 return this.current;
1889 },
1891 },
1890 update : function (e) {
1892 update : function (e) {
1891 var p = this.paginator;
1893 var p = this.paginator;
1892 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
1894 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
1893 this.leftmost_page = _pos[0];
1895 this.leftmost_page = _pos[0];
1894 this.rightmost_page = _pos[2];
1896 this.rightmost_page = _pos[2];
1895
1897
1896 if (e && e.prevValue === e.newValue) {
1898 if (e && e.prevValue === e.newValue) {
1897 return;
1899 return;
1898 }
1900 }
1899
1901
1900 var par = this.current ? this.current.parentNode : null;
1902 var par = this.current ? this.current.parentNode : null;
1901 if (this.leftmost_page > 1) {
1903 if (this.leftmost_page > 1) {
1902 if (par && this.current === this.span) {
1904 if (par && this.current === this.span) {
1903 par.replaceChild(this.link,this.current);
1905 par.replaceChild(this.link,this.current);
1904 this.current = this.link;
1906 this.current = this.link;
1905 }
1907 }
1906 } else {
1908 } else {
1907 if (par && this.current === this.link) {
1909 if (par && this.current === this.link) {
1908 par.replaceChild(this.span,this.current);
1910 par.replaceChild(this.span,this.current);
1909 this.current = this.span;
1911 this.current = this.span;
1910 }
1912 }
1911 }
1913 }
1912 },
1914 },
1913 destroy : function () {
1915 destroy : function () {
1914 YUE.purgeElement(this.link);
1916 YUE.purgeElement(this.link);
1915 this.current.parentNode.removeChild(this.current);
1917 this.current.parentNode.removeChild(this.current);
1916 this.link = this.span = null;
1918 this.link = this.span = null;
1917 },
1919 },
1918 onClick : function (e) {
1920 onClick : function (e) {
1919 YUE.stopEvent(e);
1921 YUE.stopEvent(e);
1920 this.paginator.setPage(1);
1922 this.paginator.setPage(1);
1921 }
1923 }
1922 };
1924 };
1923
1925
1924 })();
1926 })();
1925
1927
1926 (function () {
1928 (function () {
1927
1929
1928 var Paginator = YAHOO.widget.Paginator,
1930 var Paginator = YAHOO.widget.Paginator,
1929 l = YAHOO.lang,
1931 l = YAHOO.lang,
1930 setId = YAHOO.util.Dom.generateId;
1932 setId = YAHOO.util.Dom.generateId;
1931
1933
1932 Paginator.ui.MyLastPageLink = function (p) {
1934 Paginator.ui.MyLastPageLink = function (p) {
1933 this.paginator = p;
1935 this.paginator = p;
1934
1936
1935 p.subscribe('recordOffsetChange',this.update,this,true);
1937 p.subscribe('recordOffsetChange',this.update,this,true);
1936 p.subscribe('rowsPerPageChange',this.update,this,true);
1938 p.subscribe('rowsPerPageChange',this.update,this,true);
1937 p.subscribe('totalRecordsChange',this.update,this,true);
1939 p.subscribe('totalRecordsChange',this.update,this,true);
1938 p.subscribe('destroy',this.destroy,this,true);
1940 p.subscribe('destroy',this.destroy,this,true);
1939
1941
1940 // TODO: make this work
1942 // TODO: make this work
1941 p.subscribe('lastPageLinkLabelChange',this.update,this,true);
1943 p.subscribe('lastPageLinkLabelChange',this.update,this,true);
1942 p.subscribe('lastPageLinkClassChange', this.update,this,true);
1944 p.subscribe('lastPageLinkClassChange', this.update,this,true);
1943 };
1945 };
1944
1946
1945 Paginator.ui.MyLastPageLink.init = function (p) {
1947 Paginator.ui.MyLastPageLink.init = function (p) {
1946 p.setAttributeConfig('lastPageLinkLabel', {
1948 p.setAttributeConfig('lastPageLinkLabel', {
1947 value : -1,
1949 value : -1,
1948 validator : l.isString
1950 validator : l.isString
1949 });
1951 });
1950 p.setAttributeConfig('lastPageLinkClass', {
1952 p.setAttributeConfig('lastPageLinkClass', {
1951 value : 'yui-pg-last',
1953 value : 'yui-pg-last',
1952 validator : l.isString
1954 validator : l.isString
1953 });
1955 });
1954 p.setAttributeConfig('lastPageLinkTitle', {
1956 p.setAttributeConfig('lastPageLinkTitle', {
1955 value : 'Last Page',
1957 value : 'Last Page',
1956 validator : l.isString
1958 validator : l.isString
1957 });
1959 });
1958
1960
1959 };
1961 };
1960
1962
1961 Paginator.ui.MyLastPageLink.prototype = {
1963 Paginator.ui.MyLastPageLink.prototype = {
1962
1964
1963 current : null,
1965 current : null,
1964 leftmost_page: null,
1966 leftmost_page: null,
1965 rightmost_page: null,
1967 rightmost_page: null,
1966 link : null,
1968 link : null,
1967 span : null,
1969 span : null,
1968 dotdot : null,
1970 dotdot : null,
1969 na : null,
1971 na : null,
1970 getPos : function(cur_page, max_page, items){
1972 getPos : function(cur_page, max_page, items){
1971 var edge = parseInt(items / 2) + 1;
1973 var edge = parseInt(items / 2) + 1;
1972 if (cur_page <= edge){
1974 if (cur_page <= edge){
1973 var radius = Math.max(parseInt(items / 2), items - cur_page);
1975 var radius = Math.max(parseInt(items / 2), items - cur_page);
1974 }
1976 }
1975 else if ((max_page - cur_page) < edge) {
1977 else if ((max_page - cur_page) < edge) {
1976 var radius = (items - 1) - (max_page - cur_page);
1978 var radius = (items - 1) - (max_page - cur_page);
1977 }
1979 }
1978 else{
1980 else{
1979 var radius = parseInt(items / 2);
1981 var radius = parseInt(items / 2);
1980 }
1982 }
1981
1983
1982 var left = Math.max(1, (cur_page - (radius)))
1984 var left = Math.max(1, (cur_page - (radius)))
1983 var right = Math.min(max_page, cur_page + (radius))
1985 var right = Math.min(max_page, cur_page + (radius))
1984 return [left, cur_page, right]
1986 return [left, cur_page, right]
1985 },
1987 },
1986 render : function (id_base) {
1988 render : function (id_base) {
1987 var p = this.paginator,
1989 var p = this.paginator,
1988 c = p.get('lastPageLinkClass'),
1990 c = p.get('lastPageLinkClass'),
1989 label = p.get('lastPageLinkLabel'),
1991 label = p.get('lastPageLinkLabel'),
1990 last = p.getTotalPages(),
1992 last = p.getTotalPages(),
1991 title = p.get('lastPageLinkTitle');
1993 title = p.get('lastPageLinkTitle');
1992
1994
1993 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
1995 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
1994 this.leftmost_page = _pos[0];
1996 this.leftmost_page = _pos[0];
1995 this.rightmost_page = _pos[2];
1997 this.rightmost_page = _pos[2];
1996
1998
1997 this.link = document.createElement('a');
1999 this.link = document.createElement('a');
1998 this.span = document.createElement('span');
2000 this.span = document.createElement('span');
1999 $(this.span).hide();
2001 $(this.span).hide();
2000
2002
2001 this.na = this.span.cloneNode(false);
2003 this.na = this.span.cloneNode(false);
2002
2004
2003 setId(this.link, id_base + '-last-link');
2005 setId(this.link, id_base + '-last-link');
2004 this.link.href = '#';
2006 this.link.href = '#';
2005 this.link.className = c;
2007 this.link.className = c;
2006 this.link.innerHTML = label;
2008 this.link.innerHTML = label;
2007 this.link.title = title;
2009 this.link.title = title;
2008 YUE.on(this.link,'click',this.onClick,this,true);
2010 YUE.on(this.link,'click',this.onClick,this,true);
2009
2011
2010 setId(this.span, id_base + '-last-span');
2012 setId(this.span, id_base + '-last-span');
2011 this.span.className = c;
2013 this.span.className = c;
2012 this.span.innerHTML = label;
2014 this.span.innerHTML = label;
2013
2015
2014 setId(this.na, id_base + '-last-na');
2016 setId(this.na, id_base + '-last-na');
2015
2017
2016 if (this.rightmost_page < p.getTotalPages()){
2018 if (this.rightmost_page < p.getTotalPages()){
2017 this.current = this.link;
2019 this.current = this.link;
2018 }
2020 }
2019 else{
2021 else{
2020 this.current = this.span;
2022 this.current = this.span;
2021 }
2023 }
2022
2024
2023 this.current.innerHTML = p.getTotalPages();
2025 this.current.innerHTML = p.getTotalPages();
2024 return this.current;
2026 return this.current;
2025 },
2027 },
2026
2028
2027 update : function (e) {
2029 update : function (e) {
2028 var p = this.paginator;
2030 var p = this.paginator;
2029
2031
2030 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
2032 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
2031 this.leftmost_page = _pos[0];
2033 this.leftmost_page = _pos[0];
2032 this.rightmost_page = _pos[2];
2034 this.rightmost_page = _pos[2];
2033
2035
2034 if (e && e.prevValue === e.newValue) {
2036 if (e && e.prevValue === e.newValue) {
2035 return;
2037 return;
2036 }
2038 }
2037
2039
2038 var par = this.current ? this.current.parentNode : null,
2040 var par = this.current ? this.current.parentNode : null,
2039 after = this.link;
2041 after = this.link;
2040 if (par) {
2042 if (par) {
2041
2043
2042 // only show the last page if the rightmost one is
2044 // only show the last page if the rightmost one is
2043 // lower, so we don't have doubled entries at the end
2045 // lower, so we don't have doubled entries at the end
2044 if (!(this.rightmost_page < p.getTotalPages())){
2046 if (!(this.rightmost_page < p.getTotalPages())){
2045 after = this.span
2047 after = this.span
2046 }
2048 }
2047
2049
2048 if (this.current !== after) {
2050 if (this.current !== after) {
2049 par.replaceChild(after,this.current);
2051 par.replaceChild(after,this.current);
2050 this.current = after;
2052 this.current = after;
2051 }
2053 }
2052 }
2054 }
2053 this.current.innerHTML = this.paginator.getTotalPages();
2055 this.current.innerHTML = this.paginator.getTotalPages();
2054
2056
2055 },
2057 },
2056 destroy : function () {
2058 destroy : function () {
2057 YUE.purgeElement(this.link);
2059 YUE.purgeElement(this.link);
2058 this.current.parentNode.removeChild(this.current);
2060 this.current.parentNode.removeChild(this.current);
2059 this.link = this.span = null;
2061 this.link = this.span = null;
2060 },
2062 },
2061 onClick : function (e) {
2063 onClick : function (e) {
2062 YUE.stopEvent(e);
2064 YUE.stopEvent(e);
2063 this.paginator.setPage(this.paginator.getTotalPages());
2065 this.paginator.setPage(this.paginator.getTotalPages());
2064 }
2066 }
2065 };
2067 };
2066
2068
2067 })();
2069 })();
2068
2070
2069 var pagi = new YAHOO.widget.Paginator({
2071 var pagi = new YAHOO.widget.Paginator({
2070 rowsPerPage: links_per_page,
2072 rowsPerPage: links_per_page,
2071 alwaysVisible: false,
2073 alwaysVisible: false,
2072 template : "{PreviousPageLink} {MyFirstPageLink} {PageLinks} {MyLastPageLink} {NextPageLink}",
2074 template : "{PreviousPageLink} {MyFirstPageLink} {PageLinks} {MyLastPageLink} {NextPageLink}",
2073 pageLinks: 5,
2075 pageLinks: 5,
2074 containerClass: 'pagination-wh',
2076 containerClass: 'pagination-wh',
2075 currentPageClass: 'pager_curpage',
2077 currentPageClass: 'pager_curpage',
2076 pageLinkClass: 'pager_link',
2078 pageLinkClass: 'pager_link',
2077 nextPageLinkLabel: '&gt;',
2079 nextPageLinkLabel: '&gt;',
2078 previousPageLinkLabel: '&lt;',
2080 previousPageLinkLabel: '&lt;',
2079 containers:containers
2081 containers:containers
2080 })
2082 })
2081
2083
2082 return pagi
2084 return pagi
2083 }
2085 }
2084
2086
2085 var YUI_datatable = function(data, fields, columns, countnode, sortkey, rows){
2087 var YUI_datatable = function(data, fields, columns, countnode, sortkey, rows){
2086 var myDataSource = new YAHOO.util.DataSource(data);
2088 var myDataSource = new YAHOO.util.DataSource(data);
2087 myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
2089 myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
2088 myDataSource.responseSchema = {
2090 myDataSource.responseSchema = {
2089 resultsList: "records",
2091 resultsList: "records",
2090 fields: fields
2092 fields: fields
2091 };
2093 };
2092 myDataSource.doBeforeCallback = function(req, raw, res, cb) {
2094 myDataSource.doBeforeCallback = function(req, raw, res, cb) {
2093 // This is the filter function
2095 // This is the filter function
2094 var data = res.results || [],
2096 var data = res.results || [],
2095 filtered = [],
2097 filtered = [],
2096 i, l;
2098 i, l;
2097
2099
2098 if (req) {
2100 if (req) {
2099 req = req.toLowerCase();
2101 req = req.toLowerCase();
2100 for (i = 0; i<data.length; i++) {
2102 for (i = 0; i<data.length; i++) {
2101 var pos = data[i].raw_name.toLowerCase().indexOf(req)
2103 var pos = data[i].raw_name.toLowerCase().indexOf(req)
2102 if (pos != -1) {
2104 if (pos != -1) {
2103 filtered.push(data[i]);
2105 filtered.push(data[i]);
2104 }
2106 }
2105 }
2107 }
2106 res.results = filtered;
2108 res.results = filtered;
2107 }
2109 }
2108 $(countnode).html(res.results.length);
2110 $(countnode).html(res.results.length);
2109 return res;
2111 return res;
2110 }
2112 }
2111
2113
2112 var myDataTable = new YAHOO.widget.DataTable("datatable_list_wrap", columns, myDataSource, {
2114 var myDataTable = new YAHOO.widget.DataTable("datatable_list_wrap", columns, myDataSource, {
2113 sortedBy: {key:sortkey, dir:"asc"},
2115 sortedBy: {key:sortkey, dir:"asc"},
2114 paginator: YUI_paginator(rows !== undefined && rows ? rows : 25, ['user-paginator']),
2116 paginator: YUI_paginator(rows !== undefined && rows ? rows : 25, ['user-paginator']),
2115 MSG_SORTASC: _TM['MSG_SORTASC'],
2117 MSG_SORTASC: _TM['MSG_SORTASC'],
2116 MSG_SORTDESC: _TM['MSG_SORTDESC'],
2118 MSG_SORTDESC: _TM['MSG_SORTDESC'],
2117 MSG_EMPTY: _TM['MSG_EMPTY'],
2119 MSG_EMPTY: _TM['MSG_EMPTY'],
2118 MSG_ERROR: _TM['MSG_ERROR'],
2120 MSG_ERROR: _TM['MSG_ERROR'],
2119 MSG_LOADING: _TM['MSG_LOADING']
2121 MSG_LOADING: _TM['MSG_LOADING']
2120 });
2122 });
2121 myDataTable.subscribe('postRenderEvent',function(oArgs) {
2123 myDataTable.subscribe('postRenderEvent',function(oArgs) {
2122 tooltip_activate();
2124 tooltip_activate();
2123 quick_repo_menu();
2125 quick_repo_menu();
2124 });
2126 });
2125
2127
2126 var filterTimeout = null;
2128 var filterTimeout = null;
2127 var $q_filter = $('#q_filter');
2129 var $q_filter = $('#q_filter');
2128
2130
2129 var updateFilter = function () {
2131 var updateFilter = function () {
2130 // Reset timeout
2132 // Reset timeout
2131 filterTimeout = null;
2133 filterTimeout = null;
2132
2134
2133 // Reset sort
2135 // Reset sort
2134 var state = myDataTable.getState();
2136 var state = myDataTable.getState();
2135 state.sortedBy = {key:sortkey, dir:YAHOO.widget.DataTable.CLASS_ASC};
2137 state.sortedBy = {key:sortkey, dir:YAHOO.widget.DataTable.CLASS_ASC};
2136
2138
2137 // Get filtered data
2139 // Get filtered data
2138 myDataSource.sendRequest($q_filter.val(), {
2140 myDataSource.sendRequest($q_filter.val(), {
2139 success : myDataTable.onDataReturnInitializeTable,
2141 success : myDataTable.onDataReturnInitializeTable,
2140 failure : myDataTable.onDataReturnInitializeTable,
2142 failure : myDataTable.onDataReturnInitializeTable,
2141 scope : myDataTable,
2143 scope : myDataTable,
2142 argument: state});
2144 argument: state});
2143 };
2145 };
2144
2146
2145 $q_filter.click(function(){
2147 $q_filter.click(function(){
2146 if(!$q_filter.hasClass('loaded')){
2148 if(!$q_filter.hasClass('loaded')){
2147 //TODO: load here full list later to do search within groups
2149 //TODO: load here full list later to do search within groups
2148 $q_filter.addClass('loaded');
2150 $q_filter.addClass('loaded');
2149 }
2151 }
2150 });
2152 });
2151
2153
2152 $q_filter.keyup(function (e) {
2154 $q_filter.keyup(function (e) {
2153 clearTimeout(filterTimeout);
2155 clearTimeout(filterTimeout);
2154 filterTimeout = setTimeout(updateFilter, 600);
2156 filterTimeout = setTimeout(updateFilter, 600);
2155 });
2157 });
2156 }
2158 }
2157
2159
2158 // global hooks after DOM is loaded
2160 // global hooks after DOM is loaded
2159
2161
2160 $(document).ready(function(){
2162 $(document).ready(function(){
2161 $('.diff-collapse-button').click(function(e) {
2163 $('.diff-collapse-button').click(function(e) {
2162 var $button = $(e.currentTarget);
2164 var $button = $(e.currentTarget);
2163 var $target = $('#' + $button.attr('target'));
2165 var $target = $('#' + $button.attr('target'));
2164 if($target.hasClass('hidden')){
2166 if($target.hasClass('hidden')){
2165 $target.removeClass('hidden');
2167 $target.removeClass('hidden');
2166 $button.html("&uarr; {0} &uarr;".format(_TM['Collapse Diff']));
2168 $button.html("&uarr; {0} &uarr;".format(_TM['Collapse Diff']));
2167 }
2169 }
2168 else if(!$target.hasClass('hidden')){
2170 else if(!$target.hasClass('hidden')){
2169 $target.addClass('hidden');
2171 $target.addClass('hidden');
2170 $button.html("&darr; {0} &darr;".format(_TM['Expand Diff']));
2172 $button.html("&darr; {0} &darr;".format(_TM['Expand Diff']));
2171 }
2173 }
2172 });
2174 });
2173 });
2175 });
@@ -1,260 +1,271 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('New Pull Request')}
4 ${c.repo_name} ${_('New Pull Request')}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${_('New Pull Request')}
8 ${_('New Pull Request')}
9 </%def>
9 </%def>
10
10
11 <%def name="page_nav()">
11 <%def name="page_nav()">
12 ${self.menu('repositories')}
12 ${self.menu('repositories')}
13 </%def>
13 </%def>
14
14
15 <%def name="main()">
15 <%def name="main()">
16 ${self.repo_context_bar('showpullrequest')}
16 ${self.repo_context_bar('showpullrequest')}
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22
22
23 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
23 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
24 <div class="form">
24 <div class="form">
25 <!-- fields -->
25 <!-- fields -->
26
26
27 <div class="fields" style="float:left;width:50%;padding-right:30px;">
27 <div class="fields" style="float:left;width:50%;padding-right:30px;">
28
28
29 <div class="field">
29 <div class="field">
30 <div class="label">
30 <div class="label">
31 <label for="pullrequest_title">${_('Title')}:</label>
31 <label for="pullrequest_title">${_('Title')}:</label>
32 </div>
32 </div>
33 <div class="input">
33 <div class="input">
34 ${h.text('pullrequest_title',class_="large",placeholder=_('Summarize the changes - or leave empty'))}
34 ${h.text('pullrequest_title',class_="large",placeholder=_('Summarize the changes - or leave empty'))}
35 </div>
35 </div>
36 </div>
36 </div>
37
37
38 <div class="field">
38 <div class="field">
39 <div class="label label-textarea">
39 <div class="label label-textarea">
40 <label for="pullrequest_desc">${_('Description')}:</label>
40 <label for="pullrequest_desc">${_('Description')}:</label>
41 </div>
41 </div>
42 <div class="textarea text-area editor">
42 <div class="textarea text-area editor">
43 ${h.textarea('pullrequest_desc',size=30,placeholder=_('Write a short description on this pull request'))}
43 ${h.textarea('pullrequest_desc',size=30,placeholder=_('Write a short description on this pull request'))}
44 </div>
44 </div>
45 </div>
45 </div>
46
46
47 <div class="field">
47 <div class="field">
48 <div class="label label-textarea">
48 <div class="label label-textarea">
49 <label for="pullrequest_desc">${_('Changeset flow')}:</label>
49 <label for="pullrequest_desc">${_('Changeset flow')}:</label>
50 </div>
50 </div>
51 <div class="input">
51 <div class="input">
52 ##ORG
52 ##ORG
53 <div>
53 <div>
54 <div>
54 <div>
55 <div style="padding:5px 3px 3px 3px;">
55 <div style="padding:5px 3px 3px 3px;">
56 <b>${_('Origin repository')}:</b> <span id="org_repo_desc">${c.db_repo.description.split('\n')[0]}</span>
56 <b>${_('Origin repository')}:</b> <span id="org_repo_desc">${c.db_repo.description.split('\n')[0]}</span>
57 </div>
57 </div>
58 <div>
58 <div>
59 ${h.select('org_repo','',c.cs_repos,class_='refs')}:${h.select('org_ref',c.default_cs_ref,c.cs_refs,class_='refs')}
59 ${h.select('org_repo','',c.cs_repos,class_='refs')}:${h.select('org_ref',c.default_cs_ref,c.cs_refs,class_='refs')}
60 </div>
60 </div>
61 <div style="padding:5px 3px 3px 3px;">
61 <div style="padding:5px 3px 3px 3px;">
62 <b>${_('Revision')}:</b> <span id="org_rev_span">-</span>
62 <b>${_('Revision')}:</b> <span id="org_rev_span">-</span>
63 </div>
63 </div>
64 </div>
64 </div>
65 </div>
65 </div>
66
66
67 ##OTHER, most Probably the PARENT OF THIS FORK
67 ##OTHER, most Probably the PARENT OF THIS FORK
68 <div style="border-top: 1px solid #EEE; margin: 5px 0px 0px 0px">
68 <div style="border-top: 1px solid #EEE; margin: 5px 0px 0px 0px">
69 <div>
69 <div>
70 ## filled with JS
70 ## filled with JS
71 <div style="padding:5px 3px 3px 3px;">
71 <div style="padding:5px 3px 3px 3px;">
72 <b>${_('Destination repository')}:</b> <span id="other_repo_desc">${c.a_repo.description.split('\n')[0]}</span>
72 <b>${_('Destination repository')}:</b> <span id="other_repo_desc">${c.a_repo.description.split('\n')[0]}</span>
73 </div>
73 </div>
74 <div>
74 <div>
75 ${h.select('other_repo',c.a_repo.repo_name,c.a_repos,class_='refs')}:${h.select('other_ref',c.default_a_ref,c.a_refs,class_='refs')}
75 ${h.select('other_repo',c.a_repo.repo_name,c.a_repos,class_='refs')}:${h.select('other_ref',c.default_a_ref,c.a_refs,class_='refs')}
76 </div>
76 </div>
77 <div style="padding:5px 3px 3px 3px;">
77 <div style="padding:5px 3px 3px 3px;">
78 <b>${_('Revision')}:</b> <span id="other_rev_span">-</span>
78 <b>${_('Revision')}:</b> <span id="other_rev_span">-</span>
79 </div>
79 </div>
80 </div>
80 </div>
81 </div>
81 </div>
82 <div style="clear:both"></div>
82 <div style="clear:both"></div>
83 </div>
83 </div>
84 </div>
84 </div>
85
85
86 <div class="field">
86 <div class="field">
87 <div class="buttons">
87 <div class="buttons">
88 ${h.submit('save',_('Create Pull Request'),class_="btn")}
88 ${h.submit('save',_('Create Pull Request'),class_="btn")}
89 ${h.reset('reset',_('Reset'),class_="btn")}
89 ${h.reset('reset',_('Reset'),class_="btn")}
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 </div>
93 </div>
94
94
95 ## Reviewers
95 ## Reviewers
96 <div style="float:left; border-left:1px dashed #eee">
96 <div style="float:left; border-left:1px dashed #eee">
97 <div class="pr-details-title">${_('Pull Request Reviewers')}</div>
97 <div class="pr-details-title">${_('Pull Request Reviewers')}</div>
98 <div id="reviewers" style="padding:0px 0px 0px 15px">
98 <div id="reviewers" style="padding:0px 0px 0px 15px">
99 ## members goes here !
99 ## members goes here !
100 <div>
100 <div>
101 <ul id="review_members" class="group_members">
101 <ul id="review_members" class="group_members">
102 %for member in [c.a_repo.user]:
102 %for member in [c.a_repo.user]:
103 <li id="reviewer_${member.user_id}">
103 <li id="reviewer_${member.user_id}">
104 <div class="reviewers_member">
104 <div class="reviewers_member">
105 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email, 14)}"/> </div>
105 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email, 14)}"/> </div>
106 <div style="float:left">${member.firstname} ${member.lastname} (${_('owner')})</div>
106 <div style="float:left">${member.firstname} ${member.lastname} (${_('owner')})</div>
107 <input type="hidden" value="${member.user_id}" name="review_members" />
107 <input type="hidden" value="${member.user_id}" name="review_members" />
108 <span class="action_button" style="padding: 3px" onclick="removeReviewMember(${member.user_id})" title="${_('Remove reviewer')}">
108 <span class="action_button" style="padding: 3px" onclick="removeReviewMember(${member.user_id})" title="${_('Remove reviewer')}">
109 <i class="icon-remove-sign" style="color: #FF4444;"></i>
109 <i class="icon-remove-sign" style="color: #FF4444;"></i>
110 </span>
110 </span>
111 </div>
111 </div>
112 </li>
112 </li>
113 %endfor
113 %endfor
114 </ul>
114 </ul>
115 </div>
115 </div>
116
116
117 <div class='ac'>
117 <div class='ac'>
118 <div class="reviewer_ac">
118 <div class="reviewer_ac">
119 ${h.text('user', class_='yui-ac-input',placeholder=_('Type name of reviewer to add'))}
119 ${h.text('user', class_='yui-ac-input',placeholder=_('Type name of reviewer to add'))}
120 <div id="reviewers_container"></div>
120 <div id="reviewers_container"></div>
121 </div>
121 </div>
122 </div>
122 </div>
123 </div>
123 </div>
124 </div>
124 </div>
125
125
126 <div style="clear:both;padding: 0 0 30px 0;"></div>
126 <div style="clear:both;padding: 0 0 30px 0;"></div>
127
127
128 <h4>${_('Changesets')}</h4>
128 <h4>${_('Changesets')}</h4>
129 <div style="float:left;padding:0px 30px 30px 30px">
129 <div style="float:left;padding:0px 30px 30px 30px">
130 ## overview pulled by ajax
130 ## overview pulled by ajax
131 <div style="float:left" id="pull_request_overview"></div>
131 <div style="float:left" id="pull_request_overview"></div>
132 </div>
132 </div>
133 <div style="clear:both;"></div>
133 <div style="clear:both;"></div>
134
134
135 </div>
135 </div>
136
136
137 ${h.end_form()}
137 ${h.end_form()}
138
138
139 </div>
139 </div>
140
140
141 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
141 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
142 <script type="text/javascript">
142 <script type="text/javascript">
143 var _USERS_AC_DATA = ${c.users_array|n};
143 var _USERS_AC_DATA = ${c.users_array|n};
144 var _GROUPS_AC_DATA = ${c.user_groups_array|n};
144 var _GROUPS_AC_DATA = ${c.user_groups_array|n};
145 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
145 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
146
146
147 pyroutes.register('pullrequest_repo_info', "${url('pullrequest_repo_info',repo_name='%(repo_name)s')}", ['repo_name']);
147 pyroutes.register('pullrequest_repo_info', "${url('pullrequest_repo_info',repo_name='%(repo_name)s')}", ['repo_name']);
148
148
149 var pendingajax = undefined;
149 var otherrepoChanged = function(){
150 var otherrepoChanged = function(){
150 var repo_name = $('#other_repo').val();
151 var repo_name = $('#other_repo').val();
151 ajaxGET(pyroutes.url('pullrequest_repo_info', {"repo_name": repo_name}),
152 if (pendingajax) {
153 pendingajax.abort();
154 pendingajax = undefined;
155 }
156 pendingajax = ajaxGET(pyroutes.url('pullrequest_repo_info', {"repo_name": repo_name}),
152 function(o){
157 function(o){
158 pendingajax = undefined;
153 var data = JSON.parse(o.responseText);
159 var data = JSON.parse(o.responseText);
154 $('#other_repo_desc').html(data.description);
160 $('#other_repo_desc').html(data.description);
155
161
156 // replace options of other_ref with the ones for the current other_repo
162 // replace options of other_ref with the ones for the current other_repo
157 var $other_ref = $('#other_ref');
163 var $other_ref = $('#other_ref');
158 $other_ref.empty();
164 $other_ref.empty();
159 for(var i = 0; i < data.refs.length; i++)
165 for(var i = 0; i < data.refs.length; i++)
160 {
166 {
161 var $optgroup = $('<optgroup/>').attr('label', data.refs[i][1]);
167 var $optgroup = $('<optgroup/>').attr('label', data.refs[i][1]);
162 var options = data.refs[i][0];
168 var options = data.refs[i][0];
163 var length = options.length;
169 var length = options.length;
164 for(var j = 0; j < length; j++)
170 for(var j = 0; j < length; j++)
165 {
171 {
166 $optgroup.append($('<option/>').text(options[j][1]).val(options[j][0]));
172 $optgroup.append($('<option/>').text(options[j][1]).val(options[j][0]));
167 }
173 }
168 $other_ref.append($optgroup);
174 $other_ref.append($optgroup);
169 }
175 }
170 $other_ref.val(data.selected_ref);
176 $other_ref.val(data.selected_ref);
171
177
172 // reset && add the reviewer based on selected repo
178 // reset && add the reviewer based on selected repo
173 YUD.get('review_members').innerHTML = '';
179 YUD.get('review_members').innerHTML = '';
174 addReviewMember(data.user.user_id, data.user.firstname,
180 addReviewMember(data.user.user_id, data.user.firstname,
175 data.user.lastname, data.user.username,
181 data.user.lastname, data.user.username,
176 data.user.gravatar_link);
182 data.user.gravatar_link);
177
183
178 // re-populate the select2 thingie
184 // re-populate the select2 thingie
179 $("#other_ref").select2({
185 $("#other_ref").select2({
180 dropdownAutoWidth: true
186 dropdownAutoWidth: true
181 });
187 });
182
188
183 loadPreview();
189 loadPreview();
184 });
190 });
185 };
191 };
186
192
187 var loadPreview = function(){
193 var loadPreview = function(){
188 //url template
194 //url template
189 var url = "${h.url('compare_url',
195 var url = "${h.url('compare_url',
190 repo_name='__other_repo__',
196 repo_name='__other_repo__',
191 org_ref_type='rev',
197 org_ref_type='rev',
192 org_ref_name='__other_ref_name__',
198 org_ref_name='__other_ref_name__',
193 other_repo='__org_repo__',
199 other_repo='__org_repo__',
194 other_ref_type='rev',
200 other_ref_type='rev',
195 other_ref_name='__org_ref_name__',
201 other_ref_name='__org_ref_name__',
196 as_form=True,
202 as_form=True,
197 merge=True,
203 merge=True,
198 )}";
204 )}";
199 var org_repo = YUQ('#pull_request_form #org_repo')[0].value;
205 var org_repo = YUQ('#pull_request_form #org_repo')[0].value;
200 var org_ref = YUQ('#pull_request_form #org_ref')[0].value.split(':');
206 var org_ref = YUQ('#pull_request_form #org_ref')[0].value.split(':');
201 ## TODO: make nice link like link_to_ref() do
207 ## TODO: make nice link like link_to_ref() do
202 YUD.get('org_rev_span').innerHTML = org_ref[2].substr(0,12);
208 YUD.get('org_rev_span').innerHTML = org_ref[2].substr(0,12);
203
209
204 var other_repo = YUQ('#pull_request_form #other_repo')[0].value;
210 var other_repo = YUQ('#pull_request_form #other_repo')[0].value;
205 var other_ref = YUQ('#pull_request_form #other_ref')[0].value.split(':');
211 var other_ref = YUQ('#pull_request_form #other_ref')[0].value.split(':');
206 YUD.get('other_rev_span').innerHTML = other_ref[2].substr(0,12);
212 YUD.get('other_rev_span').innerHTML = other_ref[2].substr(0,12);
207
213
208 var select_refs = YUQ('#pull_request_form select.refs')
214 var select_refs = YUQ('#pull_request_form select.refs')
209 var rev_data = {
215 var rev_data = {
210 '__org_repo__': org_repo,
216 '__org_repo__': org_repo,
211 '__org_ref_name__': org_ref[2],
217 '__org_ref_name__': org_ref[2],
212 '__other_repo__': other_repo,
218 '__other_repo__': other_repo,
213 '__other_ref_name__': other_ref[2]
219 '__other_ref_name__': other_ref[2]
214 }; // gather the org/other ref and repo here
220 }; // gather the org/other ref and repo here
215
221
216 for (k in rev_data){
222 for (k in rev_data){
217 url = url.replace(k,rev_data[k]);
223 url = url.replace(k,rev_data[k]);
218 }
224 }
219
225
220 asynchtml(url, $('#pull_request_overview'), function(o){
226 if (pendingajax) {
227 pendingajax.abort();
228 pendingajax = undefined;
229 }
230 pendingajax = asynchtml(url, $('#pull_request_overview'), function(o){
231 pendingajax = undefined;
221 var jsdata = eval('('+YUD.get('jsdata').innerHTML+')'); // TODO: just get json
232 var jsdata = eval('('+YUD.get('jsdata').innerHTML+')'); // TODO: just get json
222 var r = new BranchRenderer('graph_canvas', 'graph_content_pr');
233 var r = new BranchRenderer('graph_canvas', 'graph_content_pr');
223 r.render(jsdata,100);
234 r.render(jsdata,100);
224 });
235 });
225 }
236 }
226
237
227 $(document).ready(function(){
238 $(document).ready(function(){
228 $("#org_repo").select2({
239 $("#org_repo").select2({
229 dropdownAutoWidth: true
240 dropdownAutoWidth: true
230 });
241 });
231 ## (org_repo can't change)
242 ## (org_repo can't change)
232
243
233 $("#org_ref").select2({
244 $("#org_ref").select2({
234 dropdownAutoWidth: true
245 dropdownAutoWidth: true
235 });
246 });
236 $("#org_ref").on("change", function(e){
247 $("#org_ref").on("change", function(e){
237 loadPreview();
248 loadPreview();
238 });
249 });
239
250
240 $("#other_repo").select2({
251 $("#other_repo").select2({
241 dropdownAutoWidth: true
252 dropdownAutoWidth: true
242 });
253 });
243 $("#other_repo").on("change", function(e){
254 $("#other_repo").on("change", function(e){
244 otherrepoChanged();
255 otherrepoChanged();
245 });
256 });
246
257
247 $("#other_ref").select2({
258 $("#other_ref").select2({
248 dropdownAutoWidth: true
259 dropdownAutoWidth: true
249 });
260 });
250 $("#other_ref").on("change", function(e){
261 $("#other_ref").on("change", function(e){
251 loadPreview();
262 loadPreview();
252 });
263 });
253
264
254 //lazy load overview after 0.5s
265 //lazy load overview after 0.5s
255 setTimeout(loadPreview, 500);
266 setTimeout(loadPreview, 500);
256 });
267 });
257
268
258 </script>
269 </script>
259
270
260 </%def>
271 </%def>
General Comments 0
You need to be logged in to leave comments. Login now