Changeset - 1d600909421e
[Not reviewed]
default
0 2 0
Mads Kiilerich - 8 years ago 2017-11-12 21:52:04
mads@kiilerich.com
pullrequest: avoid empty lines in reviewer list after long lines

Whitespace between the reviewer name and a hidden input element could cause
line wrapping and leave an apparently empty line.

Work around that by putting the empty element at the beginning of the line.
2 files changed with 2 insertions and 2 deletions:
0 comments (0 inline, 0 general)
kallithea/public/js/base.js
Show inline comments
 
@@ -1181,200 +1181,200 @@ var MembersAutoComplete = function ($inp
 
        // e.choice.id is automatically used as selection value - just set the type of the selection
 
        $typeElement.val(e.choice.type);
 
    });
 
}
 

	
 
var MentionsAutoComplete = function ($inputElement, users_list) {
 
    var $container = $('<div/>').insertAfter($inputElement);
 

	
 
    var matchUsers = function (sQuery) {
 
            // use the search string from $inputElement instead of sQuery
 
            if(!$container.data('search')){
 
                // return empty list so the input list isn't shown
 
                return []
 
            }
 
            return autocompleteMatchUsers($container.data('search'), users_list);
 
    }
 

	
 
    var datasource = new YAHOO.util.FunctionDataSource(matchUsers);
 
    var mentionsAC = new YAHOO.widget.AutoComplete($inputElement[0], $container[0], datasource);
 
    mentionsAC.useShadow = false;
 
    mentionsAC.resultTypeList = false;
 
    mentionsAC.animVert = false;
 
    mentionsAC.animHoriz = false;
 
    mentionsAC.animSpeed = 0.1;
 
    mentionsAC.suppressInputUpdate = true;
 
    mentionsAC.formatResult = function (oResultData, sQuery, sResultMatch) {
 
        // use the search string from $inputElement instead of sQuery
 
        return autocompleteFormatter(oResultData, $container.data('search'), sResultMatch);
 
    }
 

	
 
    // Handler for selection of an entry
 
    if(mentionsAC.itemSelectEvent){
 
        mentionsAC.itemSelectEvent.subscribe(function (sType, aArgs) {
 
            var myAC = aArgs[0]; // reference back to the AC instance
 
            var elLI = aArgs[1]; // reference to the selected LI element
 
            var oData = aArgs[2]; // object literal of selected item's result data
 
            myAC.getInputEl().value = $container.data('before') + oData.nname + ' ' + $container.data('after');
 
            _setCaretPosition($(myAC.getInputEl()), myAC.dataSource.before.length + oData.nname.length + 1);
 
        });
 
    }
 

	
 
    // Must match utils2.py MENTIONS_REGEX.
 
    // Operates on a string from char before @ up to cursor.
 
    // Check that the char before @ doesn't look like an email address, and match to end of string.
 
    var mentionRe = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$');
 

	
 
    $inputElement.keyup(function(e){
 
            var currentMessage = $inputElement.val();
 
            var currentCaretPosition = $inputElement[0].selectionStart;
 

	
 
            $container.data('search', '');
 
            var messageBeforeCaret = currentMessage.substr(0, currentCaretPosition);
 
            var lastAtPos = messageBeforeCaret.lastIndexOf('@');
 
            if(lastAtPos >= 0){
 
                // Search from one char before last @ ... if possible
 
                var m = mentionRe.exec(messageBeforeCaret.substr(Math.max(0, lastAtPos - 1)));
 
                if(m){
 
                    $container.data('before', currentMessage.substr(0, lastAtPos + 1));
 
                    $container.data('search', currentMessage.substr(lastAtPos + 1, currentCaretPosition - lastAtPos - 1));
 
                    $container.data('after', currentMessage.substr(currentCaretPosition));
 
                }
 
            }
 
        });
 
}
 

	
 

	
 
// Set caret at the given position in the input element
 
function _setCaretPosition($inputElement, caretPos) {
 
    $inputElement.each(function(){
 
        if(this.createTextRange) { // IE
 
            var range = this.createTextRange();
 
            range.move('character', caretPos);
 
            range.select();
 
        }
 
        else if(this.selectionStart) { // other recent browsers
 
            this.focus();
 
            this.setSelectionRange(caretPos, caretPos);
 
        }
 
        else // last resort - very old browser
 
            this.focus();
 
    });
 
}
 

	
 

	
 
var addReviewMember = function(id,fname,lname,nname,gravatar_link,gravatar_size){
 
    var displayname = nname;
 
    if ((fname != "") && (lname != "")) {
 
        displayname = "{0} {1} ({2})".format(fname, lname, nname);
 
    }
 
    var gravatarelm = gravatar(gravatar_link, gravatar_size, "");
 
    // WARNING: the HTML below is duplicate with
 
    // kallithea/templates/pullrequests/pullrequest_show.html
 
    // If you change something here it should be reflected in the template too.
 
    var element = (
 
        '     <li id="reviewer_{2}">\n'+
 
        '       <span class="reviewers_member">\n'+
 
        '         <input type="hidden" value="{2}" name="review_members" />\n'+
 
        '         <span class="reviewer_status" data-toggle="tooltip" title="not_reviewed">\n'+
 
        '             <i class="icon-circle changeset-status-not_reviewed"></i>\n'+
 
        '         </span>\n'+
 
        (gravatarelm ?
 
        '         {0}\n' :
 
        '')+
 
        '         <span>{1}</span>\n'+
 
        '         <input type="hidden" value="{2}" name="review_members" />\n'+
 
        '         <a href="#" class="reviewer_member_remove" onclick="removeReviewMember({2})">\n'+
 
        '             <i class="icon-minus-circled"></i>\n'+
 
        '         </a> (add not saved)\n'+
 
        '       </span>\n'+
 
        '     </li>\n'
 
        ).format(gravatarelm, displayname, id);
 
    // check if we don't have this ID already in
 
    var ids = [];
 
    $('#review_members').find('li').each(function() {
 
            ids.push(this.id);
 
        });
 
    if(ids.indexOf('reviewer_'+id) == -1){
 
        //only add if it's not there
 
        $('#review_members').append(element);
 
    }
 
}
 

	
 
var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
 
    var $li = $('#reviewer_{0}'.format(reviewer_id));
 
    $li.find('div div').css("text-decoration", "line-through");
 
    $li.find('input').prop('name', 'review_members_removed');
 
    $li.find('.reviewer_member_remove').replaceWith('&nbsp;(remove not saved)');
 
}
 

	
 
/* activate auto completion of users as PR reviewers */
 
var PullRequestAutoComplete = function ($inputElement) {
 
    $inputElement.select2(
 
    {
 
        placeholder: $inputElement.attr('placeholder'),
 
        minimumInputLength: 1,
 
        ajax: {
 
            url: pyroutes.url('users_and_groups_data'),
 
            dataType: 'json',
 
            data: function(term, page){
 
              return {
 
                query: term
 
              };
 
            },
 
            results: function (data, page){
 
              return data;
 
            },
 
            cache: true
 
        },
 
        formatSelection: autocompleteFormatter,
 
        formatResult: autocompleteFormatter,
 
        escapeMarkup: function(m) { return m; },
 
    }).on("select2-selecting", function(e) {
 
        addReviewMember(e.choice.id, e.choice.fname, e.choice.lname, e.choice.nname,
 
                        e.choice.gravatar_lnk, e.choice.gravatar_size);
 
        $inputElement.select2("close");
 
        e.preventDefault();
 
    });
 
}
 

	
 

	
 
function addPermAction(perm_type) {
 
    var template =
 
        '<td><input type="radio" value="{1}.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
 
        '<td><input type="radio" value="{1}.read" checked="checked" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
 
        '<td><input type="radio" value="{1}.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
 
        '<td><input type="radio" value="{1}.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
 
        '<td>' +
 
                '<input class="form-control" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text" placeholder="{2}">' +
 
                '<input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden">' +
 
        '</td>' +
 
        '<td></td>';
 
    var $last_node = $('.last_new_member').last(); // empty tr between last and add
 
    var next_id = $('.new_members').length;
 
    $last_node.before($('<tr class="new_members">').append(template.format(next_id, perm_type, _TM['Type name of user or member to grant permission'])));
 
    MembersAutoComplete($("#perm_new_member_name_"+next_id), $("#perm_new_member_type_"+next_id));
 
}
 

	
 
function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
 
    var success = function (o) {
 
            $('#' + field_id).remove();
 
        };
 
    var failure = function (o) {
 
            alert(_TM['Failed to revoke permission'] + ": " + o.status);
 
        };
 
    var query_params = {};
 
    // put extra data into POST
 
    if (extra_data !== undefined && (typeof extra_data === 'object')){
 
        for(var k in extra_data){
 
            query_params[k] = extra_data[k];
 
        }
 
    }
 

	
 
    if (obj_type=='user'){
 
        query_params['user_id'] = obj_id;
 
        query_params['obj_type'] = 'user';
 
    }
 
    else if (obj_type=='user_group'){
 
        query_params['user_group_id'] = obj_id;
 
        query_params['obj_type'] = 'user_group';
 
    }
 

	
