##// END OF EJS Templates
emails: improved styling, and fixed problems with some email clients...
emails: improved styling, and fixed problems with some email clients - fixes #5620

File last commit:

r4306:09801de9 default
r4377:a2405083 stable
Show More
rhodecode.js
695 lines | 22.2 KiB | application/javascript | JavascriptLexer
code: update copyrights to 2020
r4306 // # Copyright (C) 2010-2020 RhodeCode GmbH
project: added all source files and assets
r1 // #
// # This program is free software: you can redistribute it and/or modify
// # it under the terms of the GNU Affero General Public License, version 3
// # (only), as published by the Free Software Foundation.
// #
// # This program is distributed in the hope that it will be useful,
// # but WITHOUT ANY WARRANTY; without even the implied warranty of
// # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// # GNU General Public License for more details.
// #
// # You should have received a copy of the GNU Affero General Public License
// # along with this program. If not, see <http://www.gnu.org/licenses/>.
// #
// # This program is dual-licensed. If you wish to learn more about the
// # RhodeCode Enterprise Edition, including its added features, Support services,
// # and proprietary license terms, please see https://rhodecode.com/licenses/
/**
RhodeCode JS Files
**/
if (typeof console == "undefined" || typeof console.log == "undefined"){
console = { log: function() {} }
}
// TODO: move the following function to submodules
/**
* show more
*/
var show_more_event = function(){
$('table .show_more').click(function(e) {
var cid = e.target.id.substring(1);
var button = $(this);
if (button.hasClass('open')) {
$('#'+cid).hide();
button.removeClass('open');
} else {
$('#'+cid).show();
button.addClass('open one');
}
});
};
var compare_radio_buttons = function(repo_name, compare_ref_type){
$('#compare_action').on('click', function(e){
e.preventDefault();
var source = $('input[name=compare_source]:checked').val();
var target = $('input[name=compare_target]:checked').val();
if(source && target){
var url_data = {
repo_name: repo_name,
source_ref: source,
source_ref_type: compare_ref_type,
target_ref: target,
target_ref_type: compare_ref_type,
merge: 1
};
compare: migrated code from pylons to pyramid views.
r1957 window.location = pyroutes.url('repo_compare', url_data);
project: added all source files and assets
r1 }
});
$('.compare-radio-button').on('click', function(e){
var source = $('input[name=compare_source]:checked').val();
var target = $('input[name=compare_target]:checked').val();
if(source && target){
$('#compare_action').removeAttr("disabled");
$('#compare_action').removeClass("disabled");
}
})
};
var showRepoSize = function(target, repo_name, commit_id, callback) {
var container = $('#' + target);
var url = pyroutes.url('repo_stats',
{"repo_name": repo_name, "commit_id": commit_id});
summary: don't load size on container expand, only on manual action....
r3334 container.show();
project: added all source files and assets
r1 if (!container.hasClass('loaded')) {
$.ajax({url: url})
.complete(function (data) {
var responseJSON = data.responseJSON;
container.addClass('loaded');
container.html(responseJSON.size);
callback(responseJSON.code_stats)
})
.fail(function (data) {
console.log('failed to load repo stats');
});
}
};
var showRepoStats = function(target, data){
var container = $('#' + target);
if (container.hasClass('loaded')) {
return
}
var total = 0;
var no_data = true;
var tbl = document.createElement('table');
summary: re-organize order of summary expanded area...
r3657 tbl.setAttribute('class', 'trending_language_tbl rctable');
project: added all source files and assets
r1
$.each(data, function(key, val){
total += val.count;
});
var sortedStats = [];
for (var obj in data){
sortedStats.push([obj, data[obj]])
}
var sortedData = sortedStats.sort(function (a, b) {
return b[1].count - a[1].count
});
var cnt = 0;
$.each(sortedData, function(idx, val){
cnt += 1;
no_data = false;
var tr = document.createElement('tr');
var key = val[0];
var obj = {"desc": val[1].desc, "count": val[1].count};
summary: re-organize order of summary expanded area...
r3657 // meta language names
project: added all source files and assets
r1 var td1 = document.createElement('td');
var trending_language_label = document.createElement('div');
summary: re-organize order of summary expanded area...
r3657 trending_language_label.innerHTML = obj.desc;
project: added all source files and assets
r1 td1.appendChild(trending_language_label);
summary: re-organize order of summary expanded area...
r3657 // extensions
project: added all source files and assets
r1 var td2 = document.createElement('td');
summary: re-organize order of summary expanded area...
r3657 var extension = document.createElement('div');
extension.innerHTML = ".{0}".format(key)
td2.appendChild(extension);
project: added all source files and assets
r1
summary: re-organize order of summary expanded area...
r3657 // number of files
var td3 = document.createElement('td');
var file_count = document.createElement('div');
var percentage_num = Math.round((obj.count / total * 100), 2);
var label = _ngettext('file', 'files', obj.count);
file_count.innerHTML = "{0} {1} ({2}%)".format(obj.count, label, percentage_num) ;
td3.appendChild(file_count);
project: added all source files and assets
r1
summary: re-organize order of summary expanded area...
r3657 // percentage
var td4 = document.createElement('td');
td4.setAttribute("class", 'trending_language');
var percentage = document.createElement('div');
percentage.setAttribute('class', 'lang-bar');
percentage.innerHTML = "&nbsp;";
percentage.style.width = percentage_num + '%';
td4.appendChild(percentage);
project: added all source files and assets
r1
tr.appendChild(td1);
tr.appendChild(td2);
summary: re-organize order of summary expanded area...
r3657 tr.appendChild(td3);
tr.appendChild(td4);
project: added all source files and assets
r1 tbl.appendChild(tr);
});
$(container).html(tbl);
$(container).addClass('loaded');
$('#code_stats_show_more').on('click', function (e) {
e.preventDefault();
$('.stats_hidden').each(function (idx) {
$(this).css("display", "");
});
$('#code_stats_show_more').hide();
});
};
js: cleanup unused javascript code.
r1328 // returns a node from given html;
var fromHTML = function(html){
var _html = document.createElement('element');
_html.innerHTML = html;
return _html;
};
project: added all source files and assets
r1
// Toggle Collapsable Content
function collapsableContent() {
$('.collapsable-content').not('.no-hide').hide();
$('.btn-collapse').unbind(); //in case we've been here before
$('.btn-collapse').click(function() {
var button = $(this);
var togglename = $(this).data("toggle");
$('.collapsable-content[data-toggle='+togglename+']').toggle();
if ($(this).html()=="Show Less")
$(this).html("Show More");
else
$(this).html("Show Less");
});
};
var timeagoActivate = function() {
$("time.timeago").timeago();
};
clipboard: added dummy placeholders for copying commits/paths via clipboard.js
r1935
var clipboardActivate = function() {
/*
*
* <i class="tooltip icon-plus clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
* */
clipboard-js: bumped to 2.0.1 to resolve naming conflicts with window.Clipboard
r2925 var clipboard = new ClipboardJS('.clipboard-action');
clipboard: added dummy placeholders for copying commits/paths via clipboard.js
r1935
clipboard.on('success', function(e) {
clipboard-action: make the icon provide feedback when clicked.
r1954 var callback = function () {
$(e.trigger).animate({'opacity': 1.00}, 200)
};
$(e.trigger).animate({'opacity': 0.15}, 200, callback);
clipboard: added dummy placeholders for copying commits/paths via clipboard.js
r1935 e.clearSelection();
});
};
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026 var tooltipActivate = function () {
var delay = 50;
var animation = 'fade';
var theme = 'tooltipster-shadow';
var debug = false;
$('.tooltip').tooltipster({
debug: debug,
theme: theme,
animation: animation,
delay: delay,
contentCloning: true,
contentAsHTML: true,
functionBefore: function (instance, helper) {
var $origin = $(helper.origin);
var data = '<div style="white-space: pre-wrap">{0}</div>'.format(instance.content());
instance.content(data);
}
});
var hovercardCache = {};
dan
hovercards: enable hovercards on parsed commits references.
r4047 var loadHoverCard = function (url, altHovercard, callback) {
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026 var id = url;
if (hovercardCache[id] !== undefined) {
callback(hovercardCache[id]);
hovercards: added commit hovercard for files, and dashboard views.
r4032 return true;
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026 }
hovercardCache[id] = undefined;
$.get(url, function (data) {
hovercardCache[id] = data;
callback(hovercardCache[id]);
hovercards: added commit hovercard for files, and dashboard views.
r4032 return true;
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026 }).fail(function (data, textStatus, errorThrown) {
dan
hovercards: enable hovercards on parsed commits references.
r4047
if (parseInt(data.status) === 404) {
var msg = "<p>{0}</p>".format(altHovercard || "No Data exists for this hovercard");
} else {
var msg = "<p class='error-message'>Error while fetching hovercard.\nError code {0} ({1}).</p>".format(data.status,data.statusText);
}
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026 callback(msg);
hovercards: added commit hovercard for files, and dashboard views.
r4032 return false
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026 });
};
$('.tooltip-hovercard').tooltipster({
debug: debug,
theme: theme,
animation: animation,
delay: delay,
interactive: true,
contentCloning: true,
trigger: 'custom',
triggerOpen: {
mouseenter: true,
},
triggerClose: {
mouseleave: true,
originClick: true,
touchleave: true
},
content: _gettext('Loading...'),
contentAsHTML: true,
updateAnimation: null,
functionBefore: function (instance, helper) {
var $origin = $(helper.origin);
// we set a variable so the data is only loaded once via Ajax, not every time the tooltip opens
if ($origin.data('loaded') !== true) {
var hovercardUrl = $origin.data('hovercardUrl');
dan
hovercards: enable hovercards on parsed commits references.
r4047 var altHovercard =$origin.data('hovercardAlt');
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026
if (hovercardUrl !== undefined && hovercardUrl !== "") {
mentions: markdown renderer now wraps username in hovercard logic allowing checking the mentioned user....
r4221 if (hovercardUrl.substr(0,12) === 'pyroutes.url'){
hovercardUrl = eval(hovercardUrl)
}
dan
hovercards: enable hovercards on parsed commits references.
r4047 var loaded = loadHoverCard(hovercardUrl, altHovercard, function (data) {
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026 instance.content(data);
})
} else {
js: tooltip allow full html mode using a base64 encoded data property.
r4031 if ($origin.data('hovercardAltHtml')) {
var data = atob($origin.data('hovercardAltHtml'));
} else {
dan
hovercards: enable hovercards on parsed commits references.
r4047 var data = '<div style="white-space: pre-wrap">{0}</div>'.format(altHovercard)
js: tooltip allow full html mode using a base64 encoded data property.
r4031 }
hovercards: added commit hovercard for files, and dashboard views.
r4032 var loaded = true;
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026 instance.content(data);
}
// to remember that the data has been loaded
hovercards: added commit hovercard for files, and dashboard views.
r4032 $origin.data('loaded', loaded);
hovercacrds: added new tooltips and hovercards to expose certain information for objects shown in UI
r4026 }
}
})
};
clipboard: added dummy placeholders for copying commits/paths via clipboard.js
r1935
project: added all source files and assets
r1 // Formatting values in a Select2 dropdown of commit references
var formatSelect2SelectionRefs = function(commit_ref){
var tmpl = '';
if (!commit_ref.text || commit_ref.type === 'sha'){
return commit_ref.text;
}
if (commit_ref.type === 'branch'){
tmpl = tmpl.concat('<i class="icon-branch"></i> ');
} else if (commit_ref.type === 'tag'){
tmpl = tmpl.concat('<i class="icon-tag"></i> ');
} else if (commit_ref.type === 'book'){
tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
}
select2: always escape .text attributes to prevent XSS via...
r2196 return tmpl.concat(escapeHtml(commit_ref.text));
project: added all source files and assets
r1 };
// takes a given html element and scrolls it down offset pixels
dan
diffs: replace compare controller with new html based diffs:...
r1030 function offsetScroll(element, offset) {
setTimeout(function() {
project: added all source files and assets
r1 var location = element.offset().top;
// some browsers use body, some use html
$('html, body').animate({ scrollTop: (location - offset) });
}, 100);
}
dan
diffs: replace compare controller with new html based diffs:...
r1030 // scroll an element `percent`% from the top of page in `time` ms
function scrollToElement(element, percent, time) {
percent = (percent === undefined ? 25 : percent);
time = (time === undefined ? 100 : time);
var $element = $(element);
commit-page: show unresolved TODOs on commit page below comments.
r1385 if ($element.length == 0) {
throw('Cannot scroll to {0}'.format(element))
}
dan
diffs: replace compare controller with new html based diffs:...
r1030 var elOffset = $element.offset().top;
var elHeight = $element.height();
var windowHeight = $(window).height();
var offset = elOffset;
if (elHeight < windowHeight) {
offset = elOffset - ((windowHeight / (100 / percent)) - (elHeight / 2));
}
setTimeout(function() {
$('html, body').animate({ scrollTop: offset});
}, time);
}
project: added all source files and assets
r1 /**
* global hooks after DOM is loaded
*/
$(document).ready(function() {
firefoxAnchorFix();
$('.navigation a.menulink').on('click', function(e){
var menuitem = $(this).parent('li');
if (menuitem.hasClass('open')) {
menuitem.removeClass('open');
} else {
menuitem.addClass('open');
$(document).on('click', function(event) {
if (!$(event.target).closest(menuitem).length) {
menuitem.removeClass('open');
}
});
}
});
diffs/files: fix and improve line selections and anchor links.
r2642 $('body').on('click', '.cb-lineno a', function(event) {
dan
diffs: fixed selecting active lines in both types of diffs.
r3130 function sortNumber(a,b) {
return a - b;
}
dan
annotations: replace annotated source code viewer with renderer...
r986
dan
diffs: fixed selecting active lines in both types of diffs.
r3130 var lineNo = $(this).data('lineNo');
var lineName = $(this).attr('name');
if (lineNo) {
var prevLine = $('.cb-line-selected a').data('lineNo');
// on shift, we do a range selection, if we got previous line
if (event.shiftKey && prevLine !== undefined) {
var prevLine = parseInt(prevLine);
var nextLine = parseInt(lineNo);
var pos = [prevLine, nextLine].sort(sortNumber);
var anchor = '#L{0}-{1}'.format(pos[0], pos[1]);
// single click
} else {
var nextLine = parseInt(lineNo);
var pos = [nextLine, nextLine];
var anchor = '#L{0}'.format(pos[0]);
}
// highlight
var range = [];
for (var i = pos[0]; i <= pos[1]; i++) {
range.push(i);
}
// clear old selected lines
$('.cb-line-selected').removeClass('cb-line-selected');
$.each(range, function (i, lineNo) {
var line_td = $('td.cb-lineno#L' + lineNo);
if (line_td.length) {
line_td.addClass('cb-line-selected'); // line number td
line_td.prev().addClass('cb-line-selected'); // line data
line_td.next().addClass('cb-line-selected'); // line content
}
});
} else if (lineName !== undefined) { // lineName only occurs in diffs
// clear old selected lines
$('td.cb-line-selected').removeClass('cb-line-selected');
var anchor = '#{0}'.format(lineName);
var diffmode = templateContext.session_attrs.diffmode || "sideside";
if (diffmode === "unified") {
$(this).closest('tr').find('td').addClass('cb-line-selected');
} else {
var activeTd = $(this).closest('td');
activeTd.addClass('cb-line-selected');
activeTd.next('td').addClass('cb-line-selected');
files-source: allow making a range selection with shift on file lines.
r2483 }
dan
diffs: fixed selecting active lines in both types of diffs.
r3130 }
files-source: allow making a range selection with shift on file lines.
r2483
dan
diffs: fixed selecting active lines in both types of diffs.
r3130 // Replace URL without jumping to it if browser supports.
// Default otherwise
if (history.pushState && anchor !== undefined) {
var new_location = location.href.rstrip('#');
if (location.hash) {
// location without hash
new_location = new_location.replace(location.hash, "");
dan
annotations: replace annotated source code viewer with renderer...
r986 }
diffs/files: fix and improve line selections and anchor links.
r2642
dan
diffs: fixed selecting active lines in both types of diffs.
r3130 // Make new anchor url
new_location = new_location + anchor;
history.pushState(true, document.title, new_location);
diffs/files: fix and improve line selections and anchor links.
r2642
dan
diffs: fixed selecting active lines in both types of diffs.
r3130 return false;
}
diffs/files: fix and improve line selections and anchor links.
r2642
dan
diffs: fixed selecting active lines in both types of diffs.
r3130 });
project: added all source files and assets
r1
$('.collapse_file').on('click', function(e) {
e.stopPropagation();
if ($(e.target).is('a')) { return; }
var node = $(e.delegateTarget).first();
var icon = $($(node.children().first()).children().first());
var id = node.attr('fid');
var target = $('#'+id);
var tr = $('#tr_'+id);
var diff = $('#diff_'+id);
if(node.hasClass('expand_file')){
node.removeClass('expand_file');
icon.removeClass('expand_file_icon');
node.addClass('collapse_file');
icon.addClass('collapse_file_icon');
diff.show();
tr.show();
target.show();
} else {
node.removeClass('collapse_file');
icon.removeClass('collapse_file_icon');
node.addClass('expand_file');
icon.addClass('expand_file_icon');
diff.hide();
tr.hide();
target.hide();
}
});
$('#expand_all_files').click(function() {
$('.expand_file').each(function() {
var node = $(this);
var icon = $($(node.children().first()).children().first());
var id = $(this).attr('fid');
var target = $('#'+id);
var tr = $('#tr_'+id);
var diff = $('#diff_'+id);
node.removeClass('expand_file');
icon.removeClass('expand_file_icon');
node.addClass('collapse_file');
icon.addClass('collapse_file_icon');
diff.show();
tr.show();
target.show();
});
});
$('#collapse_all_files').click(function() {
$('.collapse_file').each(function() {
var node = $(this);
var icon = $($(node.children().first()).children().first());
var id = $(this).attr('fid');
var target = $('#'+id);
var tr = $('#tr_'+id);
var diff = $('#diff_'+id);
node.removeClass('collapse_file');
icon.removeClass('collapse_file_icon');
node.addClass('expand_file');
icon.addClass('expand_file_icon');
diff.hide();
tr.hide();
target.hide();
});
});
// Mouse over behavior for comments and line selection
// Select the line that comes from the url anchor
// At the time of development, Chrome didn't seem to support jquery's :target
// element, so I had to scroll manually
dan
annotations: replace annotated source code viewer with renderer...
r986
js: fixes for new diff structure
r1166 if (location.hash) {
dan
annotations: replace annotated source code viewer with renderer...
r986 var result = splitDelimitedHash(location.hash);
dan
emails: added reply link to comment type emails...
r4050
var loc = result.loc;
dan
annotations: replace annotated source code viewer with renderer...
r986 if (loc.length > 1) {
dan
diffs: replace compare controller with new html based diffs:...
r1030
var highlightable_line_tds = [];
// source code line format
dan
emails: added reply link to comment type emails...
r4050 var page_highlights = loc.substring(loc.indexOf('#') + 1).split('L');
dan
annotations: replace annotated source code viewer with renderer...
r986
dan
emails: added reply link to comment type emails...
r4050 // multi-line HL, for files
dan
annotations: replace annotated source code viewer with renderer...
r986 if (page_highlights.length > 1) {
var highlight_ranges = page_highlights[1].split(",");
var h_lines = [];
for (var pos in highlight_ranges) {
var _range = highlight_ranges[pos].split('-');
if (_range.length === 2) {
var start = parseInt(_range[0]);
var end = parseInt(_range[1]);
if (start < end) {
for (var i = start; i <= end; i++) {
h_lines.push(i);
}
}
dan
emails: added reply link to comment type emails...
r4050 } else {
dan
annotations: replace annotated source code viewer with renderer...
r986 h_lines.push(parseInt(highlight_ranges[pos]));
}
}
for (pos in h_lines) {
var line_td = $('td.cb-lineno#L' + h_lines[pos]);
if (line_td.length) {
dan
diffs: replace compare controller with new html based diffs:...
r1030 highlightable_line_tds.push(line_td);
dan
annotations: replace annotated source code viewer with renderer...
r986 }
}
dan
diffs: replace compare controller with new html based diffs:...
r1030 }
dan
annotations: replace annotated source code viewer with renderer...
r986
dan
emails: added reply link to comment type emails...
r4050 // now check a direct id reference of line in diff / pull-request page)
if ($(loc).length > 0 && $(loc).hasClass('cb-lineno')) {
dan
diffs: replace compare controller with new html based diffs:...
r1030 highlightable_line_tds.push($(loc));
}
dan
emails: added reply link to comment type emails...
r4050
// mark diff lines as selected
dan
diffs: replace compare controller with new html based diffs:...
r1030 $.each(highlightable_line_tds, function (i, $td) {
$td.addClass('cb-line-selected'); // line number td
dan
ux: add hidden comments indicators plus style tweaks...
r1157 $td.prev().addClass('cb-line-selected'); // line data
dan
diffs: replace compare controller with new html based diffs:...
r1030 $td.next().addClass('cb-line-selected'); // line content
});
dan
emails: added reply link to comment type emails...
r4050 if (highlightable_line_tds.length > 0) {
dan
diffs: replace compare controller with new html based diffs:...
r1030 var $first_line_td = highlightable_line_tds[0];
scrollToElement($first_line_td);
$.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
js: fixes for new diff structure
r1166 td: $first_line_td,
dan
diffs: replace compare controller with new html based diffs:...
r1030 remainder: result.remainder
});
dan
emails: added reply link to comment type emails...
r4050 } else {
// case for direct anchor to comments
var $line = $(loc);
if ($line.hasClass('comment-general')) {
$line.show();
} else if ($line.hasClass('comment-inline')) {
$line.show();
var $cb = $line.closest('.cb');
$cb.removeClass('cb-collapsed')
}
if ($line.length > 0) {
$line.addClass('comment-selected-hl');
offsetScroll($line, 70);
}
if (!$line.hasClass('comment-outdated') && result.remainder === '/ReplyToComment') {
$line.nextAll('.cb-comment-add-button').trigger('click');
}
dan
annotations: replace annotated source code viewer with renderer...
r986 }
dan
emails: added reply link to comment type emails...
r4050
dan
annotations: replace annotated source code viewer with renderer...
r986 }
}
project: added all source files and assets
r1 collapsableContent();
});
auth-tokens: allow specifing custom expiration date manually....
r2083
var feedLifetimeOptions = function(query, initialData){
var data = {results: []};
var isQuery = typeof query.term !== 'undefined';
var section = _gettext('Lifetime');
var children = [];
//filter results
$.each(initialData.results, function(idx, value) {
if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
children.push({
'id': this.id,
'text': this.text
})
}
});
data.results.push({
'text': section,
'children': children
});
if (isQuery) {
var now = moment.utc();
var parseQuery = function(entry, now){
var fmt = 'DD/MM/YYYY H:mm';
var parsed = moment.utc(entry, fmt);
var diffInMin = parsed.diff(now, 'minutes');
if (diffInMin > 0){
return {
id: diffInMin,
text: parsed.format(fmt)
}
} else {
return {
id: undefined,
text: parsed.format('DD/MM/YYYY') + ' ' + _gettext('date not in future')
}
}
};
data.results.push({
'text': _gettext('Specified expiration date'),
'children': [{
'id': parseQuery(query.term, now).id,
'text': parseQuery(query.term, now).text
}]
});
}
query.callback(data);
};
users: use new session store for temporary settings for users....
r3088
var storeUserSessionAttr = function (key, val) {
var postData = {
'key': key,
'val': val,
'csrf_token': CSRF_TOKEN
};
var success = function(o) {
return true
};
ajaxPOST(pyroutes.url('store_user_session_value'), postData, success);
return false;
};