module SimpleNavigation class << self def explicit_navigation_args self.adapter.controller.instance_variable_get(:"@sn_current_navigation_args") end # Reads the current navigation for the specified level from the controller. # Returns nil if there is no current navigation set for level. def current_navigation_for(level) self.adapter.controller.instance_variable_get(:"@sn_current_navigation_#{level}") end # If any navigation has been explicitely set in the controller this method evaluates the specified args set in the controller and sets # the correct instance variable in the controller. def handle_explicit_navigation if SimpleNavigation.explicit_navigation_args level, navigation = parse_explicit_navigation_args self.adapter.controller.instance_variable_set(:"@sn_current_navigation_#{level}", navigation) end end # TODO: refactor this ugly thing to make it nice and short def parse_explicit_navigation_args args = SimpleNavigation.explicit_navigation_args args = [Hash.new] if args.empty? if args.first.kind_of? Hash options = args.first else # args is a list of current navigation for several levels options = {} if args.size == 1 #only one navi-key has been specified, try to find out level level = SimpleNavigation.primary_navigation.level_for_item(args.first) options[:"level_#{level}"] = args.first if level else args.each_with_index {|arg, i| options[:"level_#{i + 1}"] = arg} end end #only the deepest level is relevant level = options.inject(0) do |max, kv| kv.first.to_s =~ /level_(\d)/ max = $1.to_i if $1.to_i > max max end raise ArgumentError, "Invalid level specified or item key not found" if level == 0 [level, options[:"level_#{level}"]] end end # Adds methods for explicitely setting the current 'active' navigation to the controllers. # Since version 2.0.0 the simple_navigation plugin determines the active navigation based on the current url by default (auto highlighting), # so explicitely defining the active navigation in the controllers is only needed for edge cases where automatic highlighting does not work. # # On the controller class level, use the navigation method to set the active navigation for all actions in the controller. # Let's assume that we have a primary navigation item :account which in turn has a sub navigation item :settings. # # ==== Examples # class AccountController << ActionController # navigation :account # ... # end # # class AccountSettingsController << ActionController # navigation :settings # ... # end # # The first example sets the current primary navigation to :account for all actions. No active sub_navigation. # The second example sets the current sub navigation to :settings and since it is a child of :account the current primary navigation is set to :account. # # On the controller instance level, use the current_navigation method to define the active navigation for a specific action. # The navigation item that is set in current_navigation overrides the one defined on the controller class level (see navigation method). # Thus if you have an :account primary item with a :special sub navigation item: # # ==== Example # class AccountController << ActionController # navigation :account # # def your_special_action # ... # current_navigation :special # end # end # # The code above still sets the active primary navigation to :account for all actions, but sets the sub_navigation to :account -> :special for 'your_special_action'. # # Note 1: As you can see above you just have to set the navigation item of your 'deepest' navigation level as active and all its parents are marked as active, too. # # Note 2: The specified symbols must match the keys for your navigation items in your config/navigation.rb file. module ControllerMethods def self.included(base) #:nodoc: base.class_eval do extend ClassMethods include InstanceMethods end end module ClassMethods # Sets the active navigation for all actions in this controller. # # The specified symbol must match the keys for your navigation items in your config/navigation.rb file. def navigation(*args) self.class_eval do define_method :sn_set_navigation do current_navigation(*args) end protected :sn_set_navigation before_filter :sn_set_navigation end end end module InstanceMethods # Sets the active navigation. Call this method in any action to override the controller-wide active navigation # specified by navigation. # # The specified symbol must match the keys for your navigation items in your config/navigation.rb file. def current_navigation(*args) @sn_current_navigation_args = args end end end class Item def selected_by_config? key == SimpleNavigation.current_navigation_for(@container.level) end end class ItemContainer def selected_item self[SimpleNavigation.current_navigation_for(self.level)] || items.find {|i| i.selected?} end end end ActionController::Base.send(:include, SimpleNavigation::ControllerMethods)