mir_extensions 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/.bundle/config +2 -0
  2. data/.document +5 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +1 -0
  5. data/Gemfile +41 -0
  6. data/Gemfile.lock +116 -0
  7. data/LICENSE +20 -0
  8. data/README +256 -0
  9. data/README.rdoc +17 -0
  10. data/Rakefile +50 -0
  11. data/VERSION +1 -0
  12. data/app/controllers/application_controller.rb +9 -0
  13. data/app/helpers/application_helper.rb +2 -0
  14. data/app/models/primary.rb +11 -0
  15. data/app/models/secondary.rb +7 -0
  16. data/app/views/layouts/application.html.erb +14 -0
  17. data/autotest/discover.rb +2 -0
  18. data/config.ru +4 -0
  19. data/config/application.rb +47 -0
  20. data/config/boot.rb +13 -0
  21. data/config/database.yml +17 -0
  22. data/config/environment.rb +5 -0
  23. data/config/environments/development.rb +22 -0
  24. data/config/environments/production.rb +49 -0
  25. data/config/environments/test.rb +35 -0
  26. data/config/initializers/backtrace_silencers.rb +7 -0
  27. data/config/initializers/inflections.rb +10 -0
  28. data/config/initializers/mime_types.rb +5 -0
  29. data/config/initializers/secret_token.rb +7 -0
  30. data/config/initializers/session_store.rb +8 -0
  31. data/config/locales/en.yml +5 -0
  32. data/config/routes.rb +58 -0
  33. data/db/development.sqlite3 +1 -0
  34. data/db/mir_ext_development.sqlite3 +0 -0
  35. data/db/mir_ext_test.sqlite3 +0 -0
  36. data/db/schema.rb +26 -0
  37. data/db/seeds.rb +7 -0
  38. data/db/test.sqlite3 +0 -0
  39. data/doc/README_FOR_APP +2 -0
  40. data/lib/core_ext/controller_extensions.rb +37 -0
  41. data/lib/core_ext/core_ext.rb +344 -0
  42. data/lib/core_ext/helper_extensions.rb +383 -0
  43. data/lib/mir_extensions.rb +37 -0
  44. data/lib/tasks/.gitkeep +0 -0
  45. data/log/development.log +151 -0
  46. data/log/production.log +0 -0
  47. data/log/server.log +0 -0
  48. data/log/test.log +27 -0
  49. data/mir_extensions.gemspec +119 -0
  50. data/public/404.html +26 -0
  51. data/public/422.html +26 -0
  52. data/public/500.html +26 -0
  53. data/public/favicon.ico +0 -0
  54. data/public/images/rails.png +0 -0
  55. data/public/index.html +262 -0
  56. data/public/javascripts/application.js +2 -0
  57. data/public/javascripts/controls.js +965 -0
  58. data/public/javascripts/dragdrop.js +974 -0
  59. data/public/javascripts/effects.js +1123 -0
  60. data/public/javascripts/prototype.js +6001 -0
  61. data/public/javascripts/rails.js +175 -0
  62. data/public/robots.txt +5 -0
  63. data/public/stylesheets/.gitkeep +0 -0
  64. data/script/rails +6 -0
  65. data/spec/controllers/application_controller_spec.rb +41 -0
  66. data/spec/helpers/application_helper_spec.rb +40 -0
  67. data/spec/mir_extensions_spec.rb +269 -0
  68. data/spec/spec_helper.rb +27 -0
  69. data/vendor/plugins/.gitkeep +0 -0
  70. metadata +170 -0
