module Zena module Use module Ajax module ViewMethods include RubyLess safe_method :ajax? => {:class => Boolean, :method => 'params[:s]'} # Return the DOM id for a node. We had to name this method 'ndom_id' because we want # to avoid the clash with Rails' dom_id method. def ndom_id(node = @node, append_form = true) if node.kind_of?(Node) && !node.new_record? if params[:action] == 'create' && !params[:udom_id] return "#{params[:dom_id]}_#{node.zip}" end elsif append_form && node.kind_of?(Node) && params[:zadd] return "#{params[:dom_id]}_#{node.zip.to_i}" end @dom_id || params[:udom_id] || params[:dom_id] end # RJS to update a page after create/update/destroy def update_page_content(page, obj) unless params[:dom_id] # simply reply with failure or success if !obj.errors.empty? page << "alert(#{obj.errors.first.join(': ')});" page << "return false;" # How to avoid 'onSuccess' ? elsif params[:udom_id] == '_page' # reload page page << "document.location.href = document.location.href;" else # ? end return end if params[:t_id] && obj.errors.empty? obj = secure(Node) { Node.find_by_zip(params[:t_id])} @node = obj end base_class = obj.kind_of?(Node) ? Node : obj.class if obj.new_record? # A. could not create object: show form with errors page.replace ndom_id, :file => template_path_from_template_url('_form') elsif @errors || !obj.errors.empty? # B. could not update/delete: show errors form_file = template_path_from_template_url('_form') if File.exist?(form_file) page.replace ndom_id, :file => form_file else page.insert_html :top, params[:dom_id], :inline => render_errors end elsif params[:udom_id] if params[:udom_id] == '_page' # reload page page << "document.location.href = document.location.href;" else # C. update another part of the page if node_id = params[:u_id] if node_id.to_i != obj.zip if base_class == Node instance_variable_set("@#{base_class.to_s.underscore}", secure(base_class) { base_class.find_by_zip(node_id) }) else instance_variable_set("@#{base_class.to_s.underscore}", secure(base_class) { base_class.find_by_id(node_id) }) end end end page.replace params[:udom_id], :file => template_path_from_template_url('', params[:u_url]) if params[:upd_both] @dom_id = params[:dom_id] page.replace params[:dom_id], :file => template_path_from_template_url end if params[:done] && params[:zadd] page.toggle "#{params[:dom_id]}_0", "#{params[:dom_id]}_add" page << params[:done] elsif params[:done] page << params[:done] end end else # D. normal update #if params[:dom_id] == '_page' # # reload page # page << "document.location.href = document.location.href;" # case params[:action] when 'edit' page.replace params[:dom_id], :file => template_path_from_template_url page << "$('#{params[:dom_id]}_form_t').focusFirstElement();" when 'create' pos = params[:position] || :before ref = params[:reference] || "#{params[:dom_id]}_add" page.insert_html pos.to_sym, ref, :file => template_path_from_template_url if obj.kind_of?(Node) @node = obj.parent.new_child(:class => obj.class) else instance_variable_set("@#{base_class.to_s.underscore}", obj.clone) end page.replace ndom_id, :file => template_path_from_template_url('_form') if params[:done] page << params[:done] elsif params[:zadd] page.toggle "#{params[:dom_id]}_0", "#{params[:dom_id]}_add" end when 'update' page.replace ndom_id, :file => template_path_from_template_url page << params[:done] if params[:done] when 'destroy' page << %Q{ new Effect.Highlight('#{ndom_id}', { duration: 0.3, afterFinish: function() { new Effect.Fade('#{ndom_id}', { duration: 0.5, afterFinish: function() { $('#{ndom_id}').remove(); } }); } }); } when 'drop' case params[:done] when 'remove' page.visual_effect :highlight, params[:drop], :duration => 0.3 page.visual_effect :fade, params[:drop], :duration => 0.3 end page.replace params[:dom_id], :file => template_path_from_template_url else if position = params[:insert] page.call 'Zena.insert_inner', params[:dom_id], position, render(:file => template_path_from_template_url) else page.replace params[:dom_id], :file => template_path_from_template_url end end end if params[:redir] page << "window.location.href = '#{params[:redir]}';" end if params[:reload] page << "Zena.reload(#{params[:reload].inspect});" end page << render_js(false) end # Used by zafu to set dom_id that need to be made draggable. def add_drag_id(dom_id, js_options = nil) @drag_ids ||= {} (@drag_ids[js_options] ||= []) << dom_id end # Used by zafu to transform a dom_id into a droppable element. def add_drop_id(dom_id, options) js_data << "Droppables.add('#{dom_id}', {hoverclass:'#{options[:hover] || 'drop_hover'}', onDrop:function(element){ new Ajax.Request('#{options[:url]}', {asynchronous:true, evalScripts:true, method:'put', parameters:'drop=' + encodeURIComponent(element.id)}); }});" end def add_toggle_id(dom_id, group_name, role, opts = {}) arity = opts[:arity] || 'many' if js = opts[:js] js = ", js:function(e) { #{js} }" end @toggle_ids ||= {} unless list = @toggle_ids[group_name] list = @toggle_ids[group_name] = [] if other = yield found = other.rel[role].other_zips else found = [] end url = "/nodes/#{other.zip}" js_data << "#{group_name} = {list:#{found.inspect}, url:#{url.inspect}, role:#{role.inspect}, arity:#{arity.inspect}#{js}};" end list << dom_id end def filter_form(node, dom_id, loading, upd) if loading loading = "\n#{loading}($('#{upd}'));" else loading = '' end js_data << %Q{new Form.Observer('#{dom_id}', 0.3, function(element, value) {#{loading} new Ajax.Request('#{zafu_node_path(node)}', {asynchronous:true, evalScripts:true, method:'get', parameters:Form.serialize('#{dom_id}')}) });} end # Include draggable ids in bottom of page Javascript. def render_js(in_html = true) if @drag_ids @drag_ids.each do |js_options, list| if js_options.nil? js_data << %Q{#{list.inspect}.each(Zena.draggable);} else js_data << %Q{#{list.inspect}.each(function(item) { Zena.draggable(item, #{js_options})});} end end end if @toggle_ids @toggle_ids.each do |group_name, list| js_data << %Q{#{list.inspect}.each(function(item) { Zena.set_toggle(item, #{group_name})});} end end # Super is in Zena::Use::Rendering super end end # ViewMethods module ZafuMethods def self.included(base) # TODO: move process_toggle in 'before_wrap' callback so that 'node' is properly set. base.before_process :process_drag, :process_toggle base.before_wrap :wrap_with_drag end def process_drag @drag_param = @params.delete(:draggable) end # Force an id on the current tag and record the DOM_ID to make the element draggable. def wrap_with_drag(text) # do not render drag in make_form return text unless @drag_param && !@context[:make_form] drag = @drag_param if @markup.params[:id] || (@markup.done && @method == 'link') # hack to rewrap link... # we do not mess with the original but use our own markup markup = @wrap_markup = Zafu::Markup.new('span') else markup = @markup end markup.tag ||= 'div' if node.instance_variable_get(:@dom_prefix).blank? # make sure we have a scope node.dom_prefix = dom_name end if erb_dom_id = markup.dyn_params[:id] # id set, get erb id else # We do not want to use the same id as the 'each' loop but we also want to # avoid changing the node context @drag_prefix ||= root.get_unique_name('drag', true).gsub(/[^\d\w\/]/,'_') erb_dom_id = node.dom_id(:dom_prefix => @drag_prefix) markup.set_id(erb_dom_id) end dom_id = erb_dom_id[/<%=\s*(.*?)\s*%>/,1] markup.append_param(:class, 'drag') drag = 'drag_handle' if drag == 'true' if drag == 'all' js_options = ['false'] else unless @blocks.detect{|b| b.kind_of?(String) ? b =~ /class=.#{drag}/ : (b.params[:class] == drag || (b.markup && b.markup.params[:class] == drag))} handle = " " end js_options = [drag.inspect] end if revert = @params.delete(:revert) js_options << (%w{true false}.include?(revert) ? revert : revert.inspect) end markup.pre_wrap[:drag] = "#{handle}<% add_drag_id(#{dom_id}, #{js_options.join(', ').inspect}) %>" if @markup == markup text else markup.wrap(text) end end # Display an input field to filter a remote block def r_filter if upd = @params[:update] return unless block = find_target(upd) else return parser_error("missing 'block' in same parent") unless parent && block = parent.descendant('block') if block.name.blank? block.name ||= unique_name end upd = block.name end return parser_error("cannot use 's' as key (used by start_node)") if @params[:key] == 's' # Move up in case we are in a list. if self.node.list_context? base_node = self.node.up(Node) else base_node = self.node end # Do not alter current node, create our own with_context(:node => base_node.dup) do node.dom_prefix = dom_name dom_id = node.dom_id(:erb => false) # TODO: add 'encode_params' and x='"foobar"' to add any value in the request out %Q{<%= form_remote_tag(:url => zafu_node_path(#{node}), :method => :get, :html => {:id => \"#{dom_id}_f\"}) %>