module Footnotes class Filter @@no_style = false @@multiple_notes = false @@klasses = [] @@lock_top_right = false @@font_size = '11px' # Default link prefix is textmate @@prefix = 'txmt://open?url=file://%s&line=%d&column=%d' # Edit notes @@notes = [ :controller, :view, :layout, :partials, :stylesheets, :javascripts ] # Show notes @@notes += [ :assigns, :session, :cookies, :params, :filters, :routes, :env, :queries, :log] # :no_style => If you don't want the style to be appended to your pages # :notes => Class variable that holds the notes to be processed # :prefix => Prefix appended to FootnotesLinks # :multiple_notes => Set to true if you want to open several notes at the same time # :lock_top_right => Lock a btn to toggle notes to the top right of the browser # :font_size => CSS font-size property cattr_accessor :no_style, :notes, :prefix, :multiple_notes, :lock_top_right, :font_size class << self include Footnotes::EachWithRescue # Calls the class method start! in each note # Sometimes notes need to set variables or clean the environment to work properly # This method allows this kind of setup # def start!(controller) self.each_with_rescue(Footnotes.before_hooks) {|hook| hook.call(controller, self)} @@klasses = [] self.each_with_rescue(@@notes.flatten) do |note| klass = "Footnotes::Notes::#{note.to_s.camelize}Note".constantize klass.start!(controller) if klass.respond_to?(:start!) @@klasses << klass end end # If none argument is sent, simply return the prefix. # Otherwise, replace the args in the prefix. # def prefix(*args) if args.empty? @@prefix else args.map! { |arg| URI.escape(arg.to_s) } if @@prefix.respond_to? :call @@prefix.call *args else format(@@prefix, *args) end end end end def initialize(controller) @controller = controller @template = controller.instance_variable_get(:@template) @notes = [] revert_pos(controller.response_body) do @body = controller.response.body end end def add_footnotes! add_footnotes_without_validation! if valid? rescue Exception => e # Discard footnotes if there are any problems self.class.log_error("Footnotes Exception", e) end # Calls the class method close! in each note # Sometimes notes need to finish their work even after being read # This method allows this kind of work # def close!(controller) self.each_with_rescue(@@klasses) {|klass| klass.close!(controller)} self.each_with_rescue(Footnotes.after_hooks) {|hook| hook.call(controller, self)} end protected def valid? @body.is_a?(String) && performed_render? && valid_format? && valid_content_type? && !component_request? && !xhr? && !footnotes_disabled? && !attached_file? end def add_footnotes_without_validation! initialize_notes! insert_styles unless @@no_style insert_footnotes end def initialize_notes! each_with_rescue(@@klasses) do |klass| note = klass.new(@controller) @notes << note if note.valid? end end def revert_pos(file) return yield unless file.respond_to?(:pos) && file.respond_to?(:pos=) original = file.pos yield file.pos = original end def performed_render? @controller.respond_to?(:performed?) && @controller.performed? end def valid_format? ['text/html', nil].include? @controller.response.content_type end def valid_content_type? c = @controller.response.headers['Content-Type'].to_s (c.empty? || c =~ /html/) end def component_request? @controller.instance_variable_get(:@parent_controller) end def xhr? @controller.request.xhr? end def footnotes_disabled? @controller.params[:footnotes] == "false" end def attached_file? !!(@controller.headers['Content-Disposition'] =~ /attachment/) end # # Insertion methods # def insert_styles #TODO More customizable(reset.css, from file etc.) if @@lock_top_right extra_styles = <<-STYLES #footnotes_debug {position: fixed; top: 0px; right: 0px; width: 100%; z-index: 10000; margin-top: 0;} #footnotes_debug #toggle_footnotes {position: absolute; right: 0; top: 0; background: #fff; border: 1px solid #ccc; color: #9b1b1b; font-size: 20px; text-align: center; padding: 8px; opacity: 0.9;} #footnotes_debug #toggle_footnotes:hover {opacity: 1;} #footnotes_debug #all_footnotes {display: none; padding: 15px; background: #fff; box-shadow: 0 0 5px rgba(0,0,0,0.4);} #footnotes_debug fieldset > div {max-height: 500px; overflow: scroll;} STYLES else extra_styles = <<-STYLES #footnotes_debug #toggle_footnotes {display: none;} STYLES end insert_text :before, /<\/head>/i, <<-HTML HTML end def insert_footnotes # Fieldsets method should be called first content = fieldsets element_style = '' if @@lock_top_right element_style = 'style="display: none;"' end footnotes_html = <<-HTML