require 'sortable_element_for_nested_set' include SortableElementForNestedSet module SortableNestedSet def self.included(base) base.extend ClassMethods end module ClassMethods def handles_sortable_nested_set handles_sorting_of_nested_set class_eval do define_method(:sns_category_controller) { controller_name } define_method(:sns_category_id_field) { "#{controller_name.singularize}_id" } define_method(:sns_category_class) { controller_name.classify.constantize } define_method(:sns_items_type) {sns_category_class.sns_items_type } define_method(:sns_item_class) {sns_items_type.to_s.classify.constantize } end include(SortableNestedSet::InstanceMethods) end end module InstanceMethods def sns_add_subcategory redirect_to :controller => sns_category_controller, :action => :new, sns_category_id_field => params[:id] end def sns_add_item redirect_to :controller => sns_items_type, :action => :new, sns_category_id_field => params[:id] end def sns_sort_categories category = sns_category_class.find(params[:moved]) new_pos = position_of(:moved).in_tree(:menu) # Ambiguous moves occur when a category is dropped outside the menu AND # a :root parameter is specified. In this case, move the element to the # :root category in the database and also put it at the end with javascript. ambiguous_move = nil begin category.transaction do if new_pos[:parent] category.move_to_child_of(new_pos[:parent]) elsif params[:root].nil? category.move_to_root else root_id = params[:root].to_i ambiguous_move = params[:menu]['0']['id'] == params[:moved] ? :top : :bottom if ambiguous_move == :bottom category.move_to_child_of(params[:root]) else category.move_to_left_of(sns_category_class.find(params[:root]).sns_subcategories.first) end end unless category.parent_id === new_pos[:parent] begin if new_pos[:move_to_right_of] category.move_to_right_of(new_pos[:move_to_right_of]) else category.move_to_left_of(new_pos[:move_to_left_of]) end rescue ActiveRecord::RecordNotFound # FIXME error raised when making first child end unless ambiguous_move raise category.errors.full_messages.to_sentence unless category.valid? end rescue => err render :update do |page| page.alert err.message page.reload # TODO return to original location with javascript end else render :update do |page| page.assign :categoryElement, page.literal("$('category_#{category.id}')") if ambiguous_move page.assign :rootElement, page.literal("$('subcategories_#{root_id}')") page.remove "category_#{category.id}" # there is a placeholder at index 0 page << (ambiguous_move == :top ? 'rootElement.insertBefore(categoryElement, rootElement.childElements()[1])' : 'rootElement.insert(categoryElement)') page.visual_effect 'highlight', "category_#{category.id}" else page.assign :elderSibling, page.literal('categoryElement.nextElementSibling') page.assign :parent, page.literal('categoryElement.parentNode') page << < category_id, :position => index + 1) end if container render :update do |page| id = "item_#{params[:moved]}" page << "$('#{id}').style.removeProperty('z-index')" end end def sns_toggle render :update do |page| page["items_#{params[:id]}"].toggle page["subcategories_#{params[:id]}"].toggle page.replace_html "toggle_#{params[:id]}", :partial => 'sortable_nested_set/toggle', :locals => { :category_type => params[:category_type], :id => params[:id], :state => (params[:state] == 'open' ? 'closed' : 'open') } end end def sns_destroy_category @category = sns_category_class.find(params[:id]) @category.destroy render :update do |page| page["category_#{@category.id}"].remove end end def sns_destroy_item @item = sns_item_class.find(params[:id]) @item.destroy render :update do |page| page["item_#{@item.id}"].remove end end end end ActionController::Base.send :include, SortableNestedSet