kallithea/templates/pullrequests/pullrequest_show.html
Show inline comments
 
@@ -124,203 +124,203 @@ ${self.repo_context_bar('showpullrequest
 
              </div>
 
            %endif
 
          </div>
 
        </div>
 
        <div class="form-group">
 
          <label>${_('Created on')}:</label>
 
          <div>
 
              <div>${h.fmt_date(c.pull_request.created_on)}</div>
 
          </div>
 
        </div>
 
        <div class="form-group">
 
          <label>${_('Owner')}:</label>
 
          <div class="pr-not-edit">
 
                  ${h.gravatar_div(c.pull_request.owner.email, size=20)}
 
                  <span>${c.pull_request.owner.full_name_and_username}</span><br/>
 
                  <span><a href="mailto:${c.pull_request.owner.email}">${c.pull_request.owner.email}</a></span><br/>
 
          </div>
 
          <div class="pr-do-edit" style="display:none">
 
               ${h.text('owner', class_='form-control', value=c.pull_request.owner.username, placeholder=_('Type name of user'))}
 
          </div>
 
        </div>
 

	
 
        <div class="form-group">
 
          <label>${_('Next iteration')}:</label>
 
            <div>
 
              <div class="msg-div">${c.update_msg}</div>
 
              %if c.avail_revs:
 
              <div id="updaterevs" class="clearfix">
 
                <div id="updaterevs-graph">
 
                  <canvas id="avail_graph_canvas"></canvas>
 
                </div>
 
                <table class="table" id="updaterevs-table">
 
                  %for cnt, cs in enumerate(c.avail_cs):
 
                    <tr id="chg_available_${cnt+1}" class="${'mergerow' if len(cs.parents) > 1 and not (editable and cs.revision in c.avail_revs) else ''}">
 
                      %if c.cs_ranges and cs.revision == c.cs_ranges[-1].revision:
 
                        %if editable:
 
                        <td>
 
                            ${h.radio(name='updaterev', value='', checked=True)}
 
                        </td>
 
                        %endif
 
                        <td colspan="4"><span>${_("Current revision - no change")}</span></td>
 
                      %else:
 
                        %if editable:
 
                        <td>
 
                          ${h.radio(name='updaterev', value=cs.raw_id, style=None if cs.revision in c.avail_revs else 'visibility: hidden')}
 
                        </td>
 
                        %endif
 
                        <td><span data-toggle="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
 
                        <td>${h.link_to(h.show_id(cs),h.url('changeset_home',repo_name=c.cs_repo.repo_name,revision=cs.raw_id), class_='changeset_hash')}</td>
 
                        <td>
 
                          <div class="pull-right">
 
                            %for tag in cs.tags:
 
                              <span class="tagtag" title="${_('Tag %s') % tag}">
 
                                ${h.link_to(tag,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
 
                              </span>
 
                            %endfor
 
                          </div>
 
                          <div class="message">${h.urlify_text(cs.message, c.repo_name)}</div>
 
                        </td>
 
                      %endif
 
                    </tr>
 
                  %endfor
 
                </table>
 
              </div>
 
              <div class="msg-div">(${_("Pull request iterations do not change content once created. Select a revision and save to make a new iteration.")})</div>
 
              %endif
 
              <div class="msg-div">${c.update_msg_other}</div>
 
            </div>
 
        </div>
 
        %if editable:
 
        <div class="form-group">
 
          <div class="buttons">
 
            ${h.submit('pr-form-save',_('Save Changes'),class_="btn btn-default btn-sm")}
 
            ${h.submit('pr-form-clone',_('Create New Iteration with Changes'),class_="btn btn-default btn-sm",disabled='disabled')}
 
            ${h.reset('pr-form-reset',_('Cancel Changes'),class_="btn btn-default btn-sm")}
 
          </div>
 
        </div>
 
        %endif
 
      </div>
 
    </div>
 
    ## REVIEWERS
 
    <div class="pull-left">
 
        <h4 class="pr-details-title">${_('Pull Request Reviewers')}</h4>
 
        <div id="reviewers">
 
          ## members goes here !
 
          <div>
 
            %for member,status in c.pull_request_reviewers:
 
              <input type="hidden" value="${member.user_id}" name="org_review_members" />
 
            %endfor
 
            <ul id="review_members" class="list-unstyled">
 
            %for member,status in c.pull_request_reviewers:
 
              ## WARNING: the HTML below is duplicate with
 
              ## kallithea/public/js/base.js
 
              ## If you change something here it should be reflected in the template too.
 
              <li id="reviewer_${member.user_id}">
 
                <span class="reviewers_member">
 
                  <input type="hidden" value="${member.user_id}" name="review_members" />
 
                  <span class="reviewer_status" data-toggle="tooltip" title="${h.changeset_status_lbl(status)}">
 
                      <i class="icon-circle changeset-status-${status}"></i>
 
                  </span>
 
                  ${h.gravatar(member.email, size=14)}
 
                  <span>
 
                    ${member.full_name_and_username}
 
                    %if c.pull_request.owner_id == member.user_id:
 
                      (${_('Owner')})
 
                    %endif
 
                  </span>
 
                  <input type="hidden" value="${member.user_id}" name="review_members" />
 
                  %if editable:
 
                  <a href="#" class="reviewer_member_remove" onclick="removeReviewMember(${member.user_id})" title="${_('Remove reviewer')}">
 
                      <i class="icon-minus-circled"></i>
 
                  </a>
 
                  %endif
 
                </span>
 
              </li>
 
            %endfor
 
            </ul>
 
          </div>
 
          %if editable:
 
          <div>
 
             ${h.text('user', class_='form-control',placeholder=_('Type name of reviewer to add'))}
 
          </div>
 
          %endif
 
        </div>
 

	
 
        %if not c.pull_request_reviewers:
 
        <h4>${_('Potential Reviewers')}</h4>
 
        <div>
 
          <div>
 
            ${_('Click to add the repository owner as reviewer:')}
 
          </div>
 
          <ul class="list-unstyled">
 
            %for u in [c.pull_request.other_repo.owner]:
 
              <li>
 
                <a class="missing_reviewer missing_reviewer_${u.user_id}"
 
                  href="#"
 
                  data-user_id="${u.user_id}"
 
                  data-fname="${u.name}"
 
                  data-lname="${u.lastname}"
 
                  data-nname="${u.username}"
 
                  data-gravatar_lnk="${h.gravatar_url(u.email, size=28, default='default')}"
 
                  data-gravatar_size="14"
 
                  title="Click to add reviewer to the list, then Save Changes.">${u.full_name}</a>
 
              </li>
 
            %endfor
 
          </ul>
 
        </div>
 
        %endif
 
    </div>
 
  ${h.end_form()}
 
</div>
 

	
 
<div class="panel panel-primary">
 
    <div class="panel-heading clearfix">
 
      <div class="breadcrumbs">${_('Pull Request Content')}</div>
 
    </div>
 
    <div class="panel-body">
 
        <div>
 
            <div id="changeset_compare_view_content">
 
              <h5>
 
                  ${comment.comment_count(c.inline_cnt, len(c.comments))}
 
              </h5>
 
              ##CS
 
              <h5>
 
                ${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}
 
              </h5>
 
              <%include file="/compare/compare_cs.html" />
 

	
 
              <h5>
 
              ${_('Common ancestor')}:
 
              ${h.link_to(h.short_id(c.a_rev),h.url('changeset_home',repo_name=c.a_repo.repo_name,revision=c.a_rev), class_="changeset_hash")}
 
              </h5>
 

	
 
              ## FILES
 
              <h5>
 
              % if c.limited_diff:
 
                  ${ungettext('%s file changed', '%s files changed', len(c.file_diff_data)) % len(c.file_diff_data)}:
 
              % else:
 
                  ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.file_diff_data)) % (len(c.file_diff_data),c.lines_added,c.lines_deleted)}:
 
              %endif
 
              </h5>
 
              <div class="cs_files">
 
                %if not c.file_diff_data:
 
                   <span class="text-muted">${_('No files')}</span>
 
                %endif
 
                %for fid, url_fid, op, a_path, path, diff, stats in c.file_diff_data:
 
                    <div class="cs_${op} clearfix">
 
                      <span class="node">
 
                          <i class="icon-diff-${op}"></i>
 
                          ${h.link_to(h.safe_unicode(path), '#%s' % fid)}
 
                      </span>
 
                      <div class="changes">${h.fancy_file_stats(stats)}</div>
 
                    </div>
 
                %endfor
 
                %if c.limited_diff:
 
                  <h5>${_('Changeset was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}">${_('Show full diff anyway')}</a></h5>
 
                %endif
 
              </div>
 
            </div>
 
        </div>
 
    </div>
 
    <script>
 
    var _USERS_AC_DATA = ${h.js(c.users_array)};
 
    // TODO: switch this to pyroutes
0 comments (0 inline, 0 general)