@@ -0,0 +1 @@
1
+ S
Binary file
@@ -0,0 +1,26 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your
6
+ # database schema. If you need to create the application database on another
7
+ # system, you should be using db:schema:load, not running all the migrations
8
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
10
+ #
11
+ # It's strongly recommended to check this file into your version control system.
12
+
13
+ ActiveRecord::Schema.define(:version => 0) do
14
+
15
+ create_table "primaries", :force => true do |t|
16
+ t.string "name"
17
+ end
18
+
19
+ create_table "secondaries", :force => true do |t|
20
+ t.string "name"
21
+ t.integer "primary_id"
22
+ end
23
+
24
+ add_index "secondaries", ["primary_id"], :name => "index_roles_on_name"
25
+
26
+ end
@@ -0,0 +1,7 @@
1
+ # This file should contain all the record creation needed to seed the database with its default values.
2
+ # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3
+ #
4
+ # Examples:
5
+ #
6
+ # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
7
+ # Mayor.create(:name => 'Daley', :city => cities.first)
Binary file
@@ -0,0 +1,2 @@
1
+ Use this README file to introduce your application and point to useful places in the API for learning more.
2
+ Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
@@ -0,0 +1,37 @@
1
+ class ActionController::Base
2
+ require 'mir_extensions'
3
+ require 'socket'
4
+
5
+ def self.local_ip
6
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
7
+
8
+ UDPSocket.open do |s|
9
+ s.connect '64.233.187.99', 1
10
+ s.addr.last
11
+ end
12
+ ensure
13
+ Socket.do_not_reverse_lookup = orig
14
+ end
15
+
16
+ # Returns a sanitized column parameter suitable for SQL order-by clauses.
17
+ def sanitize_by_param(allowed=[], default='id')
18
+ sanitize_params params && params[:by], allowed, default
19
+ end
20
+
21
+ # Returns a sanitized direction parameter suitable for SQL order-by clauses.
22
+ def sanitize_dir_param
23
+ sanitize_params params && params[:dir], ['ASC', 'DESC'], 'ASC'
24
+ end
25
+
26
+ # Use this method to prevent SQL injection vulnerabilities by verifying that a user-provided
27
+ # parameter is on a whitelist of allowed values.
28
+ #
29
+ # Accepts a value, a list of allowed values, and a default value.
30
+ # Returns the value if allowed, otherwise the default.
31
+ def sanitize_params(supplied='', allowed=[], default=nil)
32
+ raise ArgumentError, "A default value is required." unless default
33
+ return default if supplied.blank? || allowed.blank? || ! allowed.include?(supplied)
34
+ return supplied
35
+ end
36
+ end
37
+
@@ -0,0 +1,344 @@
1
+ require 'rubygems'
2
+ require 'singleton'
3
+ require File.expand_path(File.dirname(__FILE__) + '/controller_extensions')
4
+ require File.expand_path(File.dirname(__FILE__) + '/helper_extensions')
5
+ require 'soap/header/simplehandler'
6
+
7
+ module CoreExt
8
+
9
+ # String Extensions ==============================================================================
10
+ String.class_eval do
11
+
12
+ include ActionView::Helpers::NumberHelper
13
+
14
+ # Usage: "3125552312".to_phone
15
+ # Returns: 312-555-2313
16
+ #
17
+ # Usage: "3125552313".to_phone(:area_code => true)
18
+ # Returns : (312) 555-2313
19
+ #
20
+ def to_phone(options = {})
21
+ number_to_phone(self.to_i, options)
22
+ end
23
+
24
+ end
25
+ end
26
+
27
+ class String
28
+
29
+ # Bring in support for view helpers
30
+ # include MirExtensions::CoreExt::String::NumberHelper
31
+
32
+ # General methods
33
+
34
+ def capitalize_words
35
+ self.downcase.gsub(/\b([a-z])/) { $1.capitalize }.gsub( "'S", "'s" )
36
+ end
37
+
38
+ # Address methods
39
+
40
+ def expand_address_abbreviations
41
+ _address = self.strip.capitalize_words
42
+
43
+ # NOTE: DO NOT rearrange the replace sequences; order matters!
44
+
45
+ # streets
46
+ _address.gsub!( /\b(ave|av)\.?\b/i, 'Avenue ' )
47
+ _address.gsub!( /\b(blvd|blv|bld|bl)\.?\b/i, 'Boulevard ' )
48
+ _address.gsub!( /\bcr\.?\b/i, 'Circle ' )
49
+ _address.gsub!( /\bctr\.?\b/i, 'Center ' )
50
+ _address.gsub!( /\b(crt|ct)\.?\b/i, 'Court ' )
51
+ _address.gsub!( /\bdr\.?\b/i, 'Drive ' )
52
+ _address.gsub!( /\b(expressw|expw|expy)\.?\b/i, 'Expressway ' )
53
+ _address.gsub!( /\bfrwy\.?\b/i, 'Freeway ' )
54
+ _address.gsub!( /\bhwy\.?\b/i, 'Highway ' )
55
+ _address.gsub!( /\bln\.?\b/i, 'Lane ' )
56
+ _address.gsub!( /\b(prkwy|pkwy|pkw|pky)\.?\b/i, 'Parkway ' )
57
+ _address.gsub!( /\bpk\.?\b/i, 'Pike ' )
58
+ _address.gsub!( /\bplz\.?\b/i, 'Plaza ' )
59
+ _address.gsub!( /\bpl\.?\b/i, 'Place ' )
60
+ _address.gsub!( /\brd\.?\b/i, 'Road ' )
61
+ _address.gsub!( /\b(rte|rt)\.?\b/i, 'Route ' )
62
+ _address.gsub!( /\bste\.?\b/i, 'Suite ' )
63
+ _address.gsub!( /\bst\.?\b/i, 'Street ' )
64
+ _address.gsub!( /\btrpk\.?\b/i, 'Turnpike ' )
65
+ _address.gsub!( /\btr\.?\b/i, 'Trail ' )
66
+
67
+ # directions
68
+ _address.gsub!( /\bN\.?e\.?\b/i, 'Northeast ' )
69
+ _address.gsub!( /\bS\.?e\.?\b/i, 'Southeast ' )
70
+ _address.gsub!( /\bS\.?w\.?\b/i, 'Southwest ' )
71
+ _address.gsub!( /\bN\.?w\.?\b/i, 'Northwest ' )
72
+ _address.gsub!( /\bN\.?\b/, 'North ' )
73
+ _address.gsub!( /\bE\.?\b/, 'East ' )
74
+ _address.gsub!( /\bS\.?\b/, 'South ' )
75
+ _address.gsub!( /\bW\.?\b/, 'West ' )
76
+ _address.gsub!( '.', '' )
77
+ _address.gsub!( / +/, ' ' )
78
+ _address.strip
79
+ end
80
+
81
+ def formatted_phone
82
+ if self
83
+ # remove non-digit characters
84
+ _self = self.gsub(/[\(\) -]+/, '')
85
+ # format as phone if 10 digits are left
86
+ return number_to_phone(_self, :area_code => true ) if !! (_self =~ /[0-9]{10}/)
87
+ end
88
+
89
+ self
90
+ end
91
+
92
+ def formatted_zip
93
+ return if self.blank?
94
+ self.gsub!( /[\(\) -]+/, '' )
95
+ self.size == 9 ? "#{self[0 .. 4]}-#{self[5 .. -1]}" : self
96
+ end
97
+
98
+ # Time methods
99
+
100
+ def to_12_hour_time
101
+ (self == '0' || self.blank?) ? nil : Time.parse( "#{self[0..-3]}:#{self[-2..-1]}" ).to_s( :time ).gsub(/^0/, '')
102
+ end
103
+
104
+ # URL methods
105
+
106
+ # Prefixes the given url with 'http://'.
107
+ def add_http_prefix
108
+ return if self.blank?
109
+ _uri = self.to_uri
110
+ return self if _uri.nil? || _uri.is_a?(URI::FTP) || _uri.is_a?(URI::HTTP) || _uri.is_a?(URI::HTTPS) || _uri.is_a?(URI::LDAP) || _uri.is_a?(URI::MailTo)
111
+ "http://#{self}"
112
+ end
113
+
114
+ # Returns true if a given string begins with http:// or https://.
115
+ def has_http?
116
+ !! (self =~ /^http[s]?:\/\/.+/)
117
+ end
118
+
119
+ # Returns true if a given string has a trailing slash.
120
+ def has_trailing_slash?
121
+ !! (self =~ /\/$/)
122
+ end
123
+
124
+ # Returns true if a given string refers to an HTML page.
125
+ def is_page?
126
+ !! (self =~ /\.htm[l]?$/)
127
+ end
128
+
129
+ # Returns the host from a given URL string; returns nil if the string is not a valid URL.
130
+ def to_host
131
+ _uri = self.to_uri
132
+ _uri ? _uri.host : nil
133
+ end
134
+
135
+ # Returns a URI for the given string; nil if the string is invalid.
136
+ def to_uri
137
+ begin
138
+ _uri = URI.parse self
139
+ rescue URI::InvalidURIError
140
+ _uri = nil
141
+ end
142
+
143
+ _uri
144
+ end
145
+
146
+ # Returns true if the given string is a valid URL.
147
+ def valid_http_url?
148
+ self.scan(/:\/\//).size == 1 && self.to_uri.is_a?(URI::HTTP)
149
+ end
150
+
151
+ end
152
+
153
+ # Array Extensions ===============================================================================
154
+ class Array
155
+ def mean
156
+ self.inject(0){ |sum, x| sum += x } / self.size.to_f
157
+ end
158
+
159
+ def count
160
+ self.size
161
+ end
162
+
163
+ end
164
+
165
+ # Enumerable Extensions ==========================================================================
166
+ module Enumerable
167
+ def to_histogram
168
+ inject(Hash.new(0)) { |h,x| h[x] += 1; h }
169
+ end
170
+ end
171
+
172
+ # Fixnum Extensions ==============================================================================
173
+ class Fixnum
174
+
175
+ # Given a number of seconds, convert into a string like HH:MM:SS
176
+ def to_hrs_mins_secs
177
+ _now = DateTime.now
178
+ _d = Date::day_fraction_to_time((_now + self.seconds) - _now)
179
+ "#{sprintf('%02d',_d[0])}:#{sprintf('%02d',_d[1])}:#{sprintf('%02d',_d[2])}"
180
+ end
181
+ end
182
+
183
+ # Float Extensions ===============================================================================
184
+ class Float
185
+ def to_nearest_tenth
186
+ sprintf("%.1f", self).to_f
187
+ end
188
+ end
189
+
190
+ # Hash Extensions ================================================================================
191
+ class Hash
192
+ def to_params
193
+ params = ''
194
+ stack = []
195
+
196
+ each do |k, v|
197
+ if v.is_a?(Hash)
198
+ stack << [k,v]
199
+ elsif v.is_a?(Array)
200
+ stack << [k,Hash.from_array(v)]
201
+ else
202
+ params << "#{k}=#{v}&"
203
+ end
204
+ end
205
+
206
+ stack.each do |parent, hash|
207
+ hash.each do |k, v|
208
+ if v.is_a?(Hash)
209
+ stack << ["#{parent}[#{k}]", v]
210
+ else
211
+ params << "#{parent}[#{k}]=#{v}&"
212
+ end
213
+ end
214
+ end
215
+
216
+ params.chop!
217
+ params
218
+ end
219
+
220
+ def to_sql( operator = 'AND' )
221
+ _sql = self.keys.map do |_key|
222
+ _value = self[_key].is_a?(Fixnum) ? self[_key] : "'#{self[_key]}'"
223
+ self[_key].nil? ? '1 = 1' : "#{_key} = #{_value}"
224
+ end
225
+ _sql * " #{operator} "
226
+ end
227
+
228
+ def self.from_array(array = [])
229
+ h = Hash.new
230
+ array.size.times{ |t| h[t] = array[t] }
231
+ h
232
+ end
233
+
234
+ end
235
+
236
+ # SOAP Extensions ================================================================================
237
+ class Header < SOAP::Header::SimpleHandler
238
+ def initialize(tag, value)
239
+ super(XSD::QName.new(nil, tag))
240
+ @tag = tag
241
+ @value = value
242
+ end
243
+
244
+ def on_simple_outbound
245
+ @value
246
+ end
247
+ end
248
+
249
+ class ActiveRecord::Base
250
+
251
+ #FIXME Extending AR in this way will stop working under Rails 2.3.2 for some reason.
252
+
253
+ # scope :order_by, lambda{ |col, dir| {:order => (col.blank?) ? ( (dir.blank?) ? 'id' : dir ) : "#{col} #{dir}"} }
254
+ # scope :limit, lambda { |num| { :limit => num } }
255
+
256
+ # TODO: call the column_names class method on the subclass
257
+ # named_scope :sort_by, lambda{ |col, dir| {:order => (col.blank?) ? ( (dir.blank?) ? (Client.column_names.include?('name') ? 'name' : 'id') : h(dir) ) : "#{h(col)} #{h(dir)}"} }
258
+
259
+ # Returns an array of SQL conditions suitable for use with ActiveRecord's finder.
260
+ # valid_criteria is an array of valid search fields.
261
+ # pairs is a hash of field names and values.
262
+ def self.search_conditions( valid_search_criteria, pairs, operator = 'OR' )
263
+ if valid_search_criteria.detect{ |_c| ! pairs[_c].blank? } || ! pairs[:query].blank?
264
+ _conditions = []
265
+ _or_clause = ''
266
+ _or_clause_values = []
267
+ _int_terms = {}
268
+ _text_terms = {}
269
+
270
+ # build or clause for keyword search
271
+ unless pairs[:query].blank? || ! self.respond_to?(:flattened_content)
272
+ pairs[:query].split(' ').each do |keyword|
273
+ _or_clause += 'flattened_content LIKE ? OR '
274
+ _or_clause_values << "%#{keyword}%"
275
+ end
276
+
277
+ _or_clause.gsub!( / OR $/, '')
278
+ end
279
+
280
+ # iterate across each valid search field
281
+ valid_search_criteria.each do |_field|
282
+ # build or clause for keyword search
283
+ unless pairs[:query].blank? || self.respond_to?(:flattened_content)
284
+ pairs[:query].split(' ').each do |keyword|
285
+ _or_clause += "#{_field} LIKE ? OR "
286
+ _or_clause_values << "%#{keyword}%"
287
+ end
288
+ end
289
+
290
+ # build hashes of integer and/or text search fields and values for each non-blank param
291
+ if ! pairs[_field].blank?
292
+ _field.to_s =~ /^id$|_id$|\?$/ ? _int_terms[_field.to_s.gsub('?', '')] = pairs[_field] : _text_terms[_field] = pairs[_field]
293
+ end
294
+ end
295
+
296
+ _or_clause.gsub!( / OR $/, '')
297
+
298
+ # convert the hash to parametric SQL
299
+ if _or_clause.blank?
300
+ _conditions = sql_conditions_for( _int_terms, _text_terms, nil, operator )
301
+ elsif _int_terms.keys.empty? && _text_terms.keys.empty?
302
+ _conditions = [ _or_clause ]
303
+ else
304
+ _conditions = sql_conditions_for( _int_terms, _text_terms, _or_clause, operator )
305
+ end
306
+
307
+ # add integer values
308
+ _int_terms.keys.each{ |key| _conditions << _int_terms[key] }
309
+ # add wildcard-padded values
310
+ _text_terms.keys.each{ |key| _conditions << "%#{_text_terms[key]}%" }
311
+
312
+ unless _or_clause_values.empty?
313
+ # add keywords
314
+ _conditions += _or_clause_values
315
+ end
316
+
317
+ return _conditions
318
+ else
319
+ return nil
320
+ end
321
+ end
322
+
323
+ def self.to_option_values
324
+ self.all.map{ |_x| [_x.name, _x.id] }
325
+ end
326
+
327
+ # Strips the specified attribute's value.
328
+ def strip(attribute)
329
+ value = self[attribute]
330
+ self.send("#{attribute}=", value && value.strip)
331
+ end
332
+
333
+ private
334
+
335
+ def self.sql_conditions_for( integer_fields, text_fields, or_clause = nil, operator = 'OR' )
336
+ if integer_fields.empty? && ! text_fields.empty?
337
+ [ text_fields.keys.map{ |k| k } * " LIKE ? #{operator} " + ' LIKE ?' + (or_clause ? " #{operator} #{or_clause}" : '') ]
338
+ elsif ! integer_fields.empty? && text_fields.empty?
339
+ [ integer_fields.keys.map{ |k| k } * " = ? #{operator} " + ' = ?' + (or_clause ? " #{operator} #{or_clause}" : '') ]
340
+ else
341
+ [ integer_fields.keys.map{ |k| k } * " = ? #{operator} " + " = ? #{operator} " + text_fields.keys.map{ |k| k } * " LIKE ? #{operator} " + ' LIKE ?' + (or_clause ? " #{operator} #{or_clause}" : '') ]
342
+ end
343
+ end
344
+ end
@@ -0,0 +1,383 @@
1
+ module ActionController
2
+ module Helpers
3
+
4
+ def action?( expression )
5
+ !! ( expression.class == Regexp ? controller.action_name =~ expression : controller.action_name == expression )
6
+ end
7
+
8
+ # Formats an array with HTML line breaks, or the specified delimiter.
9
+ def array_to_lines(array, delimiter = '<br />')
10
+ array.blank? ? nil : array * delimiter
11
+ end
12
+
13
+ def checkmark
14
+ %{<div class="checkmark"></div>}
15
+ end
16
+
17
+ def controller?( expression )
18
+ !! ( expression.class == Regexp ? controller.controller_name =~ expression : controller.controller_name == expression )
19
+ end
20
+
21
+ # Display CRUD icons or links, according to setting in use_crud_icons method.
22
+ #
23
+ # In application_helper.rb:
24
+ #
25
+ # def use_crud_icons
26
+ # true
27
+ # end
28
+ #
29
+ # Then use in index views like this:
30
+ #
31
+ # <td class="crud_links"><%= crud_links(my_model, 'my_model', [:show, :edit, :delete]) -%></td>
32
+ #
33
+ def crud_links(model, instance_name, actions, args={})
34
+ _html = ""
35
+ _options = args.keys.empty? ? '' : ", #{args.map{|k,v| ":#{k} => #{v}"}}"
36
+
37
+ if use_crud_icons
38
+ if actions.include?(:show)
39
+ _html << eval("link_to image_tag('/images/icons/view.png', :class => 'crud_icon'), model, :title => 'View'#{_options}")
40
+ end
41
+ if actions.include?(:edit)
42
+ _html << eval("link_to image_tag('/images/icons/edit.png', :class => 'crud_icon'), edit_#{instance_name}_path(model), :title => 'Edit'#{_options}")
43
+ end
44
+ if actions.include?(:delete)
45
+ _html << eval("link_to image_tag('/images/icons/delete.png', :class => 'crud_icon'), model, :confirm => 'Are you sure? This action cannot be undone.', :method => :delete, :title => 'Delete'#{_options}")
46
+ end
47
+ else
48
+ if actions.include?(:show)
49
+ _html << eval("link_to 'View', model, :title => 'View', :class => 'crud_link'#{_options}")
50
+ end
51
+ if actions.include?(:edit)
52
+ _html << eval("link_to 'Edit', edit_#{instance_name}_path(model), :title => 'Edit', :class => 'crud_link'#{_options}")
53
+ end
54
+ if actions.include?(:delete)
55
+ _html << eval("link_to 'Delete', model, :confirm => 'Are you sure? This action cannot be undone.', :method => :delete, :title => 'Delete', :class => 'crud_link'#{_options}")
56
+ end
57
+ end
58
+ _html
59
+ end
60
+
61
+ # Display CRUD icons or links, according to setting in use_crud_icons method.
62
+ # This method works with nested resources.
63
+ # Use in index views like this:
64
+ #
65
+ # <td class="crud_links"><%= crud_links_for_nested_resource(@my_model, my_nested_model, 'my_model', 'my_nested_model', [:show, :edit, :delete]) -%></td>
66
+ #
67
+ def crud_links_for_nested_resource(model, nested_model, model_instance_name, nested_model_instance_name, actions, args={})
68
+ _html = ""
69
+ if use_crud_icons
70
+ if actions.include?(:show)
71
+ _html << eval("link_to image_tag('/images/icons/view.png', :class => 'crud_icon'), #{model_instance_name}_#{nested_model_instance_name}_path(model, nested_model), :title => 'View'")
72
+ end
73
+
74
+ if actions.include?(:edit)
75
+ _html << eval("link_to image_tag('/images/icons/edit.png', :class => 'crud_icon'), edit_#{model_instance_name}_#{nested_model_instance_name}_path(model, nested_model), :title => 'Edit'")
76
+ end
77
+
78
+ if actions.include?(:delete)
79
+ _html << eval("link_to image_tag('/images/icons/delete.png', :class => 'crud_icon'), #{model_instance_name}_#{nested_model_instance_name}_path(model, nested_model), :method => :delete, :confirm => 'Are you sure? This action cannot be undone.', :title => 'Delete'")
80
+ end
81
+ end
82
+ _html
83
+ end
84
+
85
+ # DRY way to return a legend tag that renders correctly in all browsers. This variation allows
86
+ # for more "stuff" inside the legend tag, e.g. expand/collapse controls, without having to worry
87
+ # about escape sequences.
88
+ #
89
+ # Sample usage:
90
+ #
91
+ # <%- legend_block do -%>
92
+ # <span id="hide_or_show_backlinks" class="show_link" style="background-color: #999999;
93
+ # border: 1px solid #999999;" onclick="javascript:hide_or_show('backlinks');"></span>Backlinks (<%=
94
+ # @google_results.size -%>)
95
+ # <%- end -%>
96
+ #
97
+ def legend_block(&block)
98
+ concat content_tag(:div, capture(&block), :class => "faux_legend")
99
+ end
100
+
101
+ # DRY way to return a legend tag that renders correctly in all browsers
102
+ def legend_tag(text, args={})
103
+ _html = %{<div id="#{args[:id]}" class="faux_legend">#{text}</div>\r}
104
+ _html.gsub!(/ id=""/,'')
105
+ _html.gsub!(/ class=""/,'')
106
+ _html
107
+ end
108
+
109
+ def meta_description(content=nil)
110
+ content_for(:meta_description) { content } unless content.blank?
111
+ end
112
+
113
+ def meta_keywords(content=nil)
114
+ content_for(:meta_keywords) { content } unless content.blank?
115
+ end
116
+
117
+ def models_for_select( models, label = 'name' )
118
+ models.map{ |m| [m[label], m.id] }.sort_by{ |e| e[0] }
119
+ end
120
+
121
+ def options_for_array( a, selected = nil, prompt = select_prompt )
122
+ "<option value=''>#{prompt}</option>" + a.map{ |_e| _flag = _e[0].to_s == selected ? 'selected="1"' : ''; _e.is_a?(Array) ? "<option value=\"#{_e[0]}\" #{_flag}>#{_e[1]}</option>" : "<option>#{_e}</option>" }.to_s
123
+ end
124
+
125
+ # Create a link that is opaque to search engine spiders.
126
+ def obfuscated_link_to(path, image, label, args={})
127
+ _html = %{<form action="#{path}" method="get" class="obfuscated_link">}
128
+ _html << %{ <fieldset><input alt="#{label}" src="#{image}" type="image" /></fieldset>}
129
+ args.each{ |k,v| _html << %{ <div><input id="#{k.to_s}" name="#{k}" type="hidden" value="#{v}" /></div>} }
130
+ _html << %{</form>}
131
+ _html
132
+ end
133
+
134
+ # Wraps the given HTML in Rails' default style to highlight validation errors, if any.
135
+ def required_field_helper( model, element, html )
136
+ if model && ! model.errors.empty? && element.is_required
137
+ return content_tag( :div, html, :class => 'fieldWithErrors' )
138
+ else
139
+ return html
140
+ end
141
+ end
142
+
143
+ def select_prompt
144
+ "Select..."
145
+ end
146
+
147
+ def select_prompt_option
148
+ "<option value=''>#{select_prompt}</option>"
149
+ end
150
+
151
+ # Use on index pages to create dropdown list of filtering criteria.
152
+ # Populate the filter list using a constant in the model corresponding to named scopes.
153
+ #
154
+ # Usage:
155
+ #
156
+ # - item.rb:
157
+ #
158
+ # named_scope :active, :conditions => { :is_active => true }
159
+ # named_scope :inactive, :conditions => { :is_active => false }
160
+ #
161
+ # FILTERS = [
162
+ # {:scope => "all", :label => "All"},
163
+ # {:scope => "active", :label => "Active Only"},
164
+ # {:scope => "inactive", :label => "Inactive Only"}
165
+ # ]
166
+ #
167
+ # - items/index.html.erb:
168
+ #
169
+ # <%= select_tag_for_filter("items", @filters, params) -%>
170
+ #
171
+ # - items_controller.rb:
172
+ #
173
+ # def index
174
+ # @filters = Item::FILTERS
175
+ # if params[:show] && params[:show] != "all" && @filters.collect{|f| f[:scope]}.include?(params[:show])
176
+ # @items = eval("@items.#{params[:show]}.order_by(params[:by], params[:dir])")
177
+ # else
178
+ # @items = @items.order_by(params[:by], params[:dir])
179
+ # end
180
+ # ...
181
+ # end
182
+ #
183
+ def select_tag_for_filter(model, nvpairs, params)
184
+ return unless model && nvpairs && ! nvpairs.empty?
185
+ options = { :query => params[:query] }
186
+ _url = url_for(eval("#{model}_url(options)"))
187
+ _html = %{<label for="show">Show:</label><br />}
188
+ _html << %{<select name="show" id="show" onchange="window.location='#{_url}' + '?show=' + this.value">}
189
+ nvpairs.each do |pair|
190
+ _html << %{<option value="#{pair[:scope]}"}
191
+ if params[:show] == pair[:scope] || ((params[:show].nil? || params[:show].empty?) && pair[:scope] == "all")
192
+ _html << %{ selected="selected"}
193
+ end
194
+ _html << %{>#{pair[:label]}}
195
+ _html << %{</option>}
196
+ end
197
+ _html << %{</select>}
198
+ end
199
+
200
+ # Returns a link_to tag with sorting parameters that can be used with ActiveRecord.order_by.
201
+ #
202
+ # To use standard resources, specify the resources as a plural symbol:
203
+ # sort_link(:users, 'email', params)
204
+ #
205
+ # To use resources aliased with :as (in routes.rb), specify the aliased route as a string.
206
+ # sort_link('users_admin', 'email', params)
207
+ #
208
+ # You can override the link's label by adding a labels hash to your params in the controller:
209
+ # params[:labels] = {'user_id' => 'User'}
210
+ def sort_link(model, field, params, html_options={})
211
+ if (field.to_sym == params[:by] || field == params[:by]) && params[:dir] == "ASC"
212
+ classname = "arrow-asc"
213
+ dir = "DESC"
214
+ elsif (field.to_sym == params[:by] || field == params[:by])
215
+ classname = "arrow-desc"
216
+ dir = "ASC"
217
+ else
218
+ dir = "ASC"
219
+ end
220
+
221
+ options = {
222
+ :anchor => html_options[:anchor] || nil,
223
+ :by => field,
224
+ :dir => dir,
225
+ :query => params[:query],
226
+ :show => params[:show]
227
+ }
228
+
229
+ options[:show] = params[:show] unless params[:show].blank? || params[:show] == 'all'
230
+
231
+ html_options = {
232
+ :class => "#{classname} #{html_options[:class]}",
233
+ :style => "color: white; font-weight: #{params[:by] == field ? "bold" : "normal"}; #{html_options[:style]}",
234
+ :title => "Sort by this field"
235
+ }
236
+
237
+ field_name = params[:labels] && params[:labels][field] ? params[:labels][field] : field.titleize
238
+
239
+ _link = model.is_a?(Symbol) ? eval("#{model}_url(options)") : "/#{model}?#{options.to_params}"
240
+ link_to(field_name, _link, html_options)
241
+ end
242
+
243
+ # Tabbed interface helpers =======================================================================
244
+
245
+ # Returns formatted tabs with appropriate JS for activation. Use in conjunction with tab_body.
246
+ #
247
+ # Usage:
248
+ #
249
+ # <%- tabset do -%>
250
+ # <%= tab_tag :id => 'ppc_ads', :label => 'PPC Ads', :state => 'active' %>
251
+ # <%= tab_tag :id => 'budget' %>
252
+ # <%= tab_tag :id => 'geotargeting' %>
253
+ # <%- end -%>
254
+ #
255
+ def tabset(&proc)
256
+ concat %{
257
+ <div class="jump_links">
258
+ <ul>
259
+ }
260
+ yield
261
+ concat %{
262
+ </ul>
263
+ </div>
264
+ <br style="clear: both;" /><br />
265
+ <input type="hidden" id="show_tab" />
266
+ <script type="text/javascript">
267
+ function hide_all_tabs() { $$('.tab_block').invoke('hide'); }
268
+ function activate_tab(tab) {
269
+ $$('.tab_control').each(function(elem){ elem.className = 'tab_control'});
270
+ $('show_' + tab).className = 'tab_control active';
271
+ hide_all_tabs();
272
+ $(tab).toggle();
273
+ $('show_tab').value = tab
274
+ }
275
+ function sticky_tab() { if (location.hash) { activate_tab(location.hash.gsub('#','')); } }
276
+ Event.observe(window, 'load', function() { sticky_tab(); });
277
+ </script>
278
+ }
279
+ end
280
+
281
+ # Returns a tab body corresponding to tabs in a tabset. Make sure that the id of the tab_body
282
+ # matches the id provided to the tab_tag in the tabset block.
283
+ #
284
+ # Usage:
285
+ #
286
+ # <%- tab_body :id => 'ppc_ads', :label => 'PPC Ad Details' do -%>
287
+ # PPC ads form here.
288
+ # <%- end -%>
289
+ #
290
+ # <%- tab_body :id => 'budget' do -%>
291
+ # Budget form here.
292
+ # <%- end -%>
293
+ #
294
+ # <%- tab_body :id => 'geotargeting' do -%>
295
+ # Geotargeting form here.
296
+ # <%- end -%>
297
+ #
298
+ def tab_body(args, &proc)
299
+ concat %{<div id="#{args[:id]}" class="tab_block form_container" style="display: #{args[:display] || 'none'};">}
300
+ concat %{#{legend_tag args[:label] || args[:id].titleize }}
301
+ concat %{<a name="#{args[:id]}"></a><br />}
302
+ yield
303
+ concat %{</div>}
304
+ end
305
+
306
+ # Returns the necessary HTML for a particular tab. Use inside a tabset block.
307
+ # Override the default tab label by specifying a :label parameter.
308
+ # Indicate that the tab should be active by setting its :state to 'active'.
309
+ # (NOTE: You must define a corresponding CSS style for active tabs.)
310
+ #
311
+ # Usage:
312
+ #
313
+ # <%= tab_tag :id => 'ppc_ads', :label => 'PPC Ads', :state => 'active' %>
314
+ #
315
+ def tab_tag(args, *css_class)
316
+ %{<li id="show_#{args[:id]}" class="tab_control #{args[:state]}" onclick="window.location='##{args[:id]}'; activate_tab('#{args[:id]}');">#{args[:label] || args[:id].to_s.titleize}</li>}
317
+ end
318
+
319
+ # ================================================================================================
320
+
321
+ def tag_for_collapsible_row(obj, params)
322
+ _html = ""
323
+ if obj && obj.respond_to?(:parent) && obj.parent
324
+ _html << %{<tr class="#{obj.class.name.downcase}_#{obj.parent.id} #{params[:class]}" style="display: none; #{params[:style]}">}
325
+ else
326
+ _html << %{<tr class="#{params[:class]}" style="#{params[:style]}">}
327
+ end
328
+ _html
329
+ end
330
+
331
+ def tag_for_collapsible_row_control(obj)
332
+ _base_id = "#{obj.class.name.downcase}_#{obj.id}"
333
+ _html = %{<div id="hide_or_show_#{_base_id}" class="show_link" style="background-color: #999999; border: 1px solid #999999;" onclick="javascript:hide_or_show('#{_base_id}');"></div>}
334
+ end
335
+
336
+ # Create a set of tags for displaying a field label with inline help.
337
+ # Field label text is appended with a ? icon, which responds to a click
338
+ # by showing or hiding the provided help text.
339
+ #
340
+ # Sample usage:
341
+ #
342
+ # <%= tag_for_label_with_inline_help 'Relative Frequency', 'rel_frequency', 'Relative frequency of search traffic for this keyword across multiple search engines, as measured by WordTracker.' %>
343
+ #
344
+ # Yields:
345
+ #
346
+ # <label for="rel_frequency">Relative Frequency: <%= image_tag "/images/help_icon.png", :onclick => "$('rel_frequency_help').toggle();", :class => 'inline_icon' %></label><br />
347
+ # <div class="inline_help" id="rel_frequency_help" style="display: none;">
348
+ # <p>Relative frequency of search traffic for this keyword across multiple search engines, as measured by WordTracker.</p>
349
+ # </div>
350
+ def tag_for_label_with_inline_help( label_text, field_id, help_text )
351
+ _html = ""
352
+ _html << %{<label for="#{field_id}">#{label_text}}
353
+ _html << %{<img src="/images/icons/help_icon.png" onclick="$('#{field_id}_help').toggle();" class='inline_icon' />}
354
+ _html << %{</label><br />}
355
+ _html << %{<div class="inline_help" id="#{field_id}_help" style="display: none;">}
356
+ _html << %{<p>#{help_text}</p>}
357
+ _html << %{</div>}
358
+ _html
359
+ end
360
+
361
+ # Create a set of tags for displaying a field label followed by instructions.
362
+ # The instructions are displayed on a new line following the field label.
363
+ #
364
+ # Usage:
365
+ #
366
+ # <%= tag_for_label_with_instructions 'Status', 'is_active', 'Only active widgets will be visible to the public.' %>
367
+ #
368
+ # Yields:
369
+ #
370
+ # <label for="is_active">
371
+ # Status<br />
372
+ # <span class="instructions">Only active widgets will be visible to the public.</span>
373
+ # <label><br />
374
+ def tag_for_label_with_instructions( label_text, field_id, instructions )
375
+ _html = ""
376
+ _html << %{<label for="#{field_id}">#{label_text}}
377
+ _html << %{<span class="instructions">#{instructions}</span>}
378
+ _html << %{</label><br />}
379
+ _html
380
+ end
381
+
382
+ end
383
+ end