Richard Jordan
2008-Jun-15 22:29 UTC
[Facebooker-talk] [PATCH] helper to create fb css stylized table
I attached a rails helper implementation of the fb_table described here: http://wiki.developers.facebook.com/index.php/Facebook_Styles I included testing and comments. I hope you find it useful. Curiously, it''s really a small extension of FBML. Richard -------------- next part -------------- Index: test/rails_integration_test.rb ==================================================================--- test/rails_integration_test.rb (revision 221) +++ test/rails_integration_test.rb (working copy) @@ -431,6 +431,37 @@ ActionController::Base.asset_host=''http://facebook.host.com'' end + def test_fb_table + assert_equal "<div><table border=\"0\" cellspacing=\"0\" class=\"lists\"></table></div>", @h.fb_table{} + end + def test_fb_tr + assert_equal "<tr></tr>", @h.fb_tr{} + end + def test_fb_td + assert_equal "<td class=\"list\" style=\"width:306px\"></td>", @h.fb_td{} + end + def test_fb_th + assert_equal "<th><h4>List 1</h4><a href=\"#\">See All</a></th>", @h.fb_th("List 1","#","See All") + end + def test_fb_tf + assert_equal "<td class=\"see_all\"><div><a href=\"#\">See all List 1s</a></div></td>", @h.fb_tf("#","See all List 1s") + end + def test_fb_th_spacer + assert_equal "<th class=\"spacer\" />", @h.fb_th_spacer + end + def test_fb_td_spacer + assert_equal "<td class=\"spacer\" />", @h.fb_td_spacer + end + def test_fb_tf_spacer + assert_equal "<td class=\"spacer\" />", @h.fb_tf_spacer + end + def test_fb_first_list_item + assert_equal "<div class=\"list_item.first clearfix\"></div>", @h.fb_first_list_item{} + end + def test_fb_list_item + assert_equal "<div class=\"list_item clearfix\"></div>", @h.fb_list_item{} + end + def test_fb_profile_pic assert_equal "<fb:profile-pic uid=\"1234\" />", @h.fb_profile_pic("1234") end @@ -806,4 +837,4 @@ end # rescue LoadError # $stderr.puts "Couldn''t find action controller. That''s OK. We''ll skip it." -end \ No newline at end of file +end Index: lib/facebooker/rails/helpers.rb ==================================================================--- lib/facebooker/rails/helpers.rb (revision 221) +++ lib/facebooker/rails/helpers.rb (working copy) @@ -8,6 +8,197 @@ # module Helpers + # Create a multi-list table + # <em> See: </em> http://wiki.developers.facebook.com/index.php/Facebook_Styles for details + # + # You must call fb_css() to include the css styles. You can include the css by + # putting <%= fb_css() %> in your layout. + # + # Inside use helper methods: fb_tr, fb_td, fb_first_list_item, + # fb_list_item, fb_th, fb_tf, + # fb_th_spacer, fb_td_spacer, fb_tf_spacer. + # + # For example (modeled after the basic code on the facebook wiki), + # + # <% fb_table do %> + # <% fb_tr do %> + # <%= fb_th("List 1", "#", "See All") %> + # <%= fb_th_spacer%> + # <%= fb_th("List 2", "#", "See All") %> + # <% end %> + # <% fb_tr do %> + # <% fb_td do %> + # <% fb_first_list_item do %> + # INSERT FIRST ITEM HERE + # <% end %> + # <% fb_list_item do %> + # INSERT ANOTHER ITEM HERE + # <% end %> + # <% end %> + # <%= fb_td_spacer %> + # <% fb_td do %> + # <% fb_first_list_item do %> + # INSERT FIRST ITEM HERE + # <% end %> + # <% fb_list_item do %> + # INSERT ANOTHER ITEM HERE + # <% end %> + # <% end %> + # <% end %> + # <% fb_tr do %> + # <%= fb_tf("#", "See all List 1''s") %> + # <%= fb_tf_spacer%> + # <%= fb_tf("#", "See all List 2''s") %> + # <% end %> + # <% end %> + def fb_table(options={}, &block) + options.merge!(:class=>"lists",:cellspacing=>"0", :border=>"0") + content = capture(&block) + content_tag(:div, + concat(content_tag(:table, content,stringify_vals(options) ), + block.binding + ) + ) + end + + # Include styles that are used by Facebook. + # Best to include in your layout with your other css. + # Required for fb_table. See the example there. + # <em> See: </em> http://wiki.developers.facebook.com/index.php/Facebook_Styles for more details + def fb_css + ''<style> + .lists th { + text-align: left; + padding: 5px 10px; + background: #6d84b4; + } + + .lists .spacer { + background: none; + border: none; + padding: 0px; + margin: 0px; + width: 10px; + } + + .lists th h4 { float: left; color: white; } + .lists th a { float: right; font-weight: normal; color: #d9dfea; } + .lists th a:hover { color: white; } + + .lists td { + margin:0px 10px; + padding:0px; + vertical-align:top; + } + + .lists .list { + background:white none repeat scroll 0%; + border-color:-moz-use-text-color #BBBBBB; + border-style:none solid; + border-width:medium 1px; + } + .lists .list .list_item { border-top:1px solid #E5E5E5; padding: 10px; } + .lists .list .list_item.first { border-top: none; } + + .lists .see_all { + background:white none repeat scroll 0%; + border-color:-moz-use-text-color #BBBBBB rgb(187, 187, 187); + border-style:none solid solid; + border-width:medium 1px 1px; + text-align:left; + } + + .lists .see_all div { border-top:1px solid #E5E5E5; padding:5px 10px; } + + </style>'' + end + + # Render a facebook styled tr tag. + # Use this in conjunction with fb_table. + # Provide any options you like. + def fb_tr(&block) + content = capture(&block) + concat(content_tag(:tr,content),block.binding) + end + + # Render a facebook styled td tag. + # Use this in conjunction with fb_table. + # Provide any options you like. The default options + # include {:style=>"width:306px"}, the perfect width for + # a two list table. If you use options or want a + # different width, reset the style accordingly. + def fb_td(options={:style=>"width:306px"},&block) + options.merge!(:class=>"list") + content = capture(&block) + concat(content_tag(:td,content,stringify_vals(options)), block.binding) + end + + # Render a facebook styled first list element. + # Use this in conjunction with fb_table. See the example there. + # This first element has no padding above it. + # Provide any options you like. + def fb_first_list_item(options={},&block) + content = capture(&block) + options.merge!(:class=>''list_item.first clearfix'') + concat(content_tag(:div,content,stringify_vals(options)),block.binding) + end + + # Render a facebook styled list element. + # Use this in conjunction with fb_table. See the example there. + # Unlike the first element, this element has padding above it. + # Provide any options you like. + def fb_list_item(options={}, &block) + content = capture(&block) + options.merge!(:class=>''list_item clearfix'') + concat(content_tag(:div,content,stringify_vals(options)),block.binding) + end + + # Renders a facebook styled table header. + # Use this in conjunction with fb_table. See the example there. + # Provide a title for the list, a url, a label for that url, and any options. + def fb_th(title,url,label, options={} ) + options.merge!(:href=>url) + content_tag(:th,content_tag(:h4,title)+ + content_tag(:a,label,stringify_vals(options)) + ) + end + + # Renders a facebook styled table footer. + # Use this in conjunction with fb_table. See the example there. + # Provide a url,a label for that url, and any options + # for the td tag and/or the a tag. + def fb_tf(url, label,td_options={},a_options={}) + td_options.merge!(:class=>"see_all") + a_options.merge!(:href=>url) + content_tag(:td, + content_tag(:div, + content_tag(:a,label,stringify_vals(a_options)) + ), stringify_vals(td_options) + ) + end + + # Renders a facebook styled spacer for between calls to fb_th. + # Use this in conjunction with fb_table. See the example there. + # Provide any options. + def fb_th_spacer(options={}) + options.merge!(:class=>"spacer") + tag(:th, stringify_vals(options)) + end + + # Renders a facebook styled spacer for between calls to fb_td. + # Use this in conjunction with fb_table. See the example there. + # Provide any options. + def fb_td_spacer(options={}) + options.merge!(:class=>"spacer") + tag(:td, stringify_vals(options)) + end + + # Renders a facebook styled spacer for between calls to fb_tf. + # Use this in conjunction with fb_table. See the example there. + # Provide any options. + def fb_tf_spacer(options={}) + fb_td_spacer(options) + end # Create an fb:dialog # id must be a unique name e.g. "my_dialog" @@ -581,4 +772,4 @@ self[newkey] = self.delete(oldkey) if self.has_key?(oldkey) self end -end \ No newline at end of file +end
I''ve attached a patch for a facebook_link_to which supports the :confirm and :method options. Usually, link_to spits out non-facebook-compliant javascript. I used fbjs instead. The first attachment, a new ruby file that you should call facebook_url_helper.rb, should be placed in lib/facebooker/rails/. The other diff file is probably self explanatory. Everyone does such a good job on facebooker I am insecure about my code. Please, tell me if you see any improvements, especially if you are handy with fbjs. I think the :back and :popup options are impossible with fbjs. Please correct me if I am wrong. Richard -------------- next part -------------- module Facebooker module Rails module Helpers module UrlHelperExtensions # Creates a link tag. If you do not pass any options, then it acts # exactly the same as link_to. See the documention there. # # Otherwise, it has some limitations because of # differences between JavaScript(JS) and Facebook Javascript(FBJS). # These include # * You cannot use <tt>:back</tt> to link to the referrer. # * You cannot use <tt>:popup</tt> to open a new window. # * You cannot use <tt>:href</tt> to specify the link location. # As FBJS and Facebooker evolves, these limitations may disapear or # be overcomed. # # ==== Options # * <tt>:confirm => ''question?''</tt> - This will add a Facebook Javascript # dialog with the question specified. If the user accepts, the link is # processed normally, otherwise no action is taken. # * <tt>:method => symbol of HTTP verb</tt> - This modifier will statically # create an HTML form and immediately submit the form for processing using # the HTTP verb specified. Unlike link_to, it mostly avoids Facebook # Javascript and instead wraps the link in an invisible form. Beware it # sets the form id to <tt>"_form"+ID_NUMBER+"_"</tt>. Do not use such an id # elsewhere. # * The +html_options+ will accept a hash of html attributes for the link tag. # # ==== Examples # It conforms to Facebook Javascript unlike link_to. Specifically, link_to fails with # Facebook Javascript when using the <tt>:method</tt> and <tt>:confirm</tt> # options. Use as follows: # # facebook_link_to "Visit Awesome Site", "http://facebooker.rubyforge.org/", :confirm => "Are you sure?" # # => <a href=''#'' onclick="new Dialog().showChoice('''',''Are you sure?'').onconfirm = function() { # document.setLocation(''http://facebooker.rubyforge.org/''); return false; };">Visit Awesome Site</a> # # facebook_link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete # # => <form display="none" id="_form1_" method="post" action="/images/9"> # <div> # <input name="_method" type="hidden" value="delete" /> # <a href=''#'' onclick="new Dialog().showChoice('''',''Are you sure?'').onconfirm = function() { # document.getElementById(''_form1_'').submit(); return false; }">Delete Image</a> # </div> # </form> # # facebook_link_to "Delete without confirmation", @image, :method => :delete # # => <form display="none" id="_form2_" method="post" action="/images/9"> # <div> # <input name="_method" type="hidden" value="delete" /> # <a href=''#'' onclick="document.getElementById(''_form2_'').submit(); return false; }"> # Delete without confirmation</a> # </div> # </form> def facebook_link_to(name, options = {}, html_options = nil) # maybe javascript is not needed, so just do the regular link_to return link_to(name,options,html_options) unless(html_options) html_options = html_options.stringify_keys return link_to(name,options,html_options) unless(javascript_needed?(html_options) ) url = options.is_a?(String) ? options : self.url_for(options) # save the method method = html_options[''method''] convert_options_to_facebook_javascript!(html_options, url) tag_options = tag_options(html_options) link_tag = "<a href=''#''#{tag_options}>#{name || url}</a>" return link_tag unless method # fbjs is tricky so we will make a form to look like the link method_tag = '''' if (%w{put delete}.include?(method.to_s) ) method_tag = tag(''input'', :type => ''hidden'', :name => ''_method'', :value => method.to_s) end form_method = method.to_s == ''get'' ? ''get'' : ''post'' request_token_tag = '''' if form_method == ''post'' && protect_against_forgery? request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end form_id_attr = "id=\"_form#{@@form_id.to_s}_\"" # increment the form_id so we don''t reuse it # all our forms then will have a unique id @@form_id = @@form_id + 1 "<form display=\"none\" #{form_id_attr} method=\"#{form_method}\" action=\"#{escape_once url}\"><div>" + method_tag + link_tag + request_token_tag + "</div></form>" end private # Makes sure the right form submits @@form_id=0 # Returns true when we must create an onclick tag. def javascript_needed?(html_options) (html_options["confirm"] || html_options["popup"] || html_options["method"]) end # Does not allow href or popup attributes. def convert_options_to_facebook_javascript!(html_options, url = '''') confirm, popup = html_options.delete("confirm"), html_options.delete("popup") method, href = html_options.delete("method"), html_options[''href''] html_options["onclick"] = case when href || popup raise ActionView::ActionViewError, "You can''t use :href or :popup -- at least yet" when confirm && method "#{confirm_facebook_javascript_function(confirm, url, href, method)}" when confirm "#{confirm_facebook_javascript_function(confirm, url, href)};" when method "#{method_submit}" else html_options["onclick"] end end def method_submit form_id = "_form#{@@form_id.to_s}_" "document.getElementById(''#{escape_javascript(form_id)}'').submit(); return false;" end def change_location(url) "document.setLocation(''#{escape_javascript(url)}''); return false;" end # Creates a facebook dialog. def confirm_facebook_javascript_function(confirm, url, href, method_defined=false) "new Dialog().showChoice('''',''#{escape_javascript(confirm)}'').onconfirm = function() { #{method_defined ? method_submit : change_location(url)} }" end #### Gosh, I think this function can be done someway. # def method_facebook_javascript_function(method, url = '''', href = nil) # action = (href && url.size > 0) ? "''#{url}''" : ''this.getHref'' # submit_function # "var f = document.createElement(''form''); f.setStyle(''display'',''none''); " + # "this.getParentNode.appendChild(f); f.setMethod(''POST''); f.setAction(#{action});" # # unless method == :post # submit_function << "var m = document.createElement(''input''); m.setAttribute(''type'', ''hidden''); " # submit_function << "m.setAttribute(''name'', ''_method''); m.setAttribute(''value'', ''#{method}''); f.appendChild(m);" # end # # if protect_against_forgery? # submit_function << "var s = document.createElement(''input''); s.setAttribute(''type'', ''hidden''); " # submit_function << "s.setAttribute(''name'', ''#{request_forgery_protection_token}''); # s.setAttribute(''value'', ''#{escape_javascript form_authenticity_token}''); f.appendChild(s);" # end # submit_function << " f.submit(); " # end ####### end end end end -------------- next part -------------- Index: test/rails_integration_test.rb ==================================================================--- test/rails_integration_test.rb (revision 224) +++ test/rails_integration_test.rb (working copy) @@ -6,6 +6,7 @@ require ''facebooker/rails/controller'' require ''facebooker/rails/helpers'' require ''facebooker/rails/facebook_form_builder'' + require ''facebooker/rails/facebook_url_helper'' require File.dirname(__FILE__)+''/../init'' require ''mocha'' @@ -806,4 +807,65 @@ end # rescue LoadError # $stderr.puts "Couldn''t find action controller. That''s OK. We''ll skip it." -end \ No newline at end of file +end + +class RailsUrlHelperExtensionsTest < Test::Unit::TestCase + class UrlHelperExtensionsClass + include ActionView::Helpers::UrlHelper + include Facebooker::Rails::Helpers::UrlHelperExtensions + include ActionView::Helpers::TagHelper + def initialize + @template = self + end + + def protect_against_forgery? + false + end + end + + # used for capturing the contents of some of the helper tests + # this duplicates the rails template system + attr_accessor :_erbout + + def setup + @_erbout = "" + @u = UrlHelperExtensionsClass.new + @label = "Home Sweet Home" + @url = "www.home.com" + end + + def test_facebook_link_to + assert_equal @u.link_to(@label, at url), @u.facebook_link_to(@label, @url) + end + + def test_facebook_link_to_with_popup + assert_raises(ActionView::ActionViewError) {@u.facebook_link_to(@label, at url, :popup=>true)} + end + + def test_facebook_link_to_with_confirm + assert_equal "<a href=\''#\'' onclick=\"new Dialog().showChoice(\''\'',\''Are you sure?\'').onconfirm = function() { " + + "document.setLocation(\''http://facebooker.rubyforge.org/\''); return false; };\">Visit Awesome Site</a>", + @u.facebook_link_to("Visit Awesome Site", "http://facebooker.rubyforge.org/", :confirm => "Are you sure?") + end + + def test_facebook_link_to_with_method + assert_equal "<form display=\"none\" id=\"_form1_\" method=\"post\" action=\"/images/9\">"+ + "<div>" + + "<input name=\"_method\" type=\"hidden\" value=\"delete\" />"+ + "<a href=\''#\'' onclick=\"document.getElementById(\''_form1_\'').submit(); return false;\">"+ + "Delete without confirmation</a>"+ + "</div>" + + "</form>", @u.facebook_link_to("Delete without confirmation", "/images/9", :method => :delete) + end + + def test_facebook_link_to_with_confirm_and_method + assert_equal "<form display=\"none\" id=\"_form0_\" method=\"post\" action=\"/images/9\">"+ + "<div>" + + "<input name=\"_method\" type=\"hidden\" value=\"delete\" />"+ + "<a href=\''#\'' onclick=\"new Dialog().showChoice(\''\'',\''Are you sure?\'').onconfirm = function() { "+ + "document.getElementById(\''_form0_\'').submit(); return false; }\">Delete Image</a>"+ + "</div>" + + "</form>", @u.facebook_link_to("Delete Image", "/images/9", :confirm => "Are you sure?", :method => :delete) + end +end + Index: init.rb ==================================================================--- init.rb (revision 224) +++ init.rb (working copy) @@ -19,6 +19,7 @@ require ''facebooker/rails/facebook_request_fix'' require ''facebooker/rails/routing'' require ''facebooker/rails/facebook_pretty_errors'' rescue nil +require ''facebooker/rails/facebook_url_helper'' module ::ActionController class Base def self.inherited_with_facebooker(subclass) @@ -26,6 +27,7 @@ if subclass.to_s == "ApplicationController" subclass.send(:include,Facebooker::Rails::Controller) subclass.helper Facebooker::Rails::Helpers + subclass.helper Facebooker::Rails::Helpers::UrlHelperExtensions end end class << self @@ -54,3 +56,7 @@ # pull :canvas=> into env in routing to allow for conditions ActionController::Routing::RouteSet.send :include, Facebooker::Rails::Routing::RouteSetExtensions ActionController::Routing::RouteSet::Mapper.send :include, Facebooker::Rails::Routing::MapperExtensions + +# for facebook_link_to, etc. +ActionView::Helpers::UrlHelper.send :include, Facebooker::Rails::Helpers::UrlHelperExtensions +
Richard Jordan
2008-Jun-21 02:47 UTC
[Facebooker-talk] [PATCH] complete facebook_url_helper
I converted the rest of the functions in the Rails UrlHelper module to work with Facebooker. These are now: facebook_button_to, facebook_link_to_unless, facebook_link_to_if, facebook_link_to_unless_current, and facebook_current?. The first file is the updated diff with changes to the /init.rb file to load the new facebook_url_helper.rb file as well as extensive tests on all the facebook url helpers. The other file is the updated ruby file with the new helpers as well as some insignificant changes to facebook_link_to. If anyone has trouble, I''ll try to point you in the right direction. Or if be it, I''d love to fix any bugs. Richard -------------- next part -------------- Index: test/rails_integration_test.rb ==================================================================--- test/rails_integration_test.rb (revision 224) +++ test/rails_integration_test.rb (working copy) @@ -6,6 +6,7 @@ require ''facebooker/rails/controller'' require ''facebooker/rails/helpers'' require ''facebooker/rails/facebook_form_builder'' + require ''facebooker/rails/facebook_url_helper'' require File.dirname(__FILE__)+''/../init'' require ''mocha'' @@ -806,4 +807,151 @@ end # rescue LoadError # $stderr.puts "Couldn''t find action controller. That''s OK. We''ll skip it." -end \ No newline at end of file +end + +class RailsUrlHelperExtensionsTest < Test::Unit::TestCase + class UrlHelperExtensionsClass + include ActionView::Helpers::UrlHelper + include Facebooker::Rails::Helpers::UrlHelperExtensions + include ActionView::Helpers::TagHelper + def initialize(controller) + ENV[''FACEBOOKER_RELATIVE_URL_ROOT''] =''facebook_app_name'' + @controller = controller + end + + def protect_against_forgery? + false + end + end + class UrlHelperExtensionsController < NoisyController + def index + render :nothing => true + end + def do_it + render :nothing => true + end + end + + # We need to simulate an outside request to the callback server + # where the ENV[''FACEBOOK_CANVAS_PATH] == ENV[''FACEBOOKER_RELATIVE_URL_ROOT''] + # is ***not*** prepended to the request uri + # ex. apps.facebook.com/facebook_app_name/controller/action (prepended) + # test.host/controller/action (***not*** prepended) + class FacebookRequest < ActionController::TestRequest + # Parse the canvas name off to simulate a real request. + def request_uri + super.gsub(/^\/#{ENV[''FACEBOOKER_RELATIVE_URL_ROOT'']}(.*)/, ''\1'' ) + end + end + + def setup + ENV[''FACEBOOK_CANVAS_PATH''] =''facebook_app_name'' + @controller = UrlHelperExtensionsController.new + @request = FacebookRequest.new + @response = ActionController::TestResponse.new + + @current_page = "http://test.host/rails_url_helper_extensions_test/url_helper_extensions/do_it?fb_sig_in_canvas=1" + @not_current_page = "http://some.host/control/action" + + @u = UrlHelperExtensionsClass.new(@controller) + @label = "Home Sweet Home" + @url = "www.home.com" + end + + def test_facebook_link_to + assert_equal @u.link_to(@label, at url), @u.facebook_link_to(@label, @url) + end + + def test_facebook_link_to_with_popup + assert_raises(ActionView::ActionViewError) {@u.facebook_link_to(@label, at url, :popup=>true)} + end + + def test_facebook_link_to_with_confirm + assert_equal "<a href=\''#\'' onclick=\"new Dialog().showChoice(\''\'',\''Are you sure?\'').onconfirm = function() { " + + "document.setLocation(\''http://facebooker.rubyforge.org/\''); return false; };\">Visit Awesome Site</a>", + @u.facebook_link_to("Visit Awesome Site", "http://facebooker.rubyforge.org/", :confirm => "Are you sure?") + end + + # Watch out for the form id, adding new tests will change it. + def test_facebook_link_to_with_method + assert_equal "<form display=\"none\" id=\"_form2_\" method=\"post\" action=\"/images/9\">"+ + "<div>" + + "<input name=\"_method\" type=\"hidden\" value=\"delete\" />"+ + "<a href=\''#\'' onclick=\"document.getElementById(\''_form2_\'').submit(); return false;\">"+ + "Delete without confirmation</a>"+ + "</div>" + + "</form>", @u.facebook_link_to("Delete without confirmation", "/images/9", :method => :delete) + end + + # Watch out for the form id, adding new tests will change it. + def test_facebook_link_to_with_confirm_and_method + assert_equal "<form display=\"none\" id=\"_form1_\" method=\"post\" action=\"/images/9\">"+ + "<div>" + + "<input name=\"_method\" type=\"hidden\" value=\"delete\" />"+ + "<a href=\''#\'' onclick=\"new Dialog().showChoice(\''\'',\''Are you sure?\'').onconfirm = function() { "+ + "document.getElementById(\''_form1_\'').submit(); return false; };\">Delete Image</a>"+ + "</div>" + + "</form>", @u.facebook_link_to("Delete Image", "/images/9", :confirm => "Are you sure?", :method => :delete) + end + + def test_facebook_button_to + assert_equal @u.button_to(@label, at url), @u.facebook_button_to(@label, @url) + end + + # Watch out for the form id, adding new tests will change it. + def test_facebook_button_to_with_confirm + assert_equal "<form id=\"_form0_\" method=\"post\" action=\"/images/9\" >"+ + "<div>" + + "<button onclick=\"new Dialog().showChoice(\''\'',\''Are you sure?\'').onconfirm = function() { "+ + "document.getElementById(\''_form0_\'').submit(); return false; }\" type=\"button\">Do it!</button>"+ + "</div>" + + "</form>", @u.facebook_button_to("Do it!", "/images/9", :confirm => "Are you sure?") + end + + def test_facebook_current_page_with_current_url_string + post :do_it, example_rails_params_including_fb + assert @u.facebook_current_page?(@current_page) + end + def test_facebook_current_page_with_non_current_url_string + post :do_it, example_rails_params_including_fb + assert !@u.facebook_current_page?(@not_current_page) + end + def test_facebook_current_page_with_current_url_hash + post :do_it, example_rails_params_including_fb + assert @u.facebook_current_page?(:action=>"do_it", :fb_sig_in_canvas=>"1") + end + def test_facebook_current_page_with_non_current_url_hash + post :do_it, example_rails_params_including_fb + assert !@u.facebook_current_page?(:action=>"not_action") + end + + def test_facebook_link_to_unless_with_true + assert_equal @label, @u.facebook_link_to_unless(true, at label, at url) + end + def test_facebook_link_to_unless_with_false + assert_equal @u.facebook_link_to(@label, at url, :confirm=>"What?"), @u.facebook_link_to_unless(false, at label, at url, :confirm=>"What?") + end + + def test_facebook_link_to_if_with_true + assert_equal @u.facebook_link_to(@label, at url, :confirm=>"What?"), @u.facebook_link_to_if(true, at label, at url, :confirm=>"What?") + end + def test_facebook_link_to_if_with_false + assert_equal @label, @u.facebook_link_to_if(false, at label, at url) + end + + def test_facebook_link_to_unless_current_with_current + post :do_it, example_rails_params_including_fb + assert_equal @label, @u.facebook_link_to_unless_current(@label,{:action=>"do_it", :fb_sig_in_canvas=>"1"}, :confirm=>"What?") + end + def test_facebook_link_to_unless_current_with_not_current + post :do_it, example_rails_params_including_fb + assert_equal @u.facebook_link_to(@label,{:action=>"index",:fb_sig_in_canvas=>"1"}, :confirm=>"What?"), + @u.facebook_link_to_unless_current(@label,{:action=>"index", :fb_sig_in_canvas=>"1"}, :confirm=>"What?") + end + + private + # Makes the canvas page be prepended for the current page tests + def example_rails_params_including_fb + {"fb_sig_in_canvas"=>"1"} + end +end Index: init.rb ==================================================================--- init.rb (revision 224) +++ init.rb (working copy) @@ -19,6 +19,7 @@ require ''facebooker/rails/facebook_request_fix'' require ''facebooker/rails/routing'' require ''facebooker/rails/facebook_pretty_errors'' rescue nil +require ''facebooker/rails/facebook_url_helper'' module ::ActionController class Base def self.inherited_with_facebooker(subclass) @@ -26,6 +27,7 @@ if subclass.to_s == "ApplicationController" subclass.send(:include,Facebooker::Rails::Controller) subclass.helper Facebooker::Rails::Helpers + subclass.helper Facebooker::Rails::Helpers::UrlHelperExtensions end end class << self @@ -54,3 +56,7 @@ # pull :canvas=> into env in routing to allow for conditions ActionController::Routing::RouteSet.send :include, Facebooker::Rails::Routing::RouteSetExtensions ActionController::Routing::RouteSet::Mapper.send :include, Facebooker::Rails::Routing::MapperExtensions + +# for facebook_link_to, etc. +ActionView::Helpers::UrlHelper.send :include, Facebooker::Rails::Helpers::UrlHelperExtensions + -------------- next part -------------- module Facebooker module Rails module Helpers module UrlHelperExtensions # Creates a link tag. If you do not pass any options, then it acts # exactly the same as link_to. See the documention there. # # Otherwise, it has some limitations because of # differences between JavaScript(JS) and Facebook Javascript(FBJS). # These include # * You cannot use <tt>:back</tt> to link to the referrer. # * You cannot use <tt>:popup</tt> to open a new window. # * You cannot use <tt>:href</tt> to specify the link location. # As FBJS and Facebooker evolves, these limitations may disapear or # be overcomed. # # ==== Options # * <tt>:confirm => ''question?''</tt> - This will add a Facebook Javascript # dialog with the question specified. If the user accepts, the link is # processed normally, otherwise no action is taken. # * <tt>:method => symbol of HTTP verb</tt> - This modifier will statically # create an HTML form and immediately submit the form for processing using # the HTTP verb specified. Unlike link_to, it mostly avoids Facebook # Javascript and instead wraps the link in an invisible form. Beware it # sets the form id to <tt>"_form"+ID_NUMBER+"_"</tt>. Do not use such an id # elsewhere. # * The +html_options+ will accept a hash of html attributes for the link tag. # # ==== Examples # It conforms to Facebook Javascript unlike link_to. Specifically, link_to fails with # Facebook Javascript when using the <tt>:method</tt> and <tt>:confirm</tt> # options. Use as follows: # # facebook_link_to "Visit Awesome Site", "http://facebooker.rubyforge.org/", :confirm => "Are you sure?" # # => <a href=''#'' onclick="new Dialog().showChoice('''',''Are you sure?'').onconfirm = function() { # document.setLocation(''http://facebooker.rubyforge.org/''); return false; };">Visit Awesome Site</a> # # facebook_link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete # # => <form display="none" id="_form1_" method="post" action="/images/9"> # <div> # <input name="_method" type="hidden" value="delete" /> # <a href=''#'' onclick="new Dialog().showChoice('''',''Are you sure?'').onconfirm = function() { # document.getElementById(''_form1_'').submit(); return false; }">Delete Image</a> # </div> # </form> # # facebook_link_to "Delete without confirmation", @image, :method => :delete # # => <form display="none" id="_form2_" method="post" action="/images/9"> # <div> # <input name="_method" type="hidden" value="delete" /> # <a href=''#'' onclick="document.getElementById(''_form2_'').submit(); return false; }"> # Delete without confirmation</a> # </div> # </form> def facebook_link_to(name, options = {}, fbml_options = nil) # maybe javascript is not needed, so just do the regular link_to return link_to(name,options,fbml_options) unless(fbml_options) fbml_options = fbml_options.stringify_keys return link_to(name,options,fbml_options) unless(javascript_needed?(fbml_options) ) url = options.is_a?(String) ? options : self.url_for(options) # save the method method = fbml_options[''method''] convert_options_to_facebook_javascript!(fbml_options, url) tag_options = tag_options(fbml_options) link_tag = "<a href=''#''#{tag_options}>#{name || url}</a>" return link_tag unless method # fbjs is tricky so we will make a form to look like the link method_tag = '''' if (%w{put delete}.include?(method.to_s) ) method_tag = tag(''input'', :type => ''hidden'', :name => ''_method'', :value => method.to_s) end form_method = method.to_s == ''get'' ? ''get'' : ''post'' request_token_tag = '''' if form_method == ''post'' && protect_against_forgery? request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end "<form display=\"none\" #{next_form_id_attr} method=\"#{form_method}\" action=\"#{escape_once url}\"><div>" + method_tag + link_tag + request_token_tag + "</div></form>" end # Generates a form element containing a single button that submits # to the URL created by the set of +options+. All options are the same # as for the regular button_to. See the documentation of button_to. # # The only difference is <tt>:confirm</tt>. Because of Facebook Javascript(FBJS), # button_to silently fails for <tt>:confirm</tt>. Here it creates a FBJS dialog. # # To use CSS styling pass in <tt>:class</tt>. For example, the Facebook button # styling: (<em>See</em> http://wiki.developers.facebook.com/index.php/CSS_Tips_and_Tricks) # .inputbutton, # .inputsubmit { # padding: 2px 15px 3px 15px; # border-style: solid; # border-top-width: 1px; # border-left-width: 1px; # border-bottom-width: 1px; # border-right-width: 1px; # border-top-color: #D9DFEA; # border-left-color: #D9DFEA; # border-bottom-color: #0e1f5b; # border-right-color: #0e1f5b; # background-color: #3b5998; # color: #FFFFFF; # font-size: 11px; # font-family: "lucida grande", tahoma, verdana, arial, sans-serif; # text-align: center; } def facebook_button_to(name, options = {}, fbml_options = {}) fbml_options = fbml_options.stringify_keys return button_to(name,options,fbml_options) unless(fbml_options["confirm"]) convert_boolean_attributes!(fbml_options, %w( disabled )) method_tag = '''' if (method = fbml_options.delete(''method'')) && %w{put delete}.include?(method.to_s) method_tag = tag(''input'', :type => ''hidden'', :name => ''_method'', :value => method.to_s) end form_method = method.to_s == ''get'' ? ''get'' : ''post'' request_token_tag = '''' if form_method == ''post'' && protect_against_forgery? request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end url = options.is_a?(String) ? options : self.url_for(options) name ||= url form_id_attr = '''' if confirm = fbml_options.delete("confirm") fbml_options["onclick"] = "#{confirm_facebook_javascript_function(confirm,url,nil,true)}" form_id_attr = "#{next_form_id_attr} " # add a space after it end fbml_options.merge!("type"=>"button") "<form #{form_id_attr}method=\"#{form_method}\" action=\"#{escape_once url}\" ><div>" + method_tag + content_tag("button", name, fbml_options) + request_token_tag + "</div></form>" end # Same as link_to_unless_current except it uses facebook_link_to and facebook_current?. def facebook_link_to_unless_current(name, options = {}, fbml_options = {}, &block) facebook_link_to_unless facebook_current_page?(options), name, options, fbml_options, &block end # Same as link_to_unless, except it uses facebook_link_to. def facebook_link_to_unless(condition, name, options = {}, fbml_options = {}, &block) if condition if block_given? block.arity <= 1 ? yield(name) : yield(name, options, fbml_options) else name end else facebook_link_to(name, options, fbml_options) end end # Same as link_to_if except it uses facebook_link_to. def facebook_link_to_if(condition, name, options = {}, fbml_options = {}, &block) facebook_link_to_unless !condition, name, options, fbml_options, &block end # True if the current request URI was generated by the given +options+. # Same as current_page? except for one very important fix. It adjusts # for the facebook canvas page name. # # ==== Example # Let''s say we''re in the <tt>/facebook_canvas_name/shop/checkout</tt> action. # The canvas page will ruin current_page? but here it adjusts. # # current_page?(:controller => ''shop'', :action => ''checkout'') # # => false # # facebook_current_page?(:controller => ''shop'', :action => ''checkout'') # # => true def facebook_current_page?(options) url_string = CGI.escapeHTML(url_for(options)).gsub(/^\/#{ENV["FACEBOOKER_RELATIVE_URL_ROOT"]}(.*)/, ''\1'' ) request = @controller.request if url_string =~ /^\w+:\/\// url_string == "#{request.protocol}#{request.host_with_port}#{request.request_uri}" else url_string == request.request_uri end end private # Makes sure the right form submits @@form_id=0 def form_id "_form#{@@form_id.to_s}_" end # Increments the form_id so we don''t reuse it. # All our forms then will have a unique id. def next_form_id_attr form_id_attr = "id=\"#{form_id}\"" @@form_id = @@form_id + 1 form_id_attr end # Returns true when we must create an onclick tag. def javascript_needed?(fbml_options) (fbml_options["confirm"] || fbml_options["popup"] || fbml_options["method"]) end # Does not allow href or popup attributes. def convert_options_to_facebook_javascript!(fbml_options, url = '''') confirm, popup = fbml_options.delete("confirm"), fbml_options.delete("popup") method, href = fbml_options.delete("method"), fbml_options[''href''] fbml_options["onclick"] = case when href || popup raise ActionView::ActionViewError, "You can''t use :href or :popup -- at least yet" when confirm "#{confirm_facebook_javascript_function(confirm, url, href, method)};" when method "#{method_submit}" else fbml_options["onclick"] end end def method_submit "document.getElementById(''#{escape_javascript(form_id)}'').submit(); return false;" end def change_location(url) "document.setLocation(''#{escape_javascript(url)}''); return false;" end # Creates a facebook dialog. Changes the FBJS function based on whether a method was chosen. def confirm_facebook_javascript_function(confirm, url, href, method_defined=false) "new Dialog().showChoice('''',''#{escape_javascript(confirm)}'').onconfirm = function() { #{method_defined ? method_submit : change_location(url)} }" end end end end end
Would it be possible to make the normal version work? I hate having to use facebooker_button_to instead of just button_to. We did that with link_to_remote by just making the facebooker.js interface match prototype. Is that possible here too? If you can make it work transparently, I''ll give you commit access to keep making these changes :) Mike On Jun 20, 2008, at 10:47 PM, Richard Jordan wrote:> I converted the rest of the functions in the Rails UrlHelper module > to work with Facebooker. These are now: facebook_button_to, > facebook_link_to_unless, facebook_link_to_if, > facebook_link_to_unless_current, and facebook_current?. The first > file is the updated diff with changes to the /init.rb file to load > the new facebook_url_helper.rb file as well as extensive tests on > all the facebook url helpers. The other file is the updated ruby > file with the new helpers as well as some insignificant changes to > facebook_link_to. > > If anyone has trouble, I''ll try to point you in the right > direction. Or if be it, I''d love to fix any bugs. > > Richard > < > facebook_url_helper > .diff > > > < > facebook_url_helper.rb>_______________________________________________ > Facebooker-talk mailing list > Facebooker-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/facebooker-talk-- Mike Mangino http://www.elevatedrails.com
Richard Jordan
2008-Jun-23 16:32 UTC
[Facebooker-talk] [PATCH] complete facebook_url_helper
Sure, all of these facebook_link_to, facebook_button_to, etc. could be just link_to, button_to, etc. I just call the regular link_to, button_to if none of the options passed require javascript. I''d love to make a few changes and get those to work transparently. I''ll check out how you did the link_to_remote and see what I can do. Also, I''ve found a few other problems with my changes. It''d great if I could have commit access and make sure it all works perfectly. I''ll be real careful and quick on any bugs, too. Awesome, Richard On Mon, 23 Jun 2008, Mike Mangino wrote:> Would it be possible to make the normal version work? I hate having to use > facebooker_button_to instead of just button_to. We did that with > link_to_remote by just making the facebooker.js interface match prototype. Is > that possible here too? If you can make it work transparently, I''ll give you > commit access to keep making these changes :) > > Mike > > > On Jun 20, 2008, at 10:47 PM, Richard Jordan wrote: > >> I converted the rest of the functions in the Rails UrlHelper module to work >> with Facebooker. These are now: facebook_button_to, >> facebook_link_to_unless, facebook_link_to_if, >> facebook_link_to_unless_current, and facebook_current?. The first file is >> the updated diff with changes to the /init.rb file to load the new >> facebook_url_helper.rb file as well as extensive tests on all the facebook >> url helpers. The other file is the updated ruby file with the new helpers >> as well as some insignificant changes to facebook_link_to. >> >> If anyone has trouble, I''ll try to point you in the right direction. Or if >> be it, I''d love to fix any bugs. >> >> Richard<facebook_url_helper.diff><facebook_url_helper.rb>_______________________________________________ >> Facebooker-talk mailing list >> Facebooker-talk at rubyforge.org >> http://rubyforge.org/mailman/listinfo/facebooker-talk > > -- > Mike Mangino > http://www.elevatedrails.com > > >
Richard (and those interested), We''ve also began to re-impliment lots of the prototype helpers in FBJS. Mostly for simple toggling, adding class names, etc. and ajax forms. We''ll be committing this back to Facebooker in the near future, but can''t do it right now. Maybe we can put our .js file on github for the time being if there''s interest in collaborating on this from anyone else? BJ Clark On Jun 23, 2008, at 10:32 AM, Richard Jordan wrote:> Sure, all of these facebook_link_to, facebook_button_to, etc. could > be just link_to, button_to, etc. I just call the regular link_to, > button_to if none of the options passed require javascript. I''d > love to make a few changes and get those to work transparently. > I''ll check out how you did the link_to_remote and see what I can do. > > Also, I''ve found a few other problems with my changes. It''d great > if I could have commit access and make sure it all works perfectly. > I''ll be real careful and quick on any bugs, too. > > Awesome, > Richard > > > On Mon, 23 Jun 2008, Mike Mangino wrote: > >> Would it be possible to make the normal version work? I hate having >> to use facebooker_button_to instead of just button_to. We did that >> with link_to_remote by just making the facebooker.js interface >> match prototype. Is that possible here too? If you can make it work >> transparently, I''ll give you commit access to keep making these >> changes :) >> >> Mike >> >> >> On Jun 20, 2008, at 10:47 PM, Richard Jordan wrote: >> >>> I converted the rest of the functions in the Rails UrlHelper >>> module to work with Facebooker. These are now: >>> facebook_button_to, facebook_link_to_unless, facebook_link_to_if, >>> facebook_link_to_unless_current, and facebook_current?. The first >>> file is the updated diff with changes to the /init.rb file to load >>> the new facebook_url_helper.rb file as well as extensive tests on >>> all the facebook url helpers. The other file is the updated ruby >>> file with the new helpers as well as some insignificant changes to >>> facebook_link_to. >>> If anyone has trouble, I''ll try to point you in the right >>> direction. Or if be it, I''d love to fix any bugs. >>> Richard >>> < >>> facebook_url_helper >>> .diff >>> > >>> < >>> facebook_url_helper >>> .rb>_______________________________________________ >>> Facebooker-talk mailing list >>> Facebooker-talk at rubyforge.org >>> http://rubyforge.org/mailman/listinfo/facebooker-talk >> >> -- >> Mike Mangino >> http://www.elevatedrails.com >> >> >> > _______________________________________________ > Facebooker-talk mailing list > Facebooker-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/facebooker-talk
That would be great! I''m open to using github for Facebooker as well. Mike On Jun 23, 2008, at 2:10 PM, BJ Clark wrote:> Richard (and those interested), > > We''ve also began to re-impliment lots of the prototype helpers in > FBJS. > Mostly for simple toggling, adding class names, etc. and ajax forms. > We''ll be committing this back to Facebooker in the near future, but > can''t do it right now. > Maybe we can put our .js file on github for the time being if > there''s interest in collaborating on this from anyone else? > > BJ Clark > > > > On Jun 23, 2008, at 10:32 AM, Richard Jordan wrote: > >> Sure, all of these facebook_link_to, facebook_button_to, etc. could >> be just link_to, button_to, etc. I just call the regular link_to, >> button_to if none of the options passed require javascript. I''d >> love to make a few changes and get those to work transparently. >> I''ll check out how you did the link_to_remote and see what I can do. >> >> Also, I''ve found a few other problems with my changes. It''d great >> if I could have commit access and make sure it all works >> perfectly. I''ll be real careful and quick on any bugs, too. >> >> Awesome, >> Richard >> >> >> On Mon, 23 Jun 2008, Mike Mangino wrote: >> >>> Would it be possible to make the normal version work? I hate >>> having to use facebooker_button_to instead of just button_to. We >>> did that with link_to_remote by just making the facebooker.js >>> interface match prototype. Is that possible here too? If you can >>> make it work transparently, I''ll give you commit access to keep >>> making these changes :) >>> >>> Mike >>> >>> >>> On Jun 20, 2008, at 10:47 PM, Richard Jordan wrote: >>> >>>> I converted the rest of the functions in the Rails UrlHelper >>>> module to work with Facebooker. These are now: >>>> facebook_button_to, facebook_link_to_unless, facebook_link_to_if, >>>> facebook_link_to_unless_current, and facebook_current?. The >>>> first file is the updated diff with changes to the /init.rb file >>>> to load the new facebook_url_helper.rb file as well as extensive >>>> tests on all the facebook url helpers. The other file is the >>>> updated ruby file with the new helpers as well as some >>>> insignificant changes to facebook_link_to. >>>> If anyone has trouble, I''ll try to point you in the right >>>> direction. Or if be it, I''d love to fix any bugs. >>>> Richard >>>> < >>>> facebook_url_helper >>>> .diff >>>> > >>>> < >>>> facebook_url_helper >>>> .rb>_______________________________________________ >>>> Facebooker-talk mailing list >>>> Facebooker-talk at rubyforge.org >>>> http://rubyforge.org/mailman/listinfo/facebooker-talk >>> >>> -- >>> Mike Mangino >>> http://www.elevatedrails.com >>> >>> >>> >> _______________________________________________ >> Facebooker-talk mailing list >> Facebooker-talk at rubyforge.org >> http://rubyforge.org/mailman/listinfo/facebooker-talk > > _______________________________________________ > Facebooker-talk mailing list > Facebooker-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/facebooker-talk-- Mike Mangino http://www.elevatedrails.com
Aurélien Malisart
2008-Jun-23 18:31 UTC
[Facebooker-talk] [PATCH] complete facebook_url_helper
Since all the rails community is switching to Git, I think it''s a good idea... aurels -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/facebooker-talk/attachments/20080623/de851c75/attachment-0001.html>
Attached is a new version of facebook_url_helper in pristine fbjs. I guarantee no one can write any better. Here is a summary. 1. First, I looked into prototype, but the Rais UrlHelper module does not use prototype. link_to, button_to, current_page?, etc. do not use the prototype.js file. You don''t have to include any javascript file. So, I couldn''t mimic prototype.js. Sorry Mike, I see you keep asking for patches for it. 2. So, second, I thought I might just mimic js, but it turns out you cannot extend DOM objects in fbjs. I wanted to not touch UrlHelper at all like you did with link_to_remote. Wow, that is beautiful. But, you cannot in fbjs extend the DOM and do, for ex., Object.prototype.href = this.getHref(). I tried it and know so. Also, I have some vague proof from the FB wiki:>"You aren''t allowed to extend base objects like Function or Array ..." >"FBJS objects don''t contain handles to any of their actual DOM objects,"So, it is impossible to mimic js with fbjs. You cannot just have UrlHelper do js and convert it to fbjs behind its back by adding to facebooker.js. 3. That leaves you one last option -- edit UrlHelper itself. So, I went back to the beginning and rewrote facebook_url_helper to transparently change link_to, button_to, etc. That simplified the code tremendously. I didn''t even have to touch link_to directly! I changed one darn line in button_to. Also, I had some real brilliant breakthroughs with fbjs. So, the only way facebooker can support link_to, button_to, etc. is with the attached code. See what you think. Richard On Mon, 23 Jun 2008, Mike Mangino wrote:> Would it be possible to make the normal version work? I hate having to use > facebooker_button_to instead of just button_to. We did that with > link_to_remote by just making the facebooker.js interface match prototype. Is > that possible here too? If you can make it work transparently, I''ll give you > commit access to keep making these changes :) > > Mike >-------------- next part -------------- module Facebooker module Rails module Helpers module UrlHelperExtensions # Adjusted to be the same as the regular button_to. def button_to(name, options = {}, html_options = {}) html_options = html_options.stringify_keys convert_boolean_attributes!(html_options, %w( disabled )) method_tag = '''' if (method = html_options.delete(''method'')) && %w{put delete}.include?(method.to_s) method_tag = tag(''input'', :type => ''hidden'', :name => ''_method'', :value => method.to_s) end form_method = method.to_s == ''get'' ? ''get'' : ''post'' request_token_tag = '''' if form_method == ''post'' && protect_against_forgery? request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end if confirm = html_options.delete("confirm") # this line is the only change => html_options["onclick"] = "return #{confirm_javascript_function(confirm)}" html_options["onclick"] = "#{confirm_javascript_function(confirm, ''a.getParentNode().getParentNode().submit();'')}return false;" end url = options.is_a?(String) ? options : self.url_for(options) name ||= url html_options.merge!("type" => "submit", "value" => name) "<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button-to\"><div>" + method_tag + tag("input", html_options) + request_token_tag + "</div></form>" end # Adjusted to be the same as the regular current_page? def current_page?(options) # this line is the only change => url_string = CGI.escapeHTML(url_for(options)) url_string = CGI.escapeHTML(url_for(options)).gsub(/^\/#{ENV["FACEBOOKER_RELATIVE_URL_ROOT"]}(.*)/, ''\1'' ) request = @controller.request if url_string =~ /^\w+:\/\// url_string == "#{request.protocol}#{request.host_with_port}#{request.request_uri}" else url_string == request.request_uri end end private # Overrides a private method that link_to calls. Now, we don''t have to change link_to at all! def convert_options_to_javascript!(html_options, url = '''') confirm, popup = html_options.delete("confirm"), html_options.delete("popup") method, href = html_options.delete("method"), html_options[''href''] html_options["onclick"] = case when popup raise ActionView::ActionViewError, "You can''t use :popup" when method # or maybe (confirm and method) "#{method_javascript_function(method, url, href, confirm)}return false;" when confirm # and only confirm "#{confirm_javascript_function(confirm)}return false;" else html_options["onclick"] end end # Overrides a private method that link_to calls via convert_options_to_javascript! and # also, button_to calls directly. def confirm_javascript_function(confirm, fun = nil) "var dlg = new Dialog().showChoice(''Confirm Request'',''#{escape_javascript(confirm)}'');var a=this;" + "dlg.onconfirm = function() { #{fun ? fun : ''document.setLocation(a.getHref());''} };" end # Called by confirm_javascript_function if and only if link_to has :method defined. def method_javascript_function(method, url = '''', href = nil, confirm = nil) action = (href && url.size > 0) ? "''#{url}''" : ''a.getHref()'' submit_function "var f = document.createElement(''form''); f.setStyle(''display'',''none''); " + "a.getParentNode().appendChild(f); f.setMethod(''POST''); f.setAction(#{action});" unless method == :post submit_function << "var m = document.createElement(''input''); m.setType(''hidden''); " submit_function << "m.setName(''_method''); m.setValue(''#{method}''); f.appendChild(m);" end if protect_against_forgery? submit_function << "var s = document.createElement(''input''); s.setType(''hidden''); " submit_function << "s.setName(''#{request_forgery_protection_token}''); s.setValue(''#{escape_javascript form_authenticity_token}''); f.appendChild(s);" end submit_function << "f.submit();" if(confirm) confirm_javascript_function(confirm, submit_function) else "var a=this;" + submit_function end end end end end end -------------- next part -------------- Index: test/rails_integration_test.rb ==================================================================--- test/rails_integration_test.rb (revision 225) +++ test/rails_integration_test.rb (working copy) @@ -6,6 +6,7 @@ require ''facebooker/rails/controller'' require ''facebooker/rails/helpers'' require ''facebooker/rails/facebook_form_builder'' + require ''facebooker/rails/facebook_url_helper'' require File.dirname(__FILE__)+''/../init'' require ''mocha'' @@ -811,6 +812,149 @@ assert_equal "<fb:editor-custom label=\"Friends\"></fb:editor-custom>", at form_builder.multi_friend_input end end + +class RailsUrlHelperExtensionsTest < Test::Unit::TestCase + class UrlHelperExtensionsClass + include ActionView::Helpers::UrlHelper + include Facebooker::Rails::Helpers::UrlHelperExtensions + include ActionView::Helpers::TagHelper + def initialize(controller) + ENV[''FACEBOOKER_RELATIVE_URL_ROOT''] =''facebook_app_name'' + @controller = controller + end + + def protect_against_forgery? + false + end + end + class UrlHelperExtensionsController < NoisyController + def index + render :nothing => true + end + def do_it + render :nothing => true + end + end + + # We need to simulate an outside request to the callback server + # where the ENV[''FACEBOOK_CANVAS_PATH] == ENV[''FACEBOOKER_RELATIVE_URL_ROOT''] + # is ***not*** prepended to the request uri + # ex. apps.facebook.com/facebook_app_name/controller/action (prepended) + # test.host/controller/action (***not*** prepended) + class FacebookRequest < ActionController::TestRequest + # Parse the canvas name off to simulate a real request. + def request_uri + super.gsub(/^\/#{ENV[''FACEBOOKER_RELATIVE_URL_ROOT'']}(.*)/, ''\1'' ) + end + end + + def setup + ENV[''FACEBOOK_CANVAS_PATH''] =''facebook_app_name'' + @controller = UrlHelperExtensionsController.new + @request = FacebookRequest.new + @response = ActionController::TestResponse.new + + @current_page = "http://test.host/rails_url_helper_extensions_test/url_helper_extensions/do_it?fb_sig_in_canvas=1" + @not_current_page = "http://some.host/control/action" + + @u = UrlHelperExtensionsClass.new(@controller) + @label = "Testing" + @url = "test.host" + end + + def test_link_to + assert_equal "<a href=\"test.host\">Testing</a>", @u.link_to(@label, @url) + end + + def test_link_to_with_popup + assert_raises(ActionView::ActionViewError) {@u.link_to(@label, at url, :popup=>true)} + end + + def test_link_to_with_confirm + assert_dom_equal( "<a href=\"#{@url}\" onclick=\"var dlg = new Dialog().showChoice(\''Confirm Request\'',\''Are you sure?\'');"+ + "var a=this;dlg.onconfirm = function() { " + + "document.setLocation(a.getHref()); };return false;\">#{@label}</a>", + @u.link_to(@label, @url, :confirm => "Are you sure?") ) + end + + def test_link_to_with_method + assert_dom_equal( "<a href=\"#{@url}\" onclick=\"var a=this;var f = document.createElement(''form''); f.setStyle(''display'',''none''); "+ + "a.getParentNode().appendChild(f); f.setMethod(''POST''); f.setAction(a.getHref());" + + "var m = document.createElement(''input''); m.setType(''hidden''); "+ + "m.setName(''_method''); m.setValue(''delete''); f.appendChild(m);"+ + "f.submit();return false;\">#{@label}</a>", @u.link_to(@label, at url, :method=>:delete)) + end + + def test_link_to_with_confirm_and_method + assert_dom_equal( "<a href=\"#{@url}\" onclick=\"var dlg = new Dialog().showChoice(\''Confirm Request\'',\''Are you sure?\'');"+ + "var a=this;dlg.onconfirm = function() { " + + "var f = document.createElement(''form''); f.setStyle(''display'',''none''); "+ + "a.getParentNode().appendChild(f); f.setMethod(''POST''); f.setAction(a.getHref());" + + "var m = document.createElement(''input''); m.setType(''hidden''); "+ + "m.setName(''_method''); m.setValue(''delete''); f.appendChild(m);"+ + "f.submit(); };return false;\">#{@label}</a>", @u.link_to(@label, at url, :confirm=>''Are you sure?'', :method=>:delete) ) + end + + def test_button_to + assert_equal "<form method=\"post\" action=\"#{@url}\" class=\"button-to\"><div>" + + "<input type=\"submit\" value=\"#{@label}\" /></div></form>", @u.button_to(@label, at url) + end + + def test_button_to_with_confirm + assert_equal "<form method=\"post\" action=\"#{@url}\" class=\"button-to\"><div>" + + "<input onclick=\"var dlg = new Dialog().showChoice(\''Confirm Request\'',\''Are you sure?\'');"+ + "var a=this;dlg.onconfirm = function() { "+ + "a.getParentNode().getParentNode().submit(); };return false;\" type=\"submit\" value=\"#{@label}\" /></div></form>", + @u.button_to(@label, at url, :confirm=>"Are you sure?") + end + def test_current_page_with_current_url_string + post :do_it, example_rails_params_including_fb + assert @u.current_page?(@current_page) + end + def test_current_page_with_non_current_url_string + post :do_it, example_rails_params_including_fb + assert !@u.current_page?(@not_current_page) + end + def test_current_page_with_current_url_hash + post :do_it, example_rails_params_including_fb + assert @u.current_page?(:action=>"do_it", :fb_sig_in_canvas=>"1") + end + def test_current_page_with_non_current_url_hash + post :do_it, example_rails_params_including_fb + assert !@u.current_page?(:action=>"not_action") + end + + def test_link_to_unless_with_true + assert_equal @label, @u.link_to_unless(true, at label, at url) + end + def test_link_to_unless_with_false + assert_equal @u.link_to(@label, at url), @u.link_to_unless(false, at label, at url) + end + + def test_link_to_if_with_true + assert_equal @u.link_to(@label, at url), @u.link_to_if(true, at label, at url) + end + def test_link_to_if_with_false + assert_equal @label, @u.link_to_if(false, at label, at url) + end + + def test_link_to_unless_current_with_current + post :do_it, example_rails_params_including_fb + assert_equal @label, @u.link_to_unless_current(@label,{:action=>"do_it", :fb_sig_in_canvas=>"1"}) + end + def test_link_to_unless_current_with_not_current + post :do_it, example_rails_params_including_fb + assert_equal @u.link_to(@label,{:action=>"index",:fb_sig_in_canvas=>"1"}), + @u.link_to_unless_current(@label,{:action=>"index", :fb_sig_in_canvas=>"1"}) + end + + private + # Makes the canvas page be prepended for the current page tests + def example_rails_params_including_fb + {"fb_sig_in_canvas"=>"1"} + end +end + # rescue LoadError # $stderr.puts "Couldn''t find action controller. That''s OK. We''ll skip it." end Index: init.rb ==================================================================--- init.rb (revision 225) +++ init.rb (working copy) @@ -19,6 +19,8 @@ require ''facebooker/rails/facebook_request_fix'' require ''facebooker/rails/routing'' require ''facebooker/rails/facebook_pretty_errors'' rescue nil +require ''facebooker/rails/facebook_url_helper'' +require ''facebooker/rails/helpers'' module ::ActionController class Base def self.inherited_with_facebooker(subclass) @@ -26,6 +28,7 @@ if subclass.to_s == "ApplicationController" subclass.send(:include,Facebooker::Rails::Controller) subclass.helper Facebooker::Rails::Helpers + subclass.helper Facebooker::Rails::Helpers::UrlHelperExtensions end end class << self Index: lib/facebooker/models/user.rb ==================================================================--- lib/facebooker/models/user.rb (revision 225) +++ lib/facebooker/models/user.rb (working copy) @@ -211,7 +211,7 @@ @profile_info ||= @session.post(''facebook.profile.getInfo'', :uid => self.id) do |section| InfoSection.from_hash(section) end - + end ## # Set the status of the user #
> > > 3. That leaves you one last option -- edit UrlHelper itself. So, I > went back to the beginning and rewrote facebook_url_helper to > transparently change link_to, button_to, etc. That simplified the > code tremendously. I didn''t even have to touch link_to directly! I > changed one darn line in button_to. Also, I had some real brilliant > breakthroughs with fbjs.Does this change the code for non canvas pages as well? I''m having a really hard time following what is going on here. Can you explain how this works? Mike> > > So, the only way facebooker can support link_to, button_to, etc. is > with the attached code. See what you think.I don''t think I''d say it is the only way :) you could always do: def button_to_with_facebooker(*args) if !request_is_for_a_facebook_canvas? button_to_without_facebooker(*args) else #facebooker code here end end alias_method_chain :button_to, :facebooker That way the normal Rails path isn''t impacted and you can do the bare minimum implementation that is completely custom for Facebooker. Mike> > > Richard > > On Mon, 23 Jun 2008, Mike Mangino wrote: > >> Would it be possible to make the normal version work? I hate having >> to use facebooker_button_to instead of just button_to. We did that >> with link_to_remote by just making the facebooker.js interface >> match prototype. Is that possible here too? If you can make it work >> transparently, I''ll give you commit access to keep making these >> changes :) >> >> Mike > <facebook_url_helper.rb><facebook_url_helper2.diff>-- Mike Mangino http://www.elevatedrails.com
> Does this change the code for non canvas pages as well? I''m having a really > hard time following what is going on here.Oh, yes, you''re right my changes would would replace js with fbjs on non canvas pages as well. Yikes!> Can you explain how this works?What my code does is override methods defined in the UrlHelper -- mostly private methods. I wondered if these changes could be fragile. It seems they definately are.> I don''t think I''d say it is the only way :)I was hoping you would come up with some better way. Thanks! I love learning new tricks.> you could always do: > > def button_to_with_facebooker(*args) > if !request_is_for_a_facebook_canvas? > button_to_without_facebooker(*args) > else > #facebooker code here > end > end > > alias_method_chain :button_to, :facebooker > > That way the normal Rails path isn''t impacted and you can do the bare minimum > implementation that is completely custom for Facebooker.Let me see about this alias chain deal. I have been reading about it, something like a decorator pattern, right? So, we decorate button_to with my extensions if the request is inside facebook. Otherwise, we decorate button_to with nothing. We just do the old regular button_to. That''s much, much better. I''ll get on this change and do it first class. It''s great to see material we learn in school in the real world. Richard
Attached are a diff and a new facebook_url_helper.rb that works with both non-canvas and canvas pages using the alias_method_chaining. As requested by Charles de Buerger, I also added extra functionality to the :confirm option, so you can harness the whole power of Facebook dialogs. With :confirm => [title, prompt, styles], you can stylize a dialog''s text, color, and size in full generality. It''s a little bit clunky. I wanted to do :confirm => [title,prompt, {:width=>"300px",:color=>"black", ...}], but, apparently, Ruby doesn''t have syntactic sugar for 2-deep hashes? You can also just pass in the regular :confirm =>"Are you sure?" like usual. See if all is ok. Richard On Fri, 27 Jun 2008, Richard Jordan wrote:>> Does this change the code for non canvas pages as well? I''m having a >> really hard time following what is going on here. > Oh, yes, you''re right my changes would would replace js with fbjs on non > canvas pages as well. Yikes! > >> Can you explain how this works? > What my code does is override methods defined in the UrlHelper -- mostly > private methods. I wondered if these changes could be fragile. It seems > they definately are. > >> I don''t think I''d say it is the only way :) > I was hoping you would come up with some better way. Thanks! I love > learning new tricks. > >> you could always do: >> >> def button_to_with_facebooker(*args) >> if !request_is_for_a_facebook_canvas? >> button_to_without_facebooker(*args) >> else >> #facebooker code here >> end >> end >> >> alias_method_chain :button_to, :facebooker >> >> That way the normal Rails path isn''t impacted and you can do the bare >> minimum implementation that is completely custom for Facebooker. > Let me see about this alias chain deal. I have been reading about it, > something like a decorator pattern, right? So, we decorate button_to with my > extensions if the request is inside facebook. Otherwise, we decorate > button_to with nothing. We just do the old regular button_to. That''s much, > much better. I''ll get on this change and do it first class. It''s great to > see material we learn in school in the real world. > > Richard > _______________________________________________ > Facebooker-talk mailing list > Facebooker-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/facebooker-talk > >-------------- next part -------------- # Extends the ActionView::Helpers::UrlHelper module. See it for details on # the usual url helper methods: url_for, link_to, button_to, etc. # # Mostly, the changes sanitize javascript into facebook javascript. # It sanitizes link_to solely by altering the private methods: # convert_options_to_javascript!, confirm_javascript_function, and # method_javascript_function. For button_to, it alters button_to # itself, as well as confirm_javascript_function. No other methods # need to be changed because of Facebook javascript. # # Finally, it changes current_page? not for javascript reasons, but to # adjust to the Facebook canvas root name. It strips off the canvas # root name to make current_page? return accurately within Facebook. # Because external requests come in the format # "http://test.host/controller/action" but Facebook urls come in the # format "http://apps.facebook.com/canvas_root_name/controller/action," # it has to strip off the "canvas_root_name" to adjust. A call on # current_page? for (:controller=>"x", :action=>"y") must return true # without respect to any prepended Facebook canvas root name. See # tests for more clarification. module ActionView module Helpers module UrlHelper # Alters one and only one line of the Rails button_to. See below. def button_to_with_facebooker(name, options={}, html_options = {}) if !request_is_for_a_facebook_canvas? button_to_without_facebooker(name,options,html_options) else html_options = html_options.stringify_keys convert_boolean_attributes!(html_options, %w( disabled )) method_tag = '''' if (method = html_options.delete(''method'')) && %w{put delete}.include?(method.to_s) method_tag = tag(''input'', :type => ''hidden'', :name => ''_method'', :value => method.to_s) end form_method = method.to_s == ''get'' ? ''get'' : ''post'' request_token_tag = '''' if form_method == ''post'' && protect_against_forgery? request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end if confirm = html_options.delete("confirm") # this line is the only change => html_options["onclick"] = "return #{confirm_javascript_function(confirm)}" html_options["onclick"] = "#{confirm_javascript_function(confirm, ''a.getParentNode().getParentNode().submit();'')}return false;" end url = options.is_a?(String) ? options : self.url_for(options) name ||= url html_options.merge!("type" => "submit", "value" => name) "<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button-to\"><div>" + method_tag + tag("input", html_options) + request_token_tag + "</div></form>" end end alias_method_chain :button_to, :facebooker # Strips off the Facebook canvas root name. def current_page_with_facebooker?(options) if !request_is_for_a_facebook_canvas? current_page_without_facebooker?(options) else # this line is the only change => url_string = CGI.escapeHTML(url_for(options)) url_string = CGI.escapeHTML(url_for(options)).gsub(/^\/#{ENV["FACEBOOKER_RELATIVE_URL_ROOT"]}(.*)/, ''\1'' ) request = @controller.request if url_string =~ /^\w+:\/\// url_string == "#{request.protocol}#{request.host_with_port}#{request.request_uri}" else url_string == request.request_uri end end end alias_method_chain :current_page?, :facebooker private # Redefined from controller.rb. Could be encapsilated into # some utility module to avoid repetition. def request_is_for_a_facebook_canvas? !params[''fb_sig_in_canvas''].blank? end # Altered to throw an error on :popup and sanitize the javascript # for Facebook. def convert_options_to_javascript_with_facebooker!(html_options, url ='''') if !request_is_for_a_facebook_canvas? convert_options_to_javascript_without_facebooker!(html_options,url) else confirm, popup = html_options.delete("confirm"), html_options.delete("popup") method, href = html_options.delete("method"), html_options[''href''] html_options["onclick"] = case when popup raise ActionView::ActionViewError, "You can''t use :popup" when method # or maybe (confirm and method) "#{method_javascript_function(method, url, href, confirm)}return false;" when confirm # and only confirm "#{confirm_javascript_function(confirm)}return false;" else html_options["onclick"] end end end alias_method_chain :convert_options_to_javascript!, :facebooker # Overrides a private method that link_to calls via convert_options_to_javascript! and # also, button_to calls directly. For Facebook, confirm can be an array of options to # stylize the Facebook dialog. This array goes in the format [title,prompt,style]. See # the Facebook page http://wiki.developers.facebook.com/index.php/FBJS for valid # style formats like "color: ''black'', background: ''white''" or like "''color'',''black''". # # == Examples = # # link_to("Facebooker", "http://rubyforge.org/projects/facebooker", :confirm=>"Make Awesome Choice?") # # link_to("rFacebook", "http://rubyforge.org/projects/rfacebook", :confirm=>["Terrible!", "Are you crazy?") # # link_to("Cool page", :action=>''cool_page'', :confirm=>["Please Confirm", "Are you sure?", "''width'',''300px''") def confirm_javascript_function_with_facebooker(confirm, fun = nil) if !request_is_for_a_facebook_canvas? confirm_javascript_function_without_facebooker(confirm) else if(confirm.is_a?(Array) and confirm.size > 3) raise ActionView::ActionViewError, "Too many options: You can only pass [title, prompt, style]" elsif(confirm.is_a?(Array) and confirm.size == 3) title,prompt,style = confirm[0],confirm[1],confirm[2] elsif(confirm.is_a?(Array) and confirm.size == 2) title,prompt,style = confirm[0],confirm[1], "''width'',''200px''" else title,prompt,style = ''Confirm Request'', confirm, "''width'',''200px''" end "var dlg = new Dialog().showChoice(''#{escape_javascript(title.to_s)}'',''#{escape_javascript(prompt.to_s)}'').setStyle(#{style});"+ "var a=this;dlg.onconfirm = function() { #{fun ? fun : ''document.setLocation(a.getHref());''} };" end end alias_method_chain :confirm_javascript_function, :facebooker # Dynamically creates a form for link_to with method. Calls confirm_javascript_function if and # only if (confirm && method) for link_to def method_javascript_function_with_facebooker(method, url = '''', href = nil, confirm = nil) if !request_is_for_a_facebook_canvas? method_javascript_function_without_facebooker(method,url,href) else action = (href && url.size > 0) ? "''#{url}''" : ''a.getHref()'' submit_function "var f = document.createElement(''form''); f.setStyle(''display'',''none''); " + "a.getParentNode().appendChild(f); f.setMethod(''POST''); f.setAction(#{action});" unless method == :post submit_function << "var m = document.createElement(''input''); m.setType(''hidden''); " submit_function << "m.setName(''_method''); m.setValue(''#{method}''); f.appendChild(m);" end if protect_against_forgery? submit_function << "var s = document.createElement(''input''); s.setType(''hidden''); " submit_function << "s.setName(''#{request_forgery_protection_token}''); s.setValue(''#{escape_javascript form_authenticity_token}''); f.appendChild(s);" end submit_function << "f.submit();" if(confirm) confirm_javascript_function(confirm, submit_function) else "var a=this;" + submit_function end end end alias_method_chain :method_javascript_function, :facebooker end end end -------------- next part -------------- diff --git a/init.rb b/init.rb index 8ad34b8..2db13ba 100644 --- a/init.rb +++ b/init.rb @@ -19,6 +19,8 @@ require ''facebooker/rails/facebook_asset_path'' require ''facebooker/rails/facebook_request_fix'' require ''facebooker/rails/routing'' require ''facebooker/rails/facebook_pretty_errors'' rescue nil +require ''facebooker/rails/facebook_url_helper'' + module ::ActionController class Base def self.inherited_with_facebooker(subclass) diff --git a/test/rails_integration_test.rb b/test/rails_integration_test.rb index 4e2babb..dcba67b 100644 --- a/test/rails_integration_test.rb +++ b/test/rails_integration_test.rb @@ -6,6 +6,7 @@ begin require ''facebooker/rails/controller'' require ''facebooker/rails/helpers'' require ''facebooker/rails/facebook_form_builder'' + require ''facebooker/rails/facebook_url_helper'' require File.dirname(__FILE__)+''/../init'' require ''mocha'' @@ -820,6 +821,197 @@ class RailsFacebookFormbuilderTest < Test::Unit::TestCase assert_equal "<fb:editor-custom label=\"Friends\"></fb:editor-custom>", at form_builder.multi_friend_input end end + + +class RailsUrlHelperExtensionsTest < Test::Unit::TestCase + class UrlHelperExtensionsClass + include ActionView::Helpers::UrlHelper + include ActionView::Helpers::TagHelper + def initialize(controller, canvas) + ENV[''FACEBOOKER_RELATIVE_URL_ROOT''] =''facebook_app_name'' + @controller = controller + @canvas = canvas + end + + def protect_against_forgery? + false + end + + def request_is_for_a_facebook_canvas? + @canvas + end + end + class UrlHelperExtensionsController < NoisyController + def index + render :nothing => true + end + def do_it + render :nothing => true + end + end + + # We need to simulate an outside request to the callback server + # where the ENV[''FACEBOOK_CANVAS_PATH] == ENV[''FACEBOOKER_RELATIVE_URL_ROOT''] + # is ***not*** prepended to the request uri + # ex. apps.facebook.com/facebook_app_name/controller/action (prepended) + # test.host/controller/action (***not*** prepended) + class FacebookRequest < ActionController::TestRequest + # Parse the canvas name off to simulate a real request. + def request_uri + super.gsub(/^\/#{ENV[''FACEBOOKER_RELATIVE_URL_ROOT'']}(.*)/, ''\1'' ) + end + end + + def setup + ENV[''FACEBOOK_CANVAS_PATH''] =''facebook_app_name'' + @controller = UrlHelperExtensionsController.new + @request = FacebookRequest.new + @response = ActionController::TestResponse.new + + @current_page = "http://test.host/rails_url_helper_extensions_test/url_helper_extensions/do_it?fb_sig_in_canvas=1" + @not_current_page = "http://some.host/control/action" + + @u = UrlHelperExtensionsClass.new(@controller, true) + @non_canvas_u = UrlHelperExtensionsClass.new(@controller, false) + @label = "Testing" + @url = "test.host" + @prompt = "Are you sure?" + @default_title = "Confirm Request" + @title = "Please Confirm" + @style = "''color: ''black'', background: ''white''" + @default_style = "''width'',''200px''" + end + + def test_link_to + assert_equal "<a href=\"#{@url}\">Testing</a>", @u.link_to(@label, @url) + end + + def test_link_to_with_popup + assert_raises(ActionView::ActionViewError) {@u.link_to(@label, at url, :popup=>true)} + end + + def test_link_to_with_confirm + assert_dom_equal( "<a href=\"#{@url}\" onclick=\"var dlg = new Dialog().showChoice(\''#{@default_title}\'',\''#{@prompt}\'').setStyle(#{@default_style});"+ + "var a=this;dlg.onconfirm = function() { " + + "document.setLocation(a.getHref()); };return false;\">#{@label}</a>", + @u.link_to(@label, @url, :confirm => @prompt) ) + end + def test_link_to_with_confirm_with_title + assert_dom_equal( "<a href=\"#{@url}\" onclick=\"var dlg = new Dialog().showChoice(\''#{@title}\'',\''#{@prompt}\'').setStyle(#{@default_style});"+ + "var a=this;dlg.onconfirm = function() { " + + "document.setLocation(a.getHref()); };return false;\">#{@label}</a>", + @u.link_to(@label, @url, :confirm => [@title, at prompt]) ) + end + def test_link_to_with_confirm_with_title_and_style + assert_dom_equal( "<a href=\"#{@url}\" onclick=\"var dlg = new Dialog().showChoice(\''#{@title}\'',\''#{@prompt}\'').setStyle(#{@style});"+ + "var a=this;dlg.onconfirm = function() { " + + "document.setLocation(a.getHref()); };return false;\">#{@label}</a>", + @u.link_to(@label, @url, :confirm => [@title, at prompt, at style]) ) + end + + def test_link_to_with_method + assert_dom_equal( "<a href=\"#{@url}\" onclick=\"var a=this;var f = document.createElement(''form''); f.setStyle(''display'',''none''); "+ + "a.getParentNode().appendChild(f); f.setMethod(''POST''); f.setAction(a.getHref());" + + "var m = document.createElement(''input''); m.setType(''hidden''); "+ + "m.setName(''_method''); m.setValue(''delete''); f.appendChild(m);"+ + "f.submit();return false;\">#{@label}</a>", @u.link_to(@label, at url, :method=>:delete)) + end + + def test_link_to_with_confirm_and_method + assert_dom_equal( "<a href=\"#{@url}\" onclick=\"var dlg = new Dialog().showChoice(\''#{@default_title}\'',\''#{@prompt}\'').setStyle(#{@default_style});"+ + "var a=this;dlg.onconfirm = function() { " + + "var f = document.createElement(''form''); f.setStyle(''display'',''none''); "+ + "a.getParentNode().appendChild(f); f.setMethod(''POST''); f.setAction(a.getHref());" + + "var m = document.createElement(''input''); m.setType(''hidden''); "+ + "m.setName(''_method''); m.setValue(''delete''); f.appendChild(m);"+ + "f.submit(); };return false;\">#{@label}</a>", @u.link_to(@label, at url, :confirm=>@prompt, :method=>:delete) ) + end + def test_link_to_with_confirm_and_method_for_non_canvas_page + assert_dom_equal( "<a href=\"#{@url}\" onclick=\"if (confirm(\''#{@prompt}\'')) { var f = document.createElement(''form''); f.style.display = ''none''; "+ + "this.parentNode.appendChild(f); f.method = ''POST''; f.action = this.href;var m = document.createElement(''input''); "+ + "m.setAttribute(''type'', ''hidden''); m.setAttribute(''name'', ''_method''); m.setAttribute(''value'', ''delete''); "+ + "f.appendChild(m);f.submit(); };return false;\">#{@label}</a>", + @non_canvas_u.link_to(@label, at url, :confirm=>@prompt, :method=>:delete) ) + end + + def test_button_to + assert_equal "<form method=\"post\" action=\"#{@url}\" class=\"button-to\"><div>" + + "<input type=\"submit\" value=\"#{@label}\" /></div></form>", @u.button_to(@label, at url) + end + + def test_button_to_with_confirm + assert_equal "<form method=\"post\" action=\"#{@url}\" class=\"button-to\"><div>" + + "<input onclick=\"var dlg = new Dialog().showChoice(\''#{@default_title}\'',\''#{@prompt}\'').setStyle(#{@default_style});"+ + "var a=this;dlg.onconfirm = function() { "+ + "a.getParentNode().getParentNode().submit(); };return false;\" type=\"submit\" value=\"#{@label}\" /></div></form>", + @u.button_to(@label, at url, :confirm=>@prompt) + end + + def test_button_to_with_confirm_for_non_canvas_page + assert_equal "<form method=\"post\" action=\"#{@url}\" class=\"button-to\"><div>"+ + "<input onclick=\"return confirm(\''#{@prompt}\'');\" type=\"submit\" value=\"#{@label}\" /></div></form>", + @non_canvas_u.button_to(@label, at url, :confirm=>@prompt) + end + + + def test_current_page_with_current_url_string + post :do_it, example_rails_params_including_fb + assert @u.current_page?(@current_page) + end + def test_current_page_with_non_current_url_string + post :do_it, example_rails_params_including_fb + assert !@u.current_page?(@not_current_page) + end + def test_current_page_with_current_url_hash + post :do_it, example_rails_params_including_fb + assert @u.current_page?(:action=>"do_it", :fb_sig_in_canvas=>"1") + end + def test_current_page_with_non_current_url_hash + post :do_it, example_rails_params_including_fb + assert !@u.current_page?(:action=>"not_action") + end + def test_current_page_with_current_url_hash_for_non_canvas_page + post :do_it + assert @non_canvas_u.current_page?(:action=>"do_it") + end + def test_current_page_with_non_current_url_hash_for_non_canvas_page + post :do_it + assert !@non_canvas_u.current_page?(:action=>"not_action") + end + + def test_link_to_unless_with_true + assert_equal @label, @u.link_to_unless(true, at label, at url) + end + def test_link_to_unless_with_false + assert_equal @u.link_to(@label, at url), @u.link_to_unless(false, at label, at url) + end + + def test_link_to_if_with_true + assert_equal @u.link_to(@label, at url), @u.link_to_if(true, at label, at url) + end + def test_link_to_if_with_false + assert_equal @label, @u.link_to_if(false, at label, at url) + end + + def test_link_to_unless_current_with_current + post :do_it, example_rails_params_including_fb + assert_equal @label, @u.link_to_unless_current(@label,{:action=>"do_it", :fb_sig_in_canvas=>"1"}) + end + def test_link_to_unless_current_with_not_current + post :do_it, example_rails_params_including_fb + assert_equal @u.link_to(@label,{:action=>"index",:fb_sig_in_canvas=>"1"}), + @u.link_to_unless_current(@label,{:action=>"index", :fb_sig_in_canvas=>"1"}) + end + + private + # Makes the canvas page be prepended for the current page tests + def example_rails_params_including_fb + {"fb_sig_in_canvas"=>"1"} + end +end + + + # rescue LoadError # $stderr.puts "Couldn''t find action controller. That''s OK. We''ll skip it." end