@@ -707,392 +707,392 @@ function _comment_div_append_form($comme
'f_path': f_path,
'line': line_no
};
ajaxPOST(AJAX_COMMENT_URL, postData, success);
});
$('#preview-btn_'+line_no).click(function(e){
var text = $('#text_'+line_no).val();
if(!text){
return
}
$('#preview-box_'+line_no).addClass('unloaded');
$('#preview-box_'+line_no).html(_TM['Loading ...']);
$('#edit-container_'+line_no).hide();
$('#edit-btn_'+line_no).show();
$('#preview-container_'+line_no).show();
$('#preview-btn_'+line_no).hide();
var url = pyroutes.url('changeset_comment_preview', {'repo_name': REPO_NAME});
var post_data = {'text': text};
ajaxPOST(url, post_data, function(html) {
$('#preview-box_'+line_no).html(html);
$('#preview-box_'+line_no).removeClass('unloaded');
})
$('#edit-btn_'+line_no).click(function(e) {
$('#edit-container_'+line_no).show();
$('#edit-btn_'+line_no).hide();
$('#preview-container_'+line_no).hide();
$('#preview-btn_'+line_no).show();
// create event for hide button
$form.find('.hide-inline-form').click(function(e) {
comment_div_state($comment_div, f_path, line_no, false);
setTimeout(function() {
// callbacks
tooltip_activate();
MentionsAutoComplete($('#text_'+line_no), $('#mentions_container_'+line_no),
_USERS_AC_DATA);
$('#text_'+line_no).focus();
}, 10);
function deleteComment(comment_id) {
var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
var postData = {'_method': 'delete'};
var success = function(o) {
$('#comment-'+comment_id).remove();
// Ignore that this might leave a stray Add button (or have a pending form with another comment) ...
ajaxPOST(url, postData, success);
/**
* Double link comments
*/
var linkInlineComments = function($firstlinks, $comments){
if ($comments.length > 0) {
$firstlinks.html('<a href="#{0}">First comment</a>'.format($comments.prop('id')));
if ($comments.length <= 1) {
return;
$comments.each(function(i, e){
var prev = '';
if (i > 0){
var prev_anchor = $($comments.get(i-1)).prop('id');
prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
var next = '';
if (i+1 < $comments.length){
var next_anchor = $($comments.get(i+1)).prop('id');
next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
$(this).find('.comment-prev-next-links').html(
'<div class="prev-comment">{0}</div>'.format(prev) +
'<div class="next-comment">{0}</div>'.format(next));
/* activate files.html stuff */
var fileBrowserListeners = function(current_url, node_list_url, url_base){
var current_url_branch = "?branch=__BRANCH__";
$('#stay_at_branch').on('click',function(e){
if(e.currentTarget.checked){
var uri = current_url_branch;
uri = uri.replace('__BRANCH__',e.currentTarget.value);
window.location = uri;
else{
window.location = current_url;
var $node_filter = $('#node_filter');
var filterTimeout = null;
var nodes = null;
var initFilter = function(){
$('#node_filter_box_loading').show();
$('#search_activate_id').hide();
$('#add_node_id').hide();
$.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
.done(function(json) {
nodes = json.nodes;
$('#node_filter_box_loading').hide();
$('#node_filter_box').show();
$node_filter.focus();
if($node_filter.hasClass('init')){
$node_filter.val('');
$node_filter.removeClass('init');
.fail(function() {
console.log('fileBrowserListeners initFilter failed to load');
;
var updateFilter = function(e) {
return function(){
// Reset timeout
filterTimeout = null;
var query = e.currentTarget.value.toLowerCase();
var match = [];
var matches = 0;
var matches_max = 20;
if (query != ""){
for(var i=0;i<nodes.length;i++){
var pos = nodes[i].name.toLowerCase().indexOf(query);
if(query && pos != -1){
matches++
//show only certain amount to not kill browser
if (matches > matches_max){
break;
var n = nodes[i].name;
var t = nodes[i].type;
var n_hl = n.substring(0,pos)
+ "<b>{0}</b>".format(n.substring(pos,pos+query.length))
+ n.substring(pos+query.length);
var new_url = url_base.replace('__FPATH__',n);
match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
if(match.length >= matches_max){
match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
if(query != ""){
$('#tbody').hide();
$('#tbody_filtered').show();
if (match.length==0){
match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
$('#tbody_filtered').html(match.join(""));
$('#tbody').show();
$('#tbody_filtered').hide();
$('#filter_activate').click(function(){
initFilter();
$node_filter.click(function(){
$node_filter.keyup(function(e){
clearTimeout(filterTimeout);
filterTimeout = setTimeout(updateFilter(e),600);
var initCodeMirror = function(textarea_id, resetUrl){
var initCodeMirror = function(textarea_id, baseUrl, resetUrl){
var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
mode: "null",
lineNumbers: true,
indentUnit: 4,
autofocus: true
CodeMirror.modeURL = "/codemirror/mode/%N/%N.js";
CodeMirror.modeURL = baseUrl + "/codemirror/mode/%N/%N.js";
$('#reset').click(function(e){
window.location=resetUrl;
$('#file_enable').click(function(){
$('#editor_container').show();
$('#upload_file_container').hide();
$('#filename_container').show();
$('#set_mode_header').show();
$('#upload_file_enable').click(function(){
$('#editor_container').hide();
$('#upload_file_container').show();
$('#filename_container').hide();
$('#set_mode_header').hide();
return myCodeMirror
var setCodeMirrorMode = function(codeMirrorInstance, mode) {
CodeMirror.autoLoadMode(codeMirrorInstance, mode);
var _getIdentNode = function(n){
//iterate thrugh nodes until matching interesting node
if (typeof n == 'undefined'){
return -1
if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
return n
return _getIdentNode(n.parentNode);
/* generate links for multi line selects that can be shown by files.html page_highlights.
* This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
var getSelectionLink = function(e) {
//get selection from start/to nodes
if (typeof window.getSelection != "undefined") {
s = window.getSelection();
var from = _getIdentNode(s.anchorNode);
var till = _getIdentNode(s.focusNode);
var f_int = parseInt(from.id.replace('L',''));
var t_int = parseInt(till.id.replace('L',''));
var yoffset = 35;
var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
if (ranges[0] > ranges[1]){
//highlight from bottom
yoffset = -yoffset;
ranges = [ranges[1], ranges[0]];
var $hl_div = $('div#linktt');
// if we select more than 2 lines
if (ranges[0] != ranges[1]){
if ($hl_div.length) {
$hl_div.html('');
} else {
$hl_div = $('<div id="linktt" class="hl-tip-box">');
$('body').prepend($hl_div);
$hl_div.append($('<a>').html(_TM['Selection link']).prop('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
var xy = $(till).offset();
$hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
$hl_div.show();
$hl_div.hide();
var deleteNotification = function(url, notification_id, callbacks){
var success = function(o){
$("#notification_"+notification_id).remove();
_run_callbacks(callbacks);
var failure = function(o){
alert("deleteNotification failure");
var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
ajaxPOST(sUrl, postData, success, failure);
var readNotification = function(url, notification_id, callbacks){
var $obj = $("#notification_"+notification_id);
$obj.removeClass('unread');
$obj.find('.read-notification').remove();
alert("readNotification failure");
var postData = {'_method': 'put'};
* Autocomplete functionality
// Custom search function for the DataSource of users
var autocompleteMatchUsers = function (sQuery, myUsers) {
// Case insensitive matching
var query = sQuery.toLowerCase();
var i = 0;
var l = myUsers.length;
var matches = [];
// Match against each name of each contact
for (; i < l; i++) {
var contact = myUsers[i];
if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
matches[matches.length] = contact;
return matches;
// Custom search function for the DataSource of userGroups
var autocompleteMatchGroups = function (sQuery, myGroups) {
var l = myGroups.length;
// Match against each name of each group
var matched_group = myGroups[i];
if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
matches[matches.length] = matched_group;
// Helper highlight function for the formatter
var autocompleteHighlightMatch = function (full, snippet, matchindex) {
return full.substring(0, matchindex)
+ "<span class='match'>"
+ full.substr(matchindex, snippet.length)
+ "</span>" + full.substring(matchindex + snippet.length);
var gravatar = function(link, size, cssclass) {
var elem = '<img alt="" class="{2}" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, link, cssclass);
if (!link) {
elem = '<i class="icon-user {1}" style="font-size: {0}px;"></i>'.format(size, cssclass);
return elem;
var autocompleteGravatar = function(res, link, size, group) {
var elem = gravatar(link, size, "perm-gravatar-ac");
if (group !== undefined) {
elem = '<i class="perm-gravatar-ac icon-users"></i>';
return '<div class="ac-container-wrap">{0}{1}</div>'.format(elem, res);
// Custom formatter to highlight the matching letters
var autocompleteFormatter = function (oResultData, sQuery, sResultMatch) {
// group
if (oResultData.grname != undefined) {
var grname = oResultData.grname;
var grmembers = oResultData.grmembers;
var grnameMatchIndex = grname.toLowerCase().indexOf(query);
var grprefix = "{0}: ".format(_TM['Group']);
var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
if (grnameMatchIndex > -1) {
return autocompleteGravatar(grprefix + autocompleteHighlightMatch(grname, query, grnameMatchIndex) + grsuffix, null, null, true);
## -*- coding: utf-8 -*-
<%inherit file="/base/base.html"/>
<%block name="title">
${_('Edit Gist')} · ${c.gist.gist_access_id}
</%block>
<%block name="js_extra">
<script type="text/javascript" src="${h.url('/codemirror/lib/codemirror.js')}"></script>
<script type="text/javascript" src="${h.url('/js/codemirror_loadmode.js')}"></script>
<script type="text/javascript" src="${h.url('/codemirror/mode/meta.js')}"></script>
<%block name="css_extra">
<link rel="stylesheet" type="text/css" href="${h.url('/codemirror/lib/codemirror.css')}"/>
<%def name="breadcrumbs_links()">
</%def>
<%block name="header_menu">
${self.menu('gists')}
<%def name="main()">
<div class="box">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
</div>
<div class="table">
<div id="edit_error" style="display: none" class="flash_msg">
<div class="alert alert-dismissable alert-warning">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true"><i class="icon-cancel-circled"></i></button>
${h.literal(_('Gist was update since you started editing. Copy your changes and click %(here)s to reload new version.')
% {'here': h.link_to('here',h.url('edit_gist', gist_id=c.gist.gist_access_id))})}
<script>
if (typeof jQuery != 'undefined') {
$(".alert").alert();
</script>
<div id="files_data">
${h.form(h.url('edit_gist', gist_id=c.gist.gist_access_id), method='post', id='eform')}
<div>
<div class="gravatar">
${h.gravatar(c.authuser.email, size=32)}
<input type="hidden" value="${c.file_changeset.raw_id}" name="parent_hash">
<textarea style="resize:vertical; width:400px;border: 1px solid #ccc;border-radius: 3px;"
id="description" name="description"
placeholder="${_('Gist description ...')}">${c.gist.gist_description}</textarea>
<div style="padding:0px 0px 0px 42px">
<label for='lifetime'>${_('Gist lifetime')}</label>
${h.select('lifetime', '0', c.lifetime_options)}
<span class="" style="color: #AAA">
%if c.gist.gist_expires == -1:
${_('Expires')}: ${_('Never')}
%else:
${_('Expires')}: ${h.age(h.time_to_datetime(c.gist.gist_expires))}
%endif
</span>
% for cnt, file in enumerate(c.files):
<div id="body" class="codeblock" style="margin-bottom: 4px">
<div style="padding: 10px 10px 10px 26px;color:#666666">
<input type="hidden" value="${file.path}" name="org_files">
<input id="filename_${h.FID('f',file.path)}" name="files" size="30" type="text" value="${file.path}">
<select id="mimetype_${h.FID('f',file.path)}" name="mimetypes"/>
<div class="editor_container">
<pre id="editor_pre"></pre>
<textarea id="editor_${h.FID('f',file.path)}" name="contents" style="display:none">${file.content}</textarea>
## dynamic edit box.
<script type="text/javascript">
var myCodeMirror = initCodeMirror("editor_${h.FID('f',file.path)}", '');
var myCodeMirror = initCodeMirror("editor_${h.FID('f',file.path)}", "${request.script_name}", '');
//inject new modes
var $modes_select = $('#mimetype_${h.FID('f',file.path)}');
$modes_select.each(function(){
var modes_select = this;
var index = 1;
for(var i=0;i<CodeMirror.modeInfo.length;i++) {
var m = CodeMirror.modeInfo[i];
var opt = new Option(m.name, m.mime);
$(opt).attr('mode', m.mode);
if (m.mime == 'text/plain') {
// default plain text
$(opt).prop('selected', true);
modes_select.options[0] = opt;
modes_select.options[index++] = opt;
var $filename = $('#filename_${h.FID('f',file.path)}');
// on select change set new mode
$modes_select.change(function(e){
var selected = e.currentTarget;
var node = selected.options[selected.selectedIndex];
var detected_mode = CodeMirror.findModeByMIME(node.value);
setCodeMirrorMode(myCodeMirror, detected_mode);
var proposed_ext = CodeMirror.findExtensionByMode(detected_mode);
var file_data = CodeMirror.getFilenameAndExt($filename.val());
var filename = file_data['filename'] || 'filename1';
$filename.val(filename + '.' + proposed_ext);
// on type the new filename set mode
$filename.keyup(function(e){
var file_data = CodeMirror.getFilenameAndExt(this.value);
if(file_data['ext'] != null){
var detected_mode = CodeMirror.findModeByExtension(file_data['ext']) || CodeMirror.findModeByMIME('text/plain');
if (detected_mode){
$modes_select.val(detected_mode.mime);
// set mode on page load
var detected_mode = CodeMirror.findModeByExtension("${file.extension}");
%endfor
<div style="padding-top: 5px">
${h.submit('update',_('Update Gist'),class_="btn btn-mini btn-success")}
<a class="btn btn-mini" href="${h.url('gist', gist_id=c.gist.gist_access_id)}">${_('Cancel')}</a>
${h.end_form()}
$('#update').on('click', function(e){
e.preventDefault();
// check for newer version.
$.ajax({
url: "${h.url('edit_gist_check_revision', gist_id=c.gist.gist_access_id)}",
data: {'revision': '${c.file_changeset.raw_id}', '_authentication_token': _authentication_token},
dataType: 'json',
type: 'POST',
success: function(data) {
if(data.success == false){
$('#edit_error').show();
$('#eform').submit();
${_('New Gist')}
${h.form(h.url('gists'), method='post',id='eform')}
<textarea style="resize:vertical; width:400px;border: 1px solid #ccc;border-radius: 3px;" id="description" name="description" placeholder="${_('Gist description ...')}"></textarea>
${h.select('lifetime', '', c.lifetime_options)}
<div id="body" class="codeblock">
${h.text('filename', size=30, placeholder=_('name this file...'))}
<select id="mimetype" name="mimetype"/>
<div id="editor_container">
<textarea id="editor" name="content" style="display:none"></textarea>
${h.submit('private',_('Create Private Gist'),class_="btn btn-mini btn-success")}
${h.submit('public',_('Create Public Gist'),class_="btn btn-mini")}
${h.reset('reset',_('Reset'),class_="btn btn-mini")}
var myCodeMirror = initCodeMirror('editor', '');
var myCodeMirror = initCodeMirror('editor', "${request.script_name}", '');
var $modes_select = $('#mimetype');
var $filename = $('#filename');
${_('%s Files Add') % c.repo_name}
${self.menu('repositories')}
${_('Add New File')} @ ${h.show_id(c.cs)}
${self.repo_context_bar('files')}
<ul class="links">
<li>
<span><a href="#">${_('Branch')}: ${c.cs.branch}</a></span>
</li>
</ul>
<div class="table" id="edit">
${h.form(h.url.current(),method='post',id='eform',enctype="multipart/form-data", class_="form-horizontal")}
<h3 class="files_location">
${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.raw_id,c.f_path)} /
<span id="filename_container" class="file reviewer_ac">
<input class="input-small" type="text" value="" size="30" name="filename" id="filename" placeholder="${_('Enter filename...')}">
<input type="hidden" value="${c.f_path}" size="30" name="location" id="location">
${_('or')} <div class="btn btn-small" id="upload_file_enable">${_('Upload File')}</div>
<span id="upload_file_container" class="reviewer_ac" style="display:none">
<input type="file" size="20" name="upload_file" id="upload_file">
${_('or')} <div class="btn btn-small" id="file_enable">${_('Create New File')}</div>
</h3>
<div class="code-header" id="set_mode_header">
<label class="commit" for="set_mode">${_('New file mode')}</label>
<select id="set_mode" name="set_mode"/>
<div style="padding: 10px;color:#666666">${_('Commit Message')}</div>
<textarea id="commit" name="message" style="height: 100px;width: 99%;margin-left:4px" placeholder="${c.default_message}"></textarea>
<div style="text-align: left;padding-top: 5px">
${h.submit('commit',_('Commit Changes'),class_="btn btn-small btn-success")}
${h.reset('reset',_('Reset'),class_="btn btn-small")}
var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path)}";
var myCodeMirror = initCodeMirror('editor',reset_url);
var myCodeMirror = initCodeMirror('editor', "${request.script_name}", reset_url);
//inject new modes, based on codeMirrors modeInfo object
$('#set_mode').each(function(){
for(var i=0;i<CodeMirror.modeInfo.length;i++){
$('#set_mode').change(function(e){
var filenameInput = $('#filename');
var file_data = CodeMirror.getFilenameAndExt(filenameInput.val());
filenameInput.val(filename + '.' + proposed_ext);
$('#filename').keyup(function(e){
$('#set_mode').val(detected_mode.mime);
${_('%s File Edit') % c.repo_name}
${_('Edit file')} @ ${h.show_id(c.cs)}
<h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.revision,c.file.path)}</h3>
${h.form(h.url.current(),method='post',id='eform')}
<div class="code-header">
<div class="stats">
<div class="left"><i class="icon-doc-inv"></i></div>
<div class="left item">${h.link_to(h.show_id(c.file.changeset),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</div>
<div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div>
<div class="left item last">${c.file.mimetype}</div>
<div class="buttons">
${h.link_to(_('Show Annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="btn btn-mini")}
${h.link_to(_('Show as Raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="btn btn-mini")}
${h.link_to(_('Download as Raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="btn btn-mini")}
% if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
% if not c.file.is_binary:
${h.link_to(_('Source'),h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="btn btn-mini")}
% endif
<label class="commit" for="set_mode">${_('Editing file')}: ${c.file.unicode_path}</label>
<textarea id="editor" name="content" style="display:none">${h.escape(c.file.content)|n}</textarea>
<textarea id="commit" name="message" style="height: 60px;width: 99%;margin-left:4px" placeholder="${c.default_message}"></textarea>
$(document).ready(function(){
var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.file.path)}";
// try to detect the mode based on the file we edit
var detected_mode = CodeMirror.findModeByExtension("${c.file.extension}");
if(detected_mode){
$($('#set_mode option[value="'+detected_mode.mime+'"]')[0]).prop('selected', true);
$('#set_mode').on('change', function(e){
Status change: