Changeset - 190cb30841de
[Not reviewed]
default
0 4 0
Mads Kiilerich - 9 years ago 2016-07-28 16:34:49
madski@unity3d.com
branches: fix performance of branch selectors with many branches - only show the first 200 results

The way we use select2, it will cause browser performance problems when a
select list contains thousands of entries. The primary bottleneck is the DOM
creation, secondarily for the query to filter through the entries and decide
what to show. We thus primarily have to limit how many entries we put in the
drop-down, secondarily limit the iteration over data.

One tricky case is where the user specifies a short but full branch name (like
'trunk') but many other branches contains the same string (not necessarily at
the beginning, like 'for-trunk-next-week') which come before the perfect match
in the branch list. It is thus not a solution to just stop searching when a
fixed amount of matches have been found.

Instead, we limit the amount of ordinary query matches, but always show all
prefix matches. We thus always have to iterate through all entries, but we
start using the (presumably) cheaper prefix search when the limit has been
reached.

There is no filtering initially when there is no query term, so that case has
to be handled specially.

Upstream select2 is now at 4.x. Upgrading is not trivial, and getting this
fixed properly upstream is not a short term solution. Instead, we customize our
copy. The benefit from this patch is bigger than the overhead of "maintaining"
it locally.
4 files changed with 25 insertions and 4 deletions:
0 comments (0 inline, 0 general)
kallithea/public/js/select2/select2.js
Show inline comments
 
@@ -1004,266 +1004,285 @@ the specific language governing permissi
 
                if (opts.formatLoading != null) {
 
                    console.warn(
 
                        'Select2: The `formatLoading` option has been renamed to `language.loadingMore` in Select2 4.0.0.'
 
                    );
 
                }
 
                if (opts.formatSelectionTooBig != null) {
 
                    console.warn(
 
                        'Select2: The `formatSelectionTooBig` option has been renamed to `language.maximumSelected` in Select2 4.0.0.'
 
                    );
 
                }
 

	
 
                if (opts.element.data('select2Tags')) {
 
                    console.warn(
 
                        'Select2: The `data-select2-tags` attribute has been renamed to `data-tags` in Select2 4.0.0.'
 
                    );
 
                }
 
            }
 

	
 
            // Aliasing options renamed in Select2 4.0.0
 

	
 
            // data-select2-tags -> data-tags
 
            if (opts.element.data('tags') != null) {
 
                var elemTags = opts.element.data('tags');
 

	
 
                // data-tags should actually be a boolean
 
                if (!$.isArray(elemTags)) {
 
                    elemTags = [];
 
                }
 

	
 
                opts.element.data('select2Tags', elemTags);
 
            }
 

	
 
            // sortResults -> sorter
 
            if (opts.sorter != null) {
 
                opts.sortResults = opts.sorter;
 
            }
 

	
 
            // selectOnBlur -> selectOnClose
 
            if (opts.selectOnClose != null) {
 
                opts.selectOnBlur = opts.selectOnClose;
 
            }
 

	
 
            // ajax.results -> ajax.processResults
 
            if (opts.ajax != null) {
 
                if ($.isFunction(opts.ajax.processResults)) {
 
                    opts.ajax.results = opts.ajax.processResults;
 
                }
 
            }
 

	
 
            // Formatters/language options
 
            if (opts.language != null) {
 
                var lang = opts.language;
 

	
 
                // formatNoMatches -> language.noMatches
 
                if ($.isFunction(lang.noMatches)) {
 
                    opts.formatNoMatches = lang.noMatches;
 
                }
 

	
 
                // formatSearching -> language.searching
 
                if ($.isFunction(lang.searching)) {
 
                    opts.formatSearching = lang.searching;
 
                }
 

	
 
                // formatInputTooShort -> language.inputTooShort
 
                if ($.isFunction(lang.inputTooShort)) {
 
                    opts.formatInputTooShort = lang.inputTooShort;
 
                }
 

	
 
                // formatInputTooLong -> language.inputTooLong
 
                if ($.isFunction(lang.inputTooLong)) {
 
                    opts.formatInputTooLong = lang.inputTooLong;
 
                }
 

	
 
                // formatLoading -> language.loadingMore
 
                if ($.isFunction(lang.loadingMore)) {
 
                    opts.formatLoading = lang.loadingMore;
 
                }
 

	
 
                // formatSelectionTooBig -> language.maximumSelected
 
                if ($.isFunction(lang.maximumSelected)) {
 
                    opts.formatSelectionTooBig = lang.maximumSelected;
 
                }
 
            }
 

	
 
            opts = $.extend({}, {
 
                populateResults: function(container, results, query) {
 
                    var populate, id=this.opts.id, liveRegion=this.liveRegion;
 

	
 
                    populate=function(results, container, depth) {
 

	
 
                        var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted;
 

	
 
                        results = opts.sortResults(results, container, query);
 

	
 
                        // collect the created nodes for bulk append
 
                        var nodes = [];
 
                        for (i = 0, l = results.length; i < l; i = i + 1) {
 

	
 
                        // Kallithea customization: maxResults
 
                        l = results.length;
 
                        if (query.term.length == 0 && l > opts.maxResults) {
 
                            l = opts.maxResults;
 
                        }
 
                        for (i = 0; i < l; i = i + 1) {
 

	
 
                            result=results[i];
 

	
 
                            disabled = (result.disabled === true);
 
                            selectable = (!disabled) && (id(result) !== undefined);
 

	
 
                            compound=result.children && result.children.length > 0;
 

	
 
                            node=$("<li></li>");
 
                            node.addClass("select2-results-dept-"+depth);
 
                            node.addClass("select2-result");
 
                            node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
 
                            if (disabled) { node.addClass("select2-disabled"); }
 
                            if (compound) { node.addClass("select2-result-with-children"); }
 
                            node.addClass(self.opts.formatResultCssClass(result));
 
                            node.attr("role", "presentation");
 

	
 
                            label=$(document.createElement("div"));
 
                            label.addClass("select2-result-label");
 
                            label.attr("id", "select2-result-label-" + nextUid());
 
                            label.attr("role", "option");
 

	
 
                            formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup);
 
                            if (formatted!==undefined) {
 
                                label.html(formatted);
 
                                node.append(label);
 
                            }
 

	
 

	
 
                            if (compound) {
 
                                innerContainer=$("<ul></ul>");
 
                                innerContainer.addClass("select2-result-sub");
 
                                populate(result.children, innerContainer, depth+1);
 
                                node.append(innerContainer);
 
                            }
 

	
 
                            node.data("select2-data", result);
 
                            nodes.push(node[0]);
 
                        }
 

	
 
                        if (results.length >= opts.maxResults) {
 
                            nodes.push($('<li class="select2-no-results"><div class="select2-result-label">Too many matches found</div></li>'));
 
                        }
 

	
 
                        // bulk append the created nodes
 
                        container.append(nodes);
 
                        liveRegion.text(opts.formatMatches(results.length));
 
                    };
 

	
 
                    populate(results, container, 0);
 
                }
 
            }, $.fn.select2.defaults, opts);
 

	
 
            if (typeof(opts.id) !== "function") {
 
                idKey = opts.id;
 
                opts.id = function (e) { return e[idKey]; };
 
            }
 

	
 
            if ($.isArray(opts.element.data("select2Tags"))) {
 
                if ("tags" in opts) {
 
                    throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id");
 
                }
 
                opts.tags=opts.element.data("select2Tags");
 
            }
 

	
 
            if (select) {
 
                opts.query = this.bind(function (query) {
 
                    // Kallithea customization: maxResults
 
                    var data = { results: [], more: false },
 
                        term = query.term,
 
                        children, placeholderOption, process;
 
                        children, placeholderOption, process,
 
                        maxResults = opts.maxResults || -1,
 
                        termLower = term.toLowerCase();
 

	
 
                    process=function(element, collection) {
 
                        var group;
 
                        if (element.is("option")) {
 
                          if (collection.length < maxResults) {
 
                            if (query.matcher(term, element.text(), element)) {
 
                                collection.push(self.optionToData(element));
 
                            }
 
                          } else {
 
                            if (element.text().toLowerCase().indexOf(termLower) == 0) {
 
                                collection.push(self.optionToData(element));
 
                            }
 
                          }
 
                        } else if (element.is("optgroup")) {
 
                            group=self.optionToData(element);
 
                            element.children().each2(function(i, elm) { process(elm, group.children); });
 
                            if (group.children.length>0) {
 
                                collection.push(group);
 
                            }
 
                        }
 
                    };
 

	
 
                    children=element.children();
 

	
 
                    // ignore the placeholder option if there is one
 
                    if (this.getPlaceholder() !== undefined && children.length > 0) {
 
                        placeholderOption = this.getPlaceholderOption();
 
                        if (placeholderOption) {
 
                            children=children.not(placeholderOption);
 
                        }
 
                    }
 

	
 
                    children.each2(function(i, elm) { process(elm, data.results); });
 

	
 
                    query.callback(data);
 
                });
 
                // this is needed because inside val() we construct choices from options and their id is hardcoded
 
                opts.id=function(e) { return e.id; };
 
            } else {
 
                if (!("query" in opts)) {
 
                    if ("ajax" in opts) {
 
                        ajaxUrl = opts.element.data("ajax-url");
 
                        if (ajaxUrl && ajaxUrl.length > 0) {
 
                            opts.ajax.url = ajaxUrl;
 
                        }
 
                        opts.query = ajax.call(opts.element, opts.ajax);
 
                    } else if ("data" in opts) {
 
                        opts.query = local(opts.data);
 
                    } else if ("tags" in opts) {
 
                        opts.query = tags(opts.tags);
 
                        if (opts.createSearchChoice === undefined) {
 
                            opts.createSearchChoice = function (term) { return {id: $.trim(term), text: $.trim(term)}; };
 
                        }
 
                        if (opts.initSelection === undefined) {
 
                            opts.initSelection = function (element, callback) {
 
                                var data = [];
 
                                $(splitVal(element.val(), opts.separator, opts.transformVal)).each(function () {
 
                                    var obj = { id: this, text: this },
 
                                        tags = opts.tags;
 
                                    if ($.isFunction(tags)) tags=tags();
 
                                    $(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } });
 
                                    data.push(obj);
 
                                });
 

	
 
                                callback(data);
 
                            };
 
                        }
 
                    }
 
                }
 
            }
 
            if (typeof(opts.query) !== "function") {
 
                throw "query function not defined for Select2 " + opts.element.attr("id");
 
            }
 

	
 
            if (opts.createSearchChoicePosition === 'top') {
 
                opts.createSearchChoicePosition = function(list, item) { list.unshift(item); };
 
            }
 
            else if (opts.createSearchChoicePosition === 'bottom') {
 
                opts.createSearchChoicePosition = function(list, item) { list.push(item); };
 
            }
 
            else if (typeof(opts.createSearchChoicePosition) !== "function")  {
 
                throw "invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function";
 
            }
 

	
 
            return opts;
 
        },
 

	
 
        /**
 
         * Monitor the original element for changes and update select2 accordingly
 
         */
 
        // abstract
 
        monitorSource: function () {
 
            var el = this.opts.element, observer, self = this;
 

	
 
            el.on("change.select2", this.bind(function (e) {
 
                if (this.opts.element.data("select2-change-triggered") !== true) {
 
                    this.initSelection();
 
                }
 
            }));
 

	
 
            this._sync = this.bind(function () {
 

	
 
                // sync enabled state
 
                var disabled = el.prop("disabled");
 
                if (disabled === undefined) disabled = false;
 
                this.enable(!disabled);
 

	
 
                var readonly = el.prop("readonly");
 
                if (readonly === undefined) readonly = false;
kallithea/templates/changelog/changelog.html
Show inline comments
 
@@ -215,123 +215,123 @@ ${self.repo_context_bar('changelog', c.f
 
                            $('#rev_range_container').html(
 
                                 _TM['Show Selected Changesets {0} &rarr; {1}'].format(rev_start.substr(0, 12), rev_end.substr(0, 12)));
 
                            $('#rev_range_container').show();
 
                            $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home',
 
                                                                        {'repo_name': '${c.repo_name}',
 
                                                                         'rev_start': rev_start,
 
                                                                         'rev_end': rev_end}));
 
                            $('#open_new_pr').html(_TM['Open New Pull Request for {0} &rarr; {1}'].format(rev_start.substr(0, 12), rev_end.substr(0, 12)));
 
                        } else {
 
                            $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home',
 
                                                                        {'repo_name': '${c.repo_name}',
 
                                                                         'rev_end': rev_end}));
 
                            $('#open_new_pr').html(_TM['Open New Pull Request from {0}'].format(rev_end.substr(0, 12)));
 
                        }
 
                        $('#rev_range_clear').show();
 
                        $('#compare_fork').hide();
 

	
 
                        var disabled = true;
 
                        $checkboxes.each(function(){
 
                            var $this = $(this);
 
                            if (disabled) {
 
                                if ($this.prop('checked')) {
 
                                    $this.closest('tr').removeClass('out-of-range');
 
                                    disabled = singlerange;
 
                                } else {
 
                                    $this.closest('tr').addClass('out-of-range');
 
                                }
 
                            } else {
 
                                $this.closest('tr').removeClass('out-of-range');
 
                                disabled = $this.prop('checked');
 
                            }
 
                        });
 

	
 
                        if ($checked_checkboxes.length + (singlerange ? 1 : 0) >= 2) {
 
                            $checkboxes.hide();
 
                            $checked_checkboxes.show();
 
                            if (!singlerange)
 
                                $singlerange.hide();
 
                        }
 
                    } else {
 
                        $('#singlerange').hide().prop('checked', false);
 
                        $('#rev_range_clear').hide();
 
                        %if c.revision:
 
                            $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home',
 
                                                                        {'repo_name': '${c.repo_name}',
 
                                                                         'rev_end':'${c.first_revision.raw_id}'}));
 
                            $('#open_new_pr').html(_TM['Open New Pull Request from {0}'].format('${c.revision}'));
 
                        %else:
 
                            $('#open_new_pr').prop('href', pyroutes.url('pullrequest_home',
 
                                                                        {'repo_name': '${c.repo_name}',
 
                                                                        'branch':'${c.first_revision.branch}'}));
 
                            $('#open_new_pr').html(_TM['Open New Pull Request from {0}'].format('${c.first_revision.branch}'));
 
                        %endif
 
                        $('#compare_fork').show();
 
                        $checkboxes.closest('tr').removeClass('out-of-range');
 
                    }
 
                };
 
                checkbox_checker();
 
                $checkboxes.click(function() {
 
                    checkbox_checker();
 
                    r.render(jsdata,100);
 
                });
 
                $('#singlerange').click(checkbox_checker);
 

	
 
                $('#rev_range_clear').click(function(e){
 
                    $checkboxes.prop('checked', false);
 
                    checkbox_checker();
 
                    r.render(jsdata,100);
 
                });
 

	
 
                var $msgs = $('.message');
 
                // get first element height
 
                var el = $('#graph_content .container')[0];
 
                var row_h = el.clientHeight;
 
                $msgs.each(function() {
 
                    var m = this;
 

	
 
                    var h = m.clientHeight;
 
                    if(h > row_h){
 
                        var offset = row_h - (h+12);
 
                        $(m.nextElementSibling).css('display', 'block');
 
                        $(m.nextElementSibling).css('margin-top', offset+'px');
 
                    }
 
                });
 

	
 
                $('.expand_commit').on('click',function(e){
 
                    var cid = $(this).attr('commit_id');
 
                    $('#C-'+cid).toggleClass('expanded');
 

	
 
                    //redraw the graph, r and jsdata are bound outside function
 
                    r.render(jsdata,100);
 
                });
 

	
 
                // change branch filter
 
                $("#branch_filter").select2({
 
                    dropdownAutoWidth: true,
 
                    minimumInputLength: 1,
 
                    maxResults: 50,
 
                    sortResults: branchSort
 
                    });
 

	
 
                $("#branch_filter").change(function(e){
 
                    var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
 
                    if(selected_branch != ''){
 
                        window.location = pyroutes.url('changelog_home', {'repo_name': '${c.repo_name}',
 
                                                                          'branch': selected_branch});
 
                    }else{
 
                        window.location = pyroutes.url('changelog_home', {'repo_name': '${c.repo_name}'});
 
                    }
 
                    $("#changelog").hide();
 
                });
 

	
 
                var jsdata = ${c.jsdata|n};
 
                var r = new BranchRenderer('graph_canvas', 'graph_content', 'chg_');
 
                r.render(jsdata,100);
 
            });
 

	
 
        </script>
 
        %else:
 
            ${_('There are no changes yet')}
 
        %endif
 
    </div>
 
</div>
 
</%def>
kallithea/templates/files/files.html
Show inline comments
 
@@ -140,115 +140,115 @@ var callbacks = function(State){
 
          this.scrollIntoView();
 
      });
 
    }
 

	
 
    // select code link event
 
    $('#hlcode').mouseup(getSelectionLink);
 

	
 
    // history select field
 
    var cache = {};
 
    $("#diff1").select2({
 
        placeholder: _TM['Select changeset'],
 
        dropdownAutoWidth: true,
 
        query: function(query){
 
          var key = 'cache';
 
          var cached = cache[key] ;
 
          if(cached) {
 
            var data = {results: []};
 
            //filter results
 
            $.each(cached.results, function(){
 
                var section = this.text;
 
                var children = [];
 
                $.each(this.children, function(){
 
                    if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){
 
                        children.push({'id': this.id, 'text': this.text});
 
                    }
 
                });
 
                data.results.push({'text': section, 'children': children});
 
            });
 
            query.callback(data);
 
          }else{
 
              $.ajax({
 
                url: pyroutes.url('files_history_home', {'revision': _rev, 'f_path': _f_path}),
 
                data: {},
 
                dataType: 'json',
 
                type: 'GET',
 
                success: function(data) {
 
                  cache[key] = data;
 
                  query.callback({results: data.results});
 
                }
 
              });
 
          }
 
        }
 
    });
 
    $('#show_authors').on('click', function(){
 
        $.ajax({
 
            url: pyroutes.url('files_authors_home', {'revision': _rev, 'f_path': _f_path}),
 
            success: function(data) {
 
                $('#file_authors').html(data);
 
                $('#file_authors').show();
 
                tooltip_activate();
 
            }
 
        });
 
    });
 
}
 

	
 
