microversion selector - dropdown
This does a few things: * Change from button list to a select combo box. ** This allows users to type the number they want * Add the ablity to tag versions to releases ** E.G. Say mitaka was released with 2.27, it will show in the list as "2.27 - Mitaka". ** This currently set as a config option in conf.py as a dict like: os_api_ref_release_microversions = { '2.27' : 'mitaka', '2.10': 'liberty' } * Uses https://github.com/danielfarrell/bootstrap-combobox ** Apache Licenced Change-Id: Ica7dd55cdbf413f03ea635aefb627a705132a73e
This commit is contained in:
@@ -133,12 +133,14 @@ class RestExpandAllDirective(Directive):
|
|||||||
node = rest_expand_all()
|
node = rest_expand_all()
|
||||||
max_ver = app.config.os_api_ref_max_microversion
|
max_ver = app.config.os_api_ref_max_microversion
|
||||||
min_ver = app.config.os_api_ref_min_microversion
|
min_ver = app.config.os_api_ref_min_microversion
|
||||||
|
releases = app.config.os_api_ref_release_microversions
|
||||||
node['major'] = None
|
node['major'] = None
|
||||||
try:
|
try:
|
||||||
if max_ver.split('.')[0] == min_ver.split('.')[0]:
|
if max_ver.split('.')[0] == min_ver.split('.')[0]:
|
||||||
node['max_ver'] = int(max_ver.split('.')[1])
|
node['max_ver'] = int(max_ver.split('.')[1])
|
||||||
node['min_ver'] = int(min_ver.split('.')[1])
|
node['min_ver'] = int(min_ver.split('.')[1])
|
||||||
node['major'] = int(max_ver.split('.')[0])
|
node['major'] = int(max_ver.split('.')[0])
|
||||||
|
node['releases'] = releases
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# TODO(sdague): warn that we're ignoring this all
|
# TODO(sdague): warn that we're ignoring this all
|
||||||
pass
|
pass
|
||||||
@@ -510,7 +512,9 @@ def rest_expand_all_html(self, node):
|
|||||||
tmpl = """
|
tmpl = """
|
||||||
<div class="row">
|
<div class="row">
|
||||||
%(extra_js)s
|
%(extra_js)s
|
||||||
<div class=col-md-11>%(selector)s</div>
|
<div class="col-md-2 col-md-offset-9">
|
||||||
|
%(selector)s
|
||||||
|
</div>
|
||||||
<div class=col-md-1>
|
<div class=col-md-1>
|
||||||
<button id="expand-all"
|
<button id="expand-all"
|
||||||
data-toggle="collapse"
|
data-toggle="collapse"
|
||||||
@@ -519,28 +523,64 @@ def rest_expand_all_html(self, node):
|
|||||||
</div>
|
</div>
|
||||||
</div>"""
|
</div>"""
|
||||||
|
|
||||||
|
node.setdefault('selector', "")
|
||||||
|
node.setdefault('extra_js', "")
|
||||||
|
|
||||||
if node['major']:
|
if node['major']:
|
||||||
selector = """
|
node['selector'], node['extra_js'] = create_mv_selector(node)
|
||||||
<div class="btn-group" role="group" aria-label="...">
|
|
||||||
<button type="button"
|
|
||||||
class="btn btn-default mv_selector active">All</button>\n"""
|
|
||||||
for x in range(node['min_ver'], node['max_ver'] + 1):
|
|
||||||
selector += ('<button type="button" class="btn btn-default '
|
|
||||||
'mv_selector">%d.%d</button>\n' % (node['major'], x))
|
|
||||||
selector += "</div>"
|
|
||||||
node['selector'] = selector
|
|
||||||
node['extra_js'] = ("<script>os_max_mv = %d; "
|
|
||||||
"os_min_mv = %d;</script>") % (
|
|
||||||
node['max_ver'],
|
|
||||||
node['min_ver'])
|
|
||||||
else:
|
|
||||||
node['selector'] = ""
|
|
||||||
node['extra_js'] = ""
|
|
||||||
|
|
||||||
self.body.append(tmpl % node)
|
self.body.append(tmpl % node)
|
||||||
raise nodes.SkipNode
|
raise nodes.SkipNode
|
||||||
|
|
||||||
|
|
||||||
|
def create_mv_selector(node):
|
||||||
|
|
||||||
|
mv_list = '<option value="" selected="selected">All</option>'
|
||||||
|
|
||||||
|
for x in range(node['min_ver'], node['max_ver'] + 1):
|
||||||
|
mv_list += build_mv_item(node['major'], x, node['releases'])
|
||||||
|
|
||||||
|
selector_tmpl = """
|
||||||
|
<form class=form-inline">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label">
|
||||||
|
Microversions
|
||||||
|
</label>
|
||||||
|
<select class="combobox form-control" id="mv_select">
|
||||||
|
%(mv_list)s
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
"""
|
||||||
|
|
||||||
|
js_tmpl = """
|
||||||
|
<script>
|
||||||
|
os_max_mv = %(max)d;
|
||||||
|
os_min_mv = %(min)d;
|
||||||
|
</script>
|
||||||
|
"""
|
||||||
|
|
||||||
|
selector_content = {
|
||||||
|
'mv_list': mv_list
|
||||||
|
}
|
||||||
|
|
||||||
|
js_content = {
|
||||||
|
'min': node['min_ver'],
|
||||||
|
'max': node['max_ver']
|
||||||
|
}
|
||||||
|
|
||||||
|
return selector_tmpl % selector_content, js_tmpl % js_content
|
||||||
|
|
||||||
|
|
||||||
|
def build_mv_item(major, micro, releases):
|
||||||
|
version = "%d.%d" % (major, micro)
|
||||||
|
if version in releases:
|
||||||
|
return '<option value="%s">%s - %s</option>' % (
|
||||||
|
version, version, releases[version].capitalize())
|
||||||
|
else:
|
||||||
|
return '<option value="%s">%s</option>' % (version, version)
|
||||||
|
|
||||||
|
|
||||||
def resolve_rest_references(app, doctree):
|
def resolve_rest_references(app, doctree):
|
||||||
for node in doctree.traverse():
|
for node in doctree.traverse():
|
||||||
if isinstance(node, rest_method):
|
if isinstance(node, rest_method):
|
||||||
@@ -574,12 +614,11 @@ def resolve_rest_references(app, doctree):
|
|||||||
|
|
||||||
|
|
||||||
def copy_assets(app, exception):
|
def copy_assets(app, exception):
|
||||||
assets = ('api-site.css', 'api-site.js')
|
assets = ('api-site.css', 'api-site.js', 'combobox.js')
|
||||||
fonts = (
|
fonts = (
|
||||||
'glyphicons-halflings-regular.ttf',
|
'glyphicons-halflings-regular.ttf',
|
||||||
'glyphicons-halflings-regular.woff'
|
'glyphicons-halflings-regular.woff'
|
||||||
)
|
)
|
||||||
|
|
||||||
if app.builder.name != 'html' or exception:
|
if app.builder.name != 'html' or exception:
|
||||||
return
|
return
|
||||||
app.info('Copying assets: %s' % ', '.join(assets))
|
app.info('Copying assets: %s' % ', '.join(assets))
|
||||||
@@ -597,12 +636,14 @@ def copy_assets(app, exception):
|
|||||||
def add_assets(app):
|
def add_assets(app):
|
||||||
app.add_stylesheet('api-site.css')
|
app.add_stylesheet('api-site.css')
|
||||||
app.add_javascript('api-site.js')
|
app.add_javascript('api-site.js')
|
||||||
|
app.add_javascript('combobox.js')
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
# Add some config options around microversions
|
# Add some config options around microversions
|
||||||
app.add_config_value('os_api_ref_max_microversion', '', 'env')
|
app.add_config_value('os_api_ref_max_microversion', '', 'env')
|
||||||
app.add_config_value('os_api_ref_min_microversion', '', 'env')
|
app.add_config_value('os_api_ref_min_microversion', '', 'env')
|
||||||
|
app.add_config_value('os_api_ref_release_microversions', '', 'env')
|
||||||
# TODO(sdague): if someone wants to support latex/pdf, or man page
|
# TODO(sdague): if someone wants to support latex/pdf, or man page
|
||||||
# generation using these stanzas, here is where you'd need to
|
# generation using these stanzas, here is where you'd need to
|
||||||
# specify content specific renderers.
|
# specify content specific renderers.
|
||||||
|
@@ -165,3 +165,53 @@ div.docs-top-contents {
|
|||||||
div.endpoint-container{
|
div.endpoint-container{
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#expand-all {
|
||||||
|
margin-top: 23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
### Combobox Experiment
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.form-search .combobox-container,
|
||||||
|
.form-inline .combobox-container {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 0;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.form-search .combobox-container .input-group-addon,
|
||||||
|
.form-inline .combobox-container .input-group-addon {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.combobox-selected .caret {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/* :not doesn't work in IE8 */
|
||||||
|
.combobox-container:not(.combobox-selected) .glyphicon-remove {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.typeahead-long {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.control-group.error .combobox-container .add-on {
|
||||||
|
color: #B94A48;
|
||||||
|
border-color: #B94A48;
|
||||||
|
}
|
||||||
|
.control-group.error .combobox-container .caret {
|
||||||
|
border-top-color: #B94A48;
|
||||||
|
}
|
||||||
|
.control-group.warning .combobox-container .add-on {
|
||||||
|
color: #C09853;
|
||||||
|
border-color: #C09853;
|
||||||
|
}
|
||||||
|
.control-group.warning .combobox-container .caret {
|
||||||
|
border-top-color: #C09853;
|
||||||
|
}
|
||||||
|
.control-group.success .combobox-container .add-on {
|
||||||
|
color: #468847;
|
||||||
|
border-color: #468847;
|
||||||
|
}
|
||||||
|
.control-group.success .combobox-container .caret {
|
||||||
|
border-top-color: #468847;
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,3 @@
|
|||||||
var os_min_mv = 1;
|
|
||||||
var os_max_mv = 1;
|
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
// the list of expanded element ids
|
// the list of expanded element ids
|
||||||
var expanded = [];
|
var expanded = [];
|
||||||
@@ -163,4 +160,19 @@ var os_max_mv = 1;
|
|||||||
$('[class^=rp_min_ver]').show(400);
|
$('[class^=rp_min_ver]').show(400);
|
||||||
$('[class^=rp_max_ver]').show(400);
|
$('[class^=rp_max_ver]').show(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#mv_select').combobox({appendId: '-visable'});
|
||||||
|
$('#mv_select').on('change', function() {
|
||||||
|
var version = this.value;
|
||||||
|
if (version == "") {
|
||||||
|
reset_microversion();
|
||||||
|
} else {
|
||||||
|
set_microversion(version);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
462
os_api_ref/assets/combobox.js
Normal file
462
os_api_ref/assets/combobox.js
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
/* =============================================================
|
||||||
|
* bootstrap-combobox.js v1.1.7
|
||||||
|
* =============================================================
|
||||||
|
* Copyright 2012 Daniel Farrell
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
!function( $ ) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/* COMBOBOX PUBLIC CLASS DEFINITION
|
||||||
|
* ================================ */
|
||||||
|
|
||||||
|
var Combobox = function ( element, options ) {
|
||||||
|
this.options = $.extend({}, $.fn.combobox.defaults, options);
|
||||||
|
this.template = this.options.template || this.template
|
||||||
|
this.$source = $(element);
|
||||||
|
this.$container = this.setup();
|
||||||
|
this.$element = this.$container.find('input[type=text]');
|
||||||
|
this.$target = this.$container.find('input[type=hidden]');
|
||||||
|
this.$button = this.$container.find('.dropdown-toggle');
|
||||||
|
this.$menu = $(this.options.menu).appendTo('body');
|
||||||
|
this.matcher = this.options.matcher || this.matcher;
|
||||||
|
this.sorter = this.options.sorter || this.sorter;
|
||||||
|
this.highlighter = this.options.highlighter || this.highlighter;
|
||||||
|
this.shown = false;
|
||||||
|
this.selected = false;
|
||||||
|
this.refresh();
|
||||||
|
this.transferAttributes();
|
||||||
|
this.listen();
|
||||||
|
};
|
||||||
|
|
||||||
|
Combobox.prototype = {
|
||||||
|
|
||||||
|
constructor: Combobox
|
||||||
|
|
||||||
|
, setup: function () {
|
||||||
|
var combobox = $(this.template());
|
||||||
|
this.$source.before(combobox);
|
||||||
|
this.$source.hide();
|
||||||
|
return combobox;
|
||||||
|
}
|
||||||
|
|
||||||
|
, disable: function() {
|
||||||
|
this.$element.prop('disabled', true);
|
||||||
|
this.$button.attr('disabled', true);
|
||||||
|
this.disabled = true;
|
||||||
|
this.$container.addClass('combobox-disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
, enable: function() {
|
||||||
|
this.$element.prop('disabled', false);
|
||||||
|
this.$button.attr('disabled', false);
|
||||||
|
this.disabled = false;
|
||||||
|
this.$container.removeClass('combobox-disabled');
|
||||||
|
}
|
||||||
|
, parse: function () {
|
||||||
|
var that = this
|
||||||
|
, map = {}
|
||||||
|
, source = []
|
||||||
|
, selected = false
|
||||||
|
, selectedValue = '';
|
||||||
|
this.$source.find('option').each(function() {
|
||||||
|
var option = $(this);
|
||||||
|
if (option.val() === '') {
|
||||||
|
that.options.placeholder = option.text();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
map[option.text()] = option.val();
|
||||||
|
source.push(option.text());
|
||||||
|
if (option.prop('selected')) {
|
||||||
|
selected = option.text();
|
||||||
|
selectedValue = option.val();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.map = map;
|
||||||
|
if (selected) {
|
||||||
|
this.$element.val(selected);
|
||||||
|
this.$target.val(selectedValue);
|
||||||
|
this.$container.addClass('combobox-selected');
|
||||||
|
this.selected = true;
|
||||||
|
}
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
, transferAttributes: function() {
|
||||||
|
this.options.placeholder = this.$source.attr('data-placeholder') || this.options.placeholder
|
||||||
|
if(this.options.appendId !== "undefined") {
|
||||||
|
this.$element.attr('id', this.$source.attr('id') + this.options.appendId);
|
||||||
|
}
|
||||||
|
this.$element.attr('placeholder', this.options.placeholder)
|
||||||
|
this.$target.prop('name', this.$source.prop('name'))
|
||||||
|
this.$target.val(this.$source.val())
|
||||||
|
this.$source.removeAttr('name') // Remove from source otherwise form will pass parameter twice.
|
||||||
|
this.$element.attr('required', this.$source.attr('required'))
|
||||||
|
this.$element.attr('rel', this.$source.attr('rel'))
|
||||||
|
this.$element.attr('title', this.$source.attr('title'))
|
||||||
|
this.$element.attr('class', this.$source.attr('class'))
|
||||||
|
this.$element.attr('tabindex', this.$source.attr('tabindex'))
|
||||||
|
this.$source.removeAttr('tabindex')
|
||||||
|
if (this.$source.attr('disabled')!==undefined)
|
||||||
|
this.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
, select: function () {
|
||||||
|
var val = this.$menu.find('.active').attr('data-value');
|
||||||
|
this.$element.val(this.updater(val)).trigger('change');
|
||||||
|
this.$target.val(this.map[val]).trigger('change');
|
||||||
|
this.$source.val(this.map[val]).trigger('change');
|
||||||
|
this.$container.addClass('combobox-selected');
|
||||||
|
this.selected = true;
|
||||||
|
return this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
, updater: function (item) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
, show: function () {
|
||||||
|
var pos = $.extend({}, this.$element.position(), {
|
||||||
|
height: this.$element[0].offsetHeight
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$menu
|
||||||
|
.insertAfter(this.$element)
|
||||||
|
.css({
|
||||||
|
top: pos.top + pos.height
|
||||||
|
, left: pos.left
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
|
||||||
|
$('.dropdown-menu').on('mousedown', $.proxy(this.scrollSafety, this));
|
||||||
|
|
||||||
|
this.shown = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
, hide: function () {
|
||||||
|
this.$menu.hide();
|
||||||
|
$('.dropdown-menu').off('mousedown', $.proxy(this.scrollSafety, this));
|
||||||
|
this.$element.on('blur', $.proxy(this.blur, this));
|
||||||
|
this.shown = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
, lookup: function (event) {
|
||||||
|
this.query = this.$element.val();
|
||||||
|
return this.process(this.source);
|
||||||
|
}
|
||||||
|
|
||||||
|
, process: function (items) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
items = $.grep(items, function (item) {
|
||||||
|
return that.matcher(item);
|
||||||
|
})
|
||||||
|
|
||||||
|
items = this.sorter(items);
|
||||||
|
|
||||||
|
if (!items.length) {
|
||||||
|
return this.shown ? this.hide() : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.render(items.slice(0, this.options.items)).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
, template: function() {
|
||||||
|
if (this.options.bsVersion == '2') {
|
||||||
|
return '<div class="combobox-container"><input type="hidden" /> <div class="input-append"> <input type="text" autocomplete="false" /> <span class="add-on dropdown-toggle" data-dropdown="dropdown"> <span class="caret"/> <i class="icon-remove"/> </span> </div> </div>'
|
||||||
|
} else {
|
||||||
|
return '<div class="combobox-container"> <input type="hidden" /> <div class="input-group"> <input type="text" autocomplete="false" /> <span class="input-group-addon dropdown-toggle" data-dropdown="dropdown"> <span class="caret" /> <span class="glyphicon glyphicon-remove" /> </span> </div> </div>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
, matcher: function (item) {
|
||||||
|
return ~item.toLowerCase().indexOf(this.query.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
, sorter: function (items) {
|
||||||
|
var beginswith = []
|
||||||
|
, caseSensitive = []
|
||||||
|
, caseInsensitive = []
|
||||||
|
, item;
|
||||||
|
|
||||||
|
while (item = items.shift()) {
|
||||||
|
if (!item.toLowerCase().indexOf(this.query.toLowerCase())) {beginswith.push(item);}
|
||||||
|
else if (~item.indexOf(this.query)) {caseSensitive.push(item);}
|
||||||
|
else {caseInsensitive.push(item);}
|
||||||
|
}
|
||||||
|
|
||||||
|
return beginswith.concat(caseSensitive, caseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
, highlighter: function (item) {
|
||||||
|
var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
|
||||||
|
return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
|
||||||
|
return '<strong>' + match + '</strong>';
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
, render: function (items) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
items = $(items).map(function (i, item) {
|
||||||
|
i = $(that.options.item).attr('data-value', item);
|
||||||
|
i.find('a').html(that.highlighter(item));
|
||||||
|
return i[0];
|
||||||
|
})
|
||||||
|
|
||||||
|
items.first().addClass('active');
|
||||||
|
this.$menu.html(items);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
, next: function (event) {
|
||||||
|
var active = this.$menu.find('.active').removeClass('active')
|
||||||
|
, next = active.next();
|
||||||
|
|
||||||
|
if (!next.length) {
|
||||||
|
next = $(this.$menu.find('li')[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
next.addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
, prev: function (event) {
|
||||||
|
var active = this.$menu.find('.active').removeClass('active')
|
||||||
|
, prev = active.prev();
|
||||||
|
|
||||||
|
if (!prev.length) {
|
||||||
|
prev = this.$menu.find('li').last();
|
||||||
|
}
|
||||||
|
|
||||||
|
prev.addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
, toggle: function () {
|
||||||
|
if (!this.disabled) {
|
||||||
|
if (this.$container.hasClass('combobox-selected')) {
|
||||||
|
this.clearTarget();
|
||||||
|
this.triggerChange();
|
||||||
|
this.clearElement();
|
||||||
|
} else {
|
||||||
|
if (this.shown) {
|
||||||
|
this.hide();
|
||||||
|
} else {
|
||||||
|
this.clearElement();
|
||||||
|
this.lookup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
, scrollSafety: function(e) {
|
||||||
|
if (e.target.tagName == 'UL') {
|
||||||
|
this.$element.off('blur');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, clearElement: function () {
|
||||||
|
this.$element.val('').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
, clearTarget: function () {
|
||||||
|
this.$source.val('');
|
||||||
|
this.$target.val('');
|
||||||
|
this.$container.removeClass('combobox-selected');
|
||||||
|
this.selected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
, triggerChange: function () {
|
||||||
|
this.$source.trigger('change');
|
||||||
|
}
|
||||||
|
|
||||||
|
, refresh: function () {
|
||||||
|
this.source = this.parse();
|
||||||
|
this.options.items = this.source.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
, listen: function () {
|
||||||
|
this.$element
|
||||||
|
.on('focus', $.proxy(this.focus, this))
|
||||||
|
.on('blur', $.proxy(this.blur, this))
|
||||||
|
.on('keypress', $.proxy(this.keypress, this))
|
||||||
|
.on('keyup', $.proxy(this.keyup, this));
|
||||||
|
|
||||||
|
if (this.eventSupported('keydown')) {
|
||||||
|
this.$element.on('keydown', $.proxy(this.keydown, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$menu
|
||||||
|
.on('click', $.proxy(this.click, this))
|
||||||
|
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
|
||||||
|
.on('mouseleave', 'li', $.proxy(this.mouseleave, this));
|
||||||
|
|
||||||
|
this.$button
|
||||||
|
.on('click', $.proxy(this.toggle, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
, eventSupported: function(eventName) {
|
||||||
|
var isSupported = eventName in this.$element;
|
||||||
|
if (!isSupported) {
|
||||||
|
this.$element.setAttribute(eventName, 'return;');
|
||||||
|
isSupported = typeof this.$element[eventName] === 'function';
|
||||||
|
}
|
||||||
|
return isSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
, move: function (e) {
|
||||||
|
if (!this.shown) {return;}
|
||||||
|
|
||||||
|
switch(e.keyCode) {
|
||||||
|
case 9: // tab
|
||||||
|
case 13: // enter
|
||||||
|
case 27: // escape
|
||||||
|
e.preventDefault();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 38: // up arrow
|
||||||
|
e.preventDefault();
|
||||||
|
this.prev();
|
||||||
|
this.fixMenuScroll();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 40: // down arrow
|
||||||
|
e.preventDefault();
|
||||||
|
this.next();
|
||||||
|
this.fixMenuScroll();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
, fixMenuScroll: function(){
|
||||||
|
var active = this.$menu.find('.active');
|
||||||
|
if(active.length){
|
||||||
|
var top = active.position().top;
|
||||||
|
var bottom = top + active.height();
|
||||||
|
var scrollTop = this.$menu.scrollTop();
|
||||||
|
var menuHeight = this.$menu.height();
|
||||||
|
if(bottom > menuHeight){
|
||||||
|
this.$menu.scrollTop(scrollTop + bottom - menuHeight);
|
||||||
|
} else if(top < 0){
|
||||||
|
this.$menu.scrollTop(scrollTop + top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
, keydown: function (e) {
|
||||||
|
this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27]);
|
||||||
|
this.move(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
, keypress: function (e) {
|
||||||
|
if (this.suppressKeyPressRepeat) {return;}
|
||||||
|
this.move(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
, keyup: function (e) {
|
||||||
|
switch(e.keyCode) {
|
||||||
|
case 40: // down arrow
|
||||||
|
if (!this.shown){
|
||||||
|
this.toggle();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 39: // right arrow
|
||||||
|
case 38: // up arrow
|
||||||
|
case 37: // left arrow
|
||||||
|
case 36: // home
|
||||||
|
case 35: // end
|
||||||
|
case 16: // shift
|
||||||
|
case 17: // ctrl
|
||||||
|
case 18: // alt
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 9: // tab
|
||||||
|
case 13: // enter
|
||||||
|
if (!this.shown) {return;}
|
||||||
|
this.select();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 27: // escape
|
||||||
|
if (!this.shown) {return;}
|
||||||
|
this.hide();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.clearTarget();
|
||||||
|
this.lookup();
|
||||||
|
}
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
, focus: function (e) {
|
||||||
|
this.focused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
, blur: function (e) {
|
||||||
|
var that = this;
|
||||||
|
this.focused = false;
|
||||||
|
var val = this.$element.val();
|
||||||
|
if (!this.selected && val !== '' ) {
|
||||||
|
this.$element.val('');
|
||||||
|
this.$source.val('').trigger('change');
|
||||||
|
this.$target.val('').trigger('change');
|
||||||
|
}
|
||||||
|
if (!this.mousedover && this.shown) {setTimeout(function () { that.hide(); }, 200);}
|
||||||
|
}
|
||||||
|
|
||||||
|
, click: function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
this.select();
|
||||||
|
this.$element.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
, mouseenter: function (e) {
|
||||||
|
this.mousedover = true;
|
||||||
|
this.$menu.find('.active').removeClass('active');
|
||||||
|
$(e.currentTarget).addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
, mouseleave: function (e) {
|
||||||
|
this.mousedover = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* COMBOBOX PLUGIN DEFINITION
|
||||||
|
* =========================== */
|
||||||
|
$.fn.combobox = function ( option ) {
|
||||||
|
return this.each(function () {
|
||||||
|
var $this = $(this)
|
||||||
|
, data = $this.data('combobox')
|
||||||
|
, options = typeof option == 'object' && option;
|
||||||
|
if(!data) {$this.data('combobox', (data = new Combobox(this, options)));}
|
||||||
|
if (typeof option == 'string') {data[option]();}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.combobox.defaults = {
|
||||||
|
bsVersion: '3'
|
||||||
|
, menu: '<ul class="typeahead typeahead-long dropdown-menu"></ul>'
|
||||||
|
, item: '<li><a href="#"></a></li>'
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.combobox.Constructor = Combobox;
|
||||||
|
|
||||||
|
}( window.jQuery );
|
@@ -100,40 +100,7 @@ class TestMicroversions(base.TestCase):
|
|||||||
|
|
||||||
def test_mv_selector(self):
|
def test_mv_selector(self):
|
||||||
|
|
||||||
button_selectors = \
|
button_selectors = '<option selected="selected" value="">All</option><option value="2.1">2.1</option><option value="2.2">2.2</option><option value="2.3">2.3</option><option value="2.4">2.4</option><option value="2.5">2.5</option><option value="2.6">2.6</option><option value="2.7">2.7</option><option value="2.8">2.8</option><option value="2.9">2.9</option><option value="2.10">2.10</option><option value="2.11">2.11</option><option value="2.12">2.12</option><option value="2.13">2.13</option><option value="2.14">2.14</option><option value="2.15">2.15</option><option value="2.16">2.16</option><option value="2.17">2.17</option><option value="2.18">2.18</option><option value="2.19">2.19</option><option value="2.20">2.20</option><option value="2.21">2.21</option><option value="2.22">2.22</option><option value="2.23">2.23</option><option value="2.24">2.24</option><option value="2.25">2.25</option><option value="2.26">2.26</option><option value="2.27">2.27</option><option value="2.28">2.28</option><option value="2.29">2.29</option><option value="2.30">2.30</option>' # noqa
|
||||||
"""<div aria-label="..." class="btn-group" role="group">
|
|
||||||
<button class="btn btn-default mv_selector active" type="button">All</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.1</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.2</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.3</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.4</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.5</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.6</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.7</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.8</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.9</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.10</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.11</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.12</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.13</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.14</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.15</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.16</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.17</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.18</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.19</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.20</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.21</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.22</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.23</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.24</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.25</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.26</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.27</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.28</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.29</button>
|
|
||||||
<button class="btn btn-default mv_selector" type="button">2.30</button>
|
|
||||||
</div>"""
|
|
||||||
self.assertIn(button_selectors, self.content)
|
self.assertIn(button_selectors, self.content)
|
||||||
|
|
||||||
def test_js_declares(self):
|
def test_js_declares(self):
|
||||||
|
Reference in New Issue
Block a user