diff --git a/horizon/static/horizon/js/horizon.forms.js b/horizon/static/horizon/js/horizon.forms.js index cec82559ad..991badff1e 100644 --- a/horizon/static/horizon/js/horizon.forms.js +++ b/horizon/static/horizon/js/horizon.forms.js @@ -239,11 +239,112 @@ horizon.forms.init_examples = function (el) { $el.find("#create_image_form input#id_copy_from").attr("placeholder", "http://example.com/image.iso"); }; +horizon.forms.init_themable_select = function ($elem) { + "use strict"; + + // If not specified, find them all + $elem = $elem || $('body'); + + // If a jQuery object isn't passed in ... make it one + $elem = $elem instanceof jQuery ? $elem : $($elem); + + // Pass in a container OR the themable select itself + $elem = $elem.hasClass('themable-select') ? $elem : $elem.find('.themable-select'); + + // Update the select value if dropdown value changes + $elem.on('click', 'li a', function () { + var $this = $(this); + var $container = $this.closest('.themable-select'); + var value = $this.data('selectValue'); + + // Find select ... if we've searched for it before, then its cached on 'data-select' + var $select = $container.data('mySelect'); + if (!$select) { + $select = $container.find('select'); + $container.data('mySelect', $select); + } + + // Set the select if necessary + if($select.val() !== value) { + $select.val(value).change(); + } + }); + + $elem.find('li a[title]').tooltip(); + + // We need to rebuild the dropdown if the Select html ever + // changes via javascript. Mutation Observers are DOM change + // listeners. http://stackoverflow.com/a/11546242 + MutationObserver = window.MutationObserver || window.WebKitMutationObserver; // eslint-disable-line no-native-reassign + + var $targets = $elem.find('select'); + for (var ii = 0; ii < $targets.length; ii++) { + var observer = new MutationObserver(function (mutations) { // eslint-disable-line no-loop-func + + // Will return many mutations for a select box changing, + // we just need the target of one. + var $select = $(mutations[0].target).closest('select'); + var $options = $select.find('option'); + var list = []; + + for (var jj = 0; jj < $options.length; jj++) { + + // Build new list item and anchor tag. + var $list_item = $(document.createElement('li')) + .attr('data-original-index', jj) + .attr('select-value', $options[jj].attr('value')); + + var $anchor = $(document.createElement('a')); + + // Append option text to anchor, then to list item. + $anchor.text($($options[jj]).text()).appendTo($list_item); + list[jj] = $list_item; + } + + // Add the new list to the dropdown. + $select.siblings('.dropdown-menu').html(list).change(); + }); + + var config = { + childList: true, + subtree: true, + attributes: false, + characterData: true + }; + + observer.observe($targets[ii], config); + } + + // Update the dropdown if select value changes + $elem.children('select').on('change', function () { + var $this = $(this); + var thisVal = $this.val(); + var thisLabel = $this.find('option[value="' + thisVal + '"]').text(); + + // Go find the title element + var $title = $this.parents('.themable-select').find('.dropdown-title'); + + // Set dropdown title to first option if the select menu is unset + if (thisLabel === null || thisLabel.length === 0) { + thisLabel = $this.find('option').first().text(); + } + + // Update the dropdown-title if necessary. + if (thisLabel !== $title.text()) { + $title.text(thisLabel); + } + }); +}; + horizon.addInitFunction(horizon.forms.init = function () { - horizon.forms.handle_submit($('body')); + var $body = $('body'); + horizon.forms.handle_submit($body); horizon.modals.addModalInitFunction(horizon.forms.handle_submit); - horizon.forms.init_examples($("body")); + horizon.forms.init_themable_select(); + horizon.modals.addModalInitFunction(horizon.forms.init_themable_select); + + horizon.forms.init_examples($body); horizon.modals.addModalInitFunction(horizon.forms.init_examples); horizon.forms.handle_snapshot_source(); @@ -255,14 +356,14 @@ horizon.addInitFunction(horizon.forms.init = function () { horizon.forms.handle_subnet_subnetpool(); if (!horizon.conf.disable_password_reveal) { - horizon.forms.add_password_fields_reveal_buttons($("body")); + horizon.forms.add_password_fields_reveal_buttons($body); horizon.modals.addModalInitFunction( horizon.forms.add_password_fields_reveal_buttons); } // Bind event handlers to confirm dangerous actions. // Stops angular form buttons from triggering this event - $("body").on("click", "form button:not([ng-click]).btn-danger", function (evt) { + $body.on("click", "form button:not([ng-click]).btn-danger", function (evt) { horizon.datatables.confirm(this); evt.preventDefault(); }); @@ -278,10 +379,13 @@ horizon.addInitFunction(horizon.forms.init = function () { $switchables = $fieldset.find('select.switchable'); $switchables.each(function (index, switchable) { - var $switchable = $(switchable), - slug = $switchable.data('slug'), - visible = $switchable.is(':visible'), - val = $switchable.val(); + var $switchable = $(switchable); + var slug = $switchable.data('slug'); + var isThemable = $switchable.parent('.themable-select').length > 0; + var visible = isThemable + ? $switchable.siblings('.dropdown-toggle').is(':visible') + : $switchable.is(':visible'); + var val = $switchable.val(); function handle_switched_field(index, input){ var $input = $(input), diff --git a/horizon/tables/actions.py b/horizon/tables/actions.py index 7d89cd5326..1f1e356d83 100644 --- a/horizon/tables/actions.py +++ b/horizon/tables/actions.py @@ -523,6 +523,12 @@ class FilterAction(BaseAction): return True return False + def get_select_options(self): + """Provide the value and string for the template to render. + """ + if self.filter_choices: + return [x[:2] for x in self.filter_choices] + class NameFilterAction(FilterAction): """A filter action for name property.""" diff --git a/horizon/templates/horizon/common/_data_table_table_actions.html b/horizon/templates/horizon/common/_data_table_table_actions.html index 40cb31cdc5..a7b6c6fd7d 100644 --- a/horizon/templates/horizon/common/_data_table_table_actions.html +++ b/horizon/templates/horizon/common/_data_table_table_actions.html @@ -1,4 +1,4 @@ -{% load i18n %} +{% load i18n bootstrap %}