$(document).ready(function(){
 
    ypjax_links();
 
    var $files_data = $('#files_data');
 
    //Bind to StateChange Event
 
    History.Adapter.bind(window,'statechange',function(){
 
        var State = History.getState();
 
        cache_key = State.url;
 
        //check if we have this request in cache maybe ?
 
        var _cache_obj = CACHE[cache_key];
 
        var _cur_time = new Date().getTime();
 
        // get from cache if it's there and not yet expired !
 
        if(_cache_obj !== undefined && _cache_obj[0] > _cur_time){
 
            $files_data.html(_cache_obj[1]);
 
            $files_data.css('opacity','1.0');
 
            //callbacks after ypjax call
 
            callbacks(State);
 
        }
 
        else{
 
            asynchtml(State.url, $files_data, function(){
 
                    callbacks(State);
 
                    var expire_on = new Date().getTime() + CACHE_EXPIRE;
 
                    CACHE[cache_key] = [expire_on, $files_data.html()];
 
                });
 
        }
 
    });
 

	
 
    // init the search filter
 
    var _State = {
 
       url: "${h.url.current()}",
 
       data: {
 
         node_list_url: node_list_url.replace('__REV__',"${c.changeset.raw_id}").replace('__FPATH__', "${h.safe_unicode(c.file.path)}"),
 
         url_base: url_base.replace('__REV__',"${c.changeset.raw_id}"),
 
         rev:"${c.changeset.raw_id}",
 
         f_path: "${h.safe_unicode(c.file.path)}"
 
       }
 
    }
 
    fileBrowserListeners(_State.url, _State.data.node_list_url, _State.data.url_base);
 

	
 
    // change branch filter
 
    $("#branch_selector").select2({
 
        dropdownAutoWidth: true,
 
        minimumInputLength: 1,
 
        maxResults: 50,
 
        sortResults: branchSort
 
        });
 

	
 
    $("#branch_selector").change(function(e){
 
        var selected = e.currentTarget.options[e.currentTarget.selectedIndex].value;
 
        if(selected && selected != "${c.changeset.raw_id}"){
 
            window.location = pyroutes.url('files_home', {'repo_name': "${h.safe_unicode(c.repo_name)}", 'revision': selected, 'f_path': "${h.safe_unicode(c.file.path)}"});
 
            $("#body.browserblock").hide();
 
        } else {
 
            $("#branch_selector").val("${c.changeset.raw_id}");
 
        }
 
    });
 

	
 
});
 

	
 
</script>
 

	
 
</%def>
kallithea/templates/pullrequests/pullrequest.html
Show inline comments
 
@@ -109,124 +109,126 @@ ${self.repo_context_bar('showpullrequest
 

	
 
<script type="text/javascript" src="${h.url('/js/graph.js', ver=c.kallithea_version)}"></script>
 
<script type="text/javascript">
 
  pyroutes.register('pullrequest_repo_info', "${url('pullrequest_repo_info',repo_name='%(repo_name)s')}", ['repo_name']);
 

	
 
  var pendingajax = undefined;
 
  var otherrepoChanged = function(){
 
      var $other_ref = $('#other_ref');
 
      $other_ref.prop('disabled', true);
 
      var repo_name = $('#other_repo').val();
 
      if (pendingajax) {
 
          pendingajax.abort();
 
          pendingajax = undefined;
 
      }
 
      pendingajax = ajaxGET(pyroutes.url('pullrequest_repo_info', {"repo_name": repo_name}),
 
          function(data){
 
              pendingajax = undefined;
 
              $('#other_repo_desc').html(data.description);
 

	
 
              // replace options of other_ref with the ones for the current other_repo
 
              $other_ref.empty();
 
              for(var i = 0; i < data.refs.length; i++)
 
              {
 
                var $optgroup = $('<optgroup/>').prop('label', data.refs[i][1]);
 
                var options = data.refs[i][0];
 
                var length = options.length;
 
                for(var j = 0; j < length; j++)
 
                {
 
                  $optgroup.append($('<'+'option/>').text(options[j][1]).val(options[j][0]));
 
                }
 
                $other_ref.append($optgroup);
 
              }
 
              $other_ref.val(data.selected_ref);
 

	
 
              // re-populate the select2 thingy
 
              $("#other_ref").select2({
 
                  dropdownAutoWidth: true
 
              });
 

	
 
              $other_ref.prop('disabled', false);
 
              loadPreview();
 
          });
 
  };
 

	
 
  var loadPreview = function(){
 
      //url template
 
      var url = "${h.url('compare_url',
 
                         repo_name='__other_repo__',
 
                         org_ref_type='rev',
 
                         org_ref_name='__other_ref_name__',
 
                         other_repo='__org_repo__',
 
                         other_ref_type='rev',
 
                         other_ref_name='__org_ref_name__',
 
                         as_form=True,
 
                         merge=True,
 
                         )}";
 
      var org_repo = $('#pull_request_form #org_repo').val();
 
      var org_ref = $('#pull_request_form #org_ref').val().split(':');
 
      ## TODO: make nice link like link_to_ref() do
 
      $('#org_rev_span').html(org_ref[2].substr(0,12));
 

	
 
      var other_repo = $('#pull_request_form #other_repo').val();
 
      var other_ref = $('#pull_request_form #other_ref').val().split(':');
 
      $('#other_rev_span').html(other_ref[2].substr(0,12));
 

	
 
      var rev_data = {
 
          '__org_repo__': org_repo,
 
          '__org_ref_name__': org_ref[2],
 
          '__other_repo__': other_repo,
 
          '__other_ref_name__': other_ref[2]
 
      }; // gather the org/other ref and repo here
 

	
 
      for (k in rev_data){
 
          url = url.replace(k,rev_data[k]);
 
      }
 

	
 
      if (pendingajax) {
 
          pendingajax.abort();
 
          pendingajax = undefined;
 
      }
 
      pendingajax = asynchtml(url, $('#pull_request_overview'), function(o){
 
          pendingajax = undefined;
 
          var jsdata = eval('('+$('#jsdata').html()+')'); // TODO: just get json
 
          var r = new BranchRenderer('graph_canvas', 'graph_content_pr', 'chg_');
 
          r.render(jsdata,100);
 
      });
 
  }
 

	
 
  $(document).ready(function(){
 
      $("#org_repo").select2({
 
          dropdownAutoWidth: true
 
      });
 
      ## (org_repo can't change)
 

	
 
      $("#org_ref").select2({
 
          dropdownAutoWidth: true,
 
          maxResults: 50,
 
          sortResults: branchSort
 
      });
 
      $("#org_ref").on("change", function(e){
 
          loadPreview();
 
      });
 

	
 
      $("#other_repo").select2({
 
          dropdownAutoWidth: true
 
      });
 
      $("#other_repo").on("change", function(e){
 
          otherrepoChanged();
 
      });
 

	
 
      $("#other_ref").select2({
 
          dropdownAutoWidth: true,
 
          maxResults: 50,
 
          sortResults: branchSort
 
      });
 
      $("#other_ref").on("change", function(e){
 
          loadPreview();
 
      });
 

	
 
      //lazy load overview after 0.5s
 
      setTimeout(loadPreview, 500);
 
  });
 

	
 
</script>
 

	
 
</%def>
0 comments (0 inline, 0 general)