smklib 1.0.6

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 (102) hide show
  1. checksums.yaml +7 -0
  2. data/app/assets/images/add.png +0 -0
  3. data/app/assets/images/button_cancel.png +0 -0
  4. data/app/assets/images/button_ok.png +0 -0
  5. data/app/assets/images/cmseditor/16x16/text_block.png +0 -0
  6. data/app/assets/images/cmseditor/16x16/text_bold.png +0 -0
  7. data/app/assets/images/cmseditor/16x16/text_center.png +0 -0
  8. data/app/assets/images/cmseditor/16x16/text_italic.png +0 -0
  9. data/app/assets/images/cmseditor/16x16/text_left.png +0 -0
  10. data/app/assets/images/cmseditor/16x16/text_right.png +0 -0
  11. data/app/assets/images/cmseditor/16x16/text_strike.png +0 -0
  12. data/app/assets/images/cmseditor/16x16/text_sub.png +0 -0
  13. data/app/assets/images/cmseditor/16x16/text_super.png +0 -0
  14. data/app/assets/images/cmseditor/16x16/text_under.png +0 -0
  15. data/app/assets/images/cmseditor/22x22/text_block.png +0 -0
  16. data/app/assets/images/cmseditor/22x22/text_bold.png +0 -0
  17. data/app/assets/images/cmseditor/22x22/text_center.png +0 -0
  18. data/app/assets/images/cmseditor/22x22/text_italic.png +0 -0
  19. data/app/assets/images/cmseditor/22x22/text_left.png +0 -0
  20. data/app/assets/images/cmseditor/22x22/text_right.png +0 -0
  21. data/app/assets/images/cmseditor/22x22/text_strike.png +0 -0
  22. data/app/assets/images/cmseditor/22x22/text_sub.png +0 -0
  23. data/app/assets/images/cmseditor/22x22/text_super.png +0 -0
  24. data/app/assets/images/cmseditor/22x22/text_under.png +0 -0
  25. data/app/assets/images/trashcan_empty.png +0 -0
  26. data/app/assets/javascripts/collapsable.js +56 -0
  27. data/app/assets/javascripts/hide_show.js +39 -0
  28. data/app/assets/javascripts/jscalendar-1.0/calendar-setup.js +200 -0
  29. data/app/assets/javascripts/jscalendar-1.0/calendar.js +1806 -0
  30. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-af.js +39 -0
  31. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-al.js +101 -0
  32. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-bg.js +124 -0
  33. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-big5-utf8.js +123 -0
  34. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-big5.js +123 -0
  35. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-br.js +108 -0
  36. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ca.js +123 -0
  37. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-cs-utf8.js +65 -0
  38. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-cs-win.js +65 -0
  39. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-da.js +123 -0
  40. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-de.js +124 -0
  41. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-du.js +45 -0
  42. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-el.js +89 -0
  43. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-en.js +127 -0
  44. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-es.js +129 -0
  45. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-fi.js +98 -0
  46. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-fr.js +125 -0
  47. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-he-utf8.js +123 -0
  48. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-hr-utf8.js +49 -0
  49. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-hr.js +0 -0
  50. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-hu.js +124 -0
  51. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-it.js +124 -0
  52. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-jp.js +45 -0
  53. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ko-utf8.js +120 -0
  54. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ko.js +120 -0
  55. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-lt-utf8.js +114 -0
  56. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-lt.js +114 -0
  57. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-lv.js +123 -0
  58. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-nl.js +73 -0
  59. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-no.js +114 -0
  60. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-pl-utf8.js +93 -0
  61. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-pl.js +56 -0
  62. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-pt.js +123 -0
  63. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ro.js +66 -0
  64. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ru.js +123 -0
  65. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-ru_win_.js +123 -0
  66. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-si.js +94 -0
  67. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-sk.js +99 -0
  68. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-sp.js +110 -0
  69. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-sv.js +93 -0
  70. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-tr.js +58 -0
  71. data/app/assets/javascripts/jscalendar-1.0/lang/calendar-zh.js +119 -0
  72. data/app/assets/javascripts/jscalendar-1.0/lang/cn_utf8.js +123 -0
  73. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/active-bg.gif +0 -0
  74. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/dark-bg.gif +0 -0
  75. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/hover-bg.gif +0 -0
  76. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/menuarrow.gif +0 -0
  77. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/normal-bg.gif +0 -0
  78. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/rowhover-bg.gif +0 -0
  79. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/status-bg.gif +0 -0
  80. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/theme.css +236 -0
  81. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/title-bg.gif +0 -0
  82. data/app/assets/stylesheets/jscalendar-1.0/skins/aqua/today-bg.gif +0 -0
  83. data/app/views/error_mailer/snapshot.rhtml +71 -0
  84. data/lib/smklib.rb +6 -0
  85. data/lib/smklib/array_ext.rb +76 -0
  86. data/lib/smklib/browsers.rb +69 -0
  87. data/lib/smklib/builder_ext.rb +103 -0
  88. data/lib/smklib/date_ext.rb +23 -0
  89. data/lib/smklib/debug_support.rb +27 -0
  90. data/lib/smklib/error_mailer.rb +70 -0
  91. data/lib/smklib/hash_ext.rb +19 -0
  92. data/lib/smklib/htmlutils.rb +604 -0
  93. data/lib/smklib/movable_children.rb +28 -0
  94. data/lib/smklib/object_ext.rb +34 -0
  95. data/lib/smklib/php_serialize.rb +309 -0
  96. data/lib/smklib/railtie.rb +6 -0
  97. data/lib/smklib/somekool_scaffold.rb +198 -0
  98. data/lib/smklib/super_looking_list.rb +63 -0
  99. data/lib/smklib/version.rb +11 -0
  100. data/vendor/calendar_helper.rb +100 -0
  101. data/vendor/htmltokenizer.rb +259 -0
  102. metadata +158 -0
@@ -0,0 +1,19 @@
1
+ class Hash
2
+
3
+ def method_missing(id)
4
+ to_hash[id.id2name]
5
+ end
6
+
7
+ # takes an array of keys and returns an array of values
8
+ # it would really be ideal to extend the definition of []
9
+ def find_multiple_key_values(array)
10
+ return [self[array]] unless array.is_a? Array
11
+ array.collect { |k| self[k] }
12
+ end
13
+
14
+ def inspect
15
+ a = keys.collect { |key| n = (key.respond_to?(:to_s)) ? key.to_s : nil; [key, n] }.sort { |l, r| l[1] <=> r[1] }
16
+ "{" + a.collect { |(key, junk)| key.inspect + "=>" + self[key].inspect }.join(", ") + "}"
17
+ end
18
+
19
+ end
@@ -0,0 +1,604 @@
1
+ # encoding: UTF-8
2
+ #require 'timezone'
3
+ #require 'rmagick'
4
+
5
+ # The methods added to this helper will be available to all templates in the application.
6
+ module SMKLib
7
+ module HtmlUtils
8
+ # Return valid google analytics Javascript code
9
+ def google_analytics(code)
10
+ # code should look like this UA-97533-1
11
+ if RAILS_ENV == "production" and not(code.to_s.empty?)
12
+ return <<EOT
13
+ <script src="https://www.google-analytics.com/urchin.js" type="text/javascript">
14
+ </script>
15
+ <script type="text/javascript">
16
+ _uacct = "#{code}";
17
+ urchinTracker();
18
+ </script>
19
+ EOT
20
+ else
21
+ ""
22
+ end
23
+ end
24
+
25
+ def get_file_value(parent, opclass, spname, ref_id=nil)
26
+ case opclass.to_s
27
+ when 'CompanyLogo'
28
+ parent.company_logo
29
+ when 'RealtorPhoto'
30
+ parent.realtor_photo
31
+ when 'ProductPhoto'
32
+ parent.product_photo
33
+ when 'Photo'
34
+ parent.photo
35
+ when 'Floorplan'
36
+ nil
37
+ else
38
+ raise "fuck you #{opclass.inspect}"
39
+ ref_id = (opclass.is_a?(RealtorAttachment) ? -1 : nil) if ref_id.nil?
40
+ opclass.find(:first, :conditions => ["properties_id = ? and htmlname = ?", ref_id, spname])
41
+ end
42
+ end
43
+
44
+ def fileinput_field(parent, opclass, options={})
45
+ raise "'wrong options #{options.inspect}'" unless options.is_a?(Hash)
46
+ options = {:no_div => false, :value => nil, :lang => 'en'}.merge(options)
47
+ controller = parent.class.to_s.downcase.pluralize
48
+ spname = "#{controller.singularize}_#{opclass.to_s.downcase}"
49
+ options[:value] = get_file_value(parent, opclass, spname) if options[:value].nil?
50
+ id_name = "fileinput_field_#{spname}"
51
+ if options[:value].kind_of?(opclass) and options[:value][:id].to_i > 0
52
+ url_hash = {:controller => controller, :action => 'kill_picture', :id => parent, :pic_class => Inflector.underscore(opclass)}
53
+ if options[:lang]=='en'
54
+ link_text = 'Delete picture'
55
+ confirm_text = 'Are you sure?'
56
+ msg_text = 'To replace the picture, you need to delete this one first.'
57
+ else
58
+ link_text = '変更'
59
+ confirm_text = '本当ですか?'
60
+ msg_text = ''
61
+ end
62
+ link = link_to_remote(link_text, :update => id_name, :url => url_hash, :confirm => confirm_text)
63
+ h = "<img src='#{options[:value].thumbnail_path}' /><br/>#{link}<br/>#{msg_text}"
64
+ else
65
+ t = "<br/><small>(jpeg, jpg, gif files of up to 3MB in size can be uploaded.)</small>" if options[:lang]=='en'
66
+ h = "<input type='file' name='#{controller.singularize}[#{opclass.to_s.downcase}]' />#{t}"
67
+ end
68
+ if options[:no_div]
69
+ h
70
+ else
71
+ "<div id='#{id_name}'>#{h}</div>"
72
+ end
73
+ end
74
+
75
+ def form_filter(content, action = '')
76
+ "<form id='my_f' style='display: inline; margin: 0px; padding: 0px;' action='#{action}' method='post'>" + content + '</form>'
77
+ end
78
+
79
+ begin
80
+ do_nothing = true if Locale.is_a?(Class)
81
+ rescue
82
+ def _(str)
83
+ str
84
+ end
85
+ end
86
+
87
+ def datetime_select(object, method, start_date=Time.now.strftime("%Y, %m, %d, %H, %M"), options = {})
88
+ raise "dont use me you bastard."
89
+ # datetime_select_popup(object, method, start_date, options)
90
+ end
91
+
92
+ def datetime_select_popup(object, method, start_date=Time.now.strftime("%Y, %m, %d, %H, %M"), options = {})
93
+ id = "#{object}_#{method}"
94
+ # <form action="#" method="get" style="visibility: hidden">
95
+ # <input type="hidden" name="date" id="f_date_d" />
96
+ # </form>
97
+ code = <<EOC
98
+
99
+ <span style="background-color: #ff8; cursor: default;"
100
+ onmouseover="this.style.backgroundColor='#ff0';"
101
+ onmouseout="this.style.backgroundColor='#ff8';"
102
+ id="span_#{id}">Click to open date &amp; time selector</span>
103
+
104
+ <script type="text/javascript">
105
+ Calendar.setup({
106
+ inputField : "#{id}", // id of the input field
107
+ ifFormat : "%Y-%m-%d %H:%M", // format of the input field (even if hidden, this format will be honored)
108
+ displayArea : "span_#{id}", // ID of the span where the date is to be shown
109
+ daFormat : "%A, %B %d, %Y [%H:%M]",// format of the displayed date
110
+ showsTime : true,
111
+ timeFormat : "24",
112
+ align : "Tl", // alignment (defaults to "Bl")
113
+ singleClick : true
114
+ });
115
+ </script>
116
+ EOC
117
+ #js = 'return showCalendar("#{id}", "%Y-%m-%d [%W] %H:%M", "24", true);'
118
+ #"#{text_field(object, method, :size => 30)}<input type='reset' value='...' onclick='#{js}'>"
119
+ hidden_field(object, method) + code.html_safe
120
+ end
121
+
122
+ def datetime_select_flat(object, method, options = {})
123
+ id = "#{object}_#{method}"
124
+ code = <<EOC
125
+ <div style="float: right; margin-left: 1em; margin-bottom: 1em;" id="calendar-container"></div>
126
+
127
+ <script type="text/javascript">
128
+ Calendar.setup(
129
+ {
130
+ inputField : "#{id}", // id of the input field
131
+ ifFormat : "%Y-%m-%d %H:%M", // format of the input field (even if hidden, this format will be honored)
132
+ showsTime : true,
133
+ timeFormat : "24",
134
+ align : "Tl", // alignment (defaults to "Bl")
135
+ flat : "calendar-container", // ID of the parent element
136
+ flatCallback : dateChanged // our callback function
137
+ }
138
+ );
139
+ </script>
140
+ EOC
141
+ hidden_field(object, method) + code.html_safe
142
+ end
143
+
144
+
145
+ def get_img_size(filename)
146
+ #im = Magick::Image.read(real_filename(filename)).first
147
+ #return [im.columns, im.rows]
148
+ [22,22]
149
+ [64,64]
150
+ end
151
+
152
+ def img_tag(options = {})
153
+ if false and ENV['HTTP_USER_AGENT'] =~ /MSIE.*Windows/
154
+ if options["src"] =~ /\.png/
155
+ options["style"] = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='#{options["src"]}');" # , enabled='enabled', sizingMethod='scale');"
156
+ options["width"], options["height"] = get_img_size(options["src"]) unless options["width"] && options["height"]
157
+ options["src"] = "/images/spacer.gif"
158
+ end
159
+ end
160
+ #if block_given?
161
+ # method_missing(:img, options, yield)
162
+ #else
163
+ # method_missing(:img, options)
164
+ #end
165
+ o = options.collect {|f,v| "#{f}='#{v}'" }.join(' ')
166
+ "<img #{o} />"
167
+ end
168
+
169
+ # <div class="div_title">Advertising / Publicit&eacute;</div>
170
+ # help keep www.justbudget.com alive, click a link!<br/>
171
+ # aider moi a garder www.justbudget.com en vie, cliquer sur un liens sponsoriser!<br/>
172
+ def google_ads(orientation, channel="")
173
+ if channel == "public"
174
+ channel = "0967175666"
175
+ elsif channel == "private"
176
+ channel = "2002318862"
177
+ end
178
+ # justbudget.com
179
+ if orientation == "horizontal"
180
+ '
181
+ <div class="ads_on_the_bottom">
182
+ <script type="text/javascript"><!--
183
+ google_ad_client = "pub-4887039760095281";
184
+ google_ad_width = 728;
185
+ google_ad_height = 90;
186
+ google_ad_format = "728x90_as";
187
+ google_ad_channel ="";
188
+ google_color_border = "FFFFFF";
189
+ google_color_bg = "FFFFFF";
190
+ google_color_link = "00008B";
191
+ google_color_url = "00008B";
192
+ google_color_text = "000000";
193
+ //--></script>
194
+ <script type="text/javascript"
195
+ src="https://pagead2.googlesyndication.com/pagead/show_ads.js">
196
+ </script>
197
+ </div>
198
+ '
199
+ elsif orientation == "vertical"
200
+ '
201
+ <div class="ads_on_the_side">
202
+ <div class="div_title">Advertising</div>
203
+ <script type="text/javascript"><!--
204
+ google_ad_client = "pub-4887039760095281";
205
+ google_ad_width = 120;
206
+ google_ad_height = 600;
207
+ google_ad_format = "120x600_as";
208
+ google_ad_channel ="";
209
+ google_color_border = "E0FFE3";
210
+ google_color_bg = "E0FFE3";
211
+ google_color_link = "0000CC";
212
+ google_color_url = "008000";
213
+ google_color_text = "000000";
214
+ //--></script>
215
+ <script type="text/javascript"
216
+ src="https://pagead2.googlesyndication.com/pagead/show_ads.js">
217
+ </script>
218
+ </div>
219
+ '
220
+ elsif orientation == "justbudget"
221
+ return <<EOC
222
+ <script type="text/javascript"><!--
223
+ google_ad_client = "pub-4887039760095281";
224
+ google_alternate_color = "FFFFFF";
225
+ google_ad_width = 120;
226
+ google_ad_height = 240;
227
+ google_ad_format = "120x240_as";
228
+ google_ad_type = "text_image";
229
+ google_ad_channel ="#{channel}";
230
+ google_color_border = "000000";
231
+ google_color_bg = "F0F0F0";
232
+ google_color_link = "0000FF";
233
+ google_color_url = "008000";
234
+ google_color_text = "000000";
235
+ //--></script>
236
+ <script type="text/javascript"
237
+ src="https://pagead2.googlesyndication.com/pagead/show_ads.js">
238
+ </script>
239
+ EOC
240
+ elsif orientation == "srosa"
241
+ return <<EOC
242
+ <script type="text/javascript"><!--
243
+ google_ad_client = "pub-4887039760095281";
244
+ google_ad_width = 120;
245
+ google_ad_height = 240;
246
+ google_ad_format = "120x240_as";
247
+ google_ad_channel ="";
248
+ google_color_border = "FFFFFF";
249
+ google_color_bg = "FFFFFF";
250
+ google_color_link = "086582";
251
+ google_color_url = "086582";
252
+ google_color_text = "333333";
253
+ //--></script>
254
+ <script type="text/javascript" src="https://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
255
+ EOC
256
+ else
257
+ '
258
+ <p>
259
+ </p>
260
+ '
261
+ end
262
+ end
263
+
264
+ def google_conversion_tracker_english
265
+ if session['signup']
266
+ session['signup'] = false
267
+ '
268
+ <br/>
269
+ <!-- Google Code for Signup Conversion Page -->
270
+ <script language="JavaScript" type="text/javascript">
271
+ <!--
272
+ var google_conversion_id = 1068909979;
273
+ var google_conversion_language = "en_US";
274
+ var google_conversion_format = "2";
275
+ var google_conversion_color = "0066CC";
276
+ if (1.0) {
277
+ var google_conversion_value = 1.0;
278
+ }
279
+ var google_conversion_label = "Signup";
280
+ //-->
281
+ </script>
282
+ <script language="JavaScript" src="https://www.googleadservices.com/pagead/conversion.js">
283
+ </script>
284
+ <noscript>
285
+ <img height=1 width=1 border=0 src="https://www.googleadservices.com/pagead/conversion/1068909979/?value=1.0&label=Signup&script=0">
286
+ </noscript>
287
+ '
288
+ end
289
+ end
290
+
291
+ def display_errors
292
+ return '' if flash.class == String # flash got broken by render_component.
293
+ s = ""
294
+ css = "
295
+ <style>
296
+ .flash { text-align: left; padding: 5px 25px; border: 4px solid blue; margin: 0px auto 20px; display: table; }
297
+ .flash img { margin-right: 15px; }
298
+ .flash.error_content { border-color: red; }
299
+ .flash.notice_content { border-color: green; }
300
+ </style>
301
+ "
302
+ # s += '<pre>--' + flash.to_yaml + '<br/> -- ' + @flash.to_yaml + '<br/> -- </pre>' if local_request?
303
+ f_n = [flash[:notice], flash['notice'], flash[:message], flash['message']].compact.sort.uniq.join(' ')
304
+ unless f_n.empty?
305
+ s += '<div class="flash notice_content">'
306
+ s += img_tag(:src => image_path("button_ok.png"))
307
+ s += '<span>'
308
+ s += f_n
309
+ #s += '<br/>' + google_conversion_tracker_english if @flash['signup']
310
+ s += '</span>'
311
+ s += '</div>'
312
+ s += '<div>&nbsp;</div>' # unless s.empty?
313
+ end
314
+ f_a = [flash[:warning], flash['warning'], flash[:alert], flash['alert']].compact.sort.uniq.join(' ')
315
+ unless f_a.empty?
316
+ s += '<div class="flash error_content">'
317
+ s += img_tag(:src => image_path("button_cancel.png"))
318
+ s += '<span>'
319
+ s += f_a
320
+ s += '</span>'
321
+ s += '</div>'
322
+ s += '<div>&nbsp;</div>' # unless s.empty?
323
+ end
324
+ s = css + s unless s.empty?
325
+ return s
326
+ end
327
+
328
+ def mailto(text, email)
329
+ "<a href='mailto:#{email}'>#{text}</a>"
330
+ end
331
+
332
+ def display_footer
333
+ "any questions ? suggestions ? comments ? please email #{mailto('me', 'somekool@somekool.net')}" if (@session['user']).is_a?(User)
334
+ end
335
+
336
+ def separator
337
+ '<div class="separator"><hr/></div>'
338
+ end
339
+
340
+ def select_timezone(object, method)
341
+ select object, method, Timezone.find_all.collect {|p| [ p.first, p.last ] }, { :include_blank => true }
342
+ end
343
+
344
+ # File actionpack/lib/action_view/helpers/date_helper.rb, line 238
345
+ def select_html(type, options, prefix = nil, include_blank = false, discard_type = false, disabled = false)
346
+ select_html = %(<select name="#{prefix || 'date'})
347
+ select_html << "[#{type}]" unless discard_type
348
+ select_html << %(")
349
+ select_html << %( disabled="disabled") if disabled
350
+ select_html << %(>\n)
351
+ select_html << %(<option value=""></option>\n) if include_blank
352
+ select_html << options.to_s
353
+ select_html << "</select>\n"
354
+ select_html.html_safe
355
+ end
356
+
357
+ def my_select_day(date, select_name, options = {})
358
+ day_options = []
359
+ 1.upto(31) do |day|
360
+ #raise "he #{date.kind_of?(Fixnum)} ... #{date.class} -- #{date}"
361
+ day_options << ((date.kind_of?(Fixnum) ? date : date.day) == day ?
362
+ "<option selected=\"selected\">#{day}</option>\n" :
363
+ "<option>#{day}</option>\n"
364
+ )
365
+ end
366
+ select_html(select_name, day_options, options[:prefix], options[:include_blank], options[:discard_type])
367
+ end
368
+
369
+ def text_and_select_field(label, object, method, select_data, current_value, options = {})
370
+ text_id = "#{object}_#{method}"
371
+ select_id = "select_#{object}_#{method}_box"
372
+ options = {
373
+ :select_extra_js => '',
374
+ :img_extra_js => '',
375
+ :select_js => "AddToCategoryClick('#{text_id}', this);",
376
+ :img_js => "showOrHide('#{select_id}');showOrHide('#{text_id}');",
377
+ :field_only => false,
378
+ :text_field_type => 'magic', # can be 'tag'
379
+ :text_field_value => '', # used if text_field_type == 'tag'
380
+ :button_value => '...',
381
+ :default_img_alt => '', # obsolete, button was and image
382
+ :extra => nil, # some custom stuff I will add at the end.
383
+ }.merge(options)
384
+ widget_width = "width: 190px;"
385
+ if select_data.empty?
386
+ show_textfield = true
387
+ text_field_html_options = {'style' => widget_width}
388
+ select_css_style = "display:none;margin:0px;padding:0px;#{widget_width}"
389
+ else
390
+ show_textfield = false
391
+ text_field_html_options = {'style' => "display:none;#{widget_width}"}
392
+ select_css_style = "display:inline;margin:0px;padding:0px;#{widget_width}"
393
+ end
394
+ def field_only_or_not(label_for, label_text, field_only) # , &block)
395
+ xml = Builder::XmlMarkup.new(:indent=>2)
396
+ if field_only
397
+ yield(xml)
398
+ else
399
+ xml.p do
400
+ xml.label(:for => label_for) { xml << label_text }
401
+ xml.br
402
+ yield(xml)
403
+ end
404
+ end
405
+ end
406
+ #raise options[:field_only].inspect
407
+ field_only_or_not(text_id, label.to_s.capitalize, options[:field_only]) do |xml|
408
+ if options[:text_field_type] == "tag"
409
+ xml << text_field_tag(text_id, options[:text_field_value], text_field_html_options)
410
+ else
411
+ xml << text_field(object, method, text_field_html_options)
412
+ end
413
+ xml.span(:id => select_id + '_div') do
414
+ xml.select(:id => select_id, :name => select_id, :style => select_css_style, :onchange => "#{options[:select_js]}#{options[:select_extra_js]}") do
415
+ xml.option(:value => '') { xml << _("Choose one") } # a #{label}" }
416
+ xml << options_for_select(select_data, current_value)
417
+ end
418
+ end
419
+ xml.input(:type => 'button', :value => options[:button_value], :alt => options[:default_img_alt], :border => 0, :align => "absmiddle", :onclick => "#{options[:img_js]}#{options[:img_extra_js]}")
420
+ xml.span do
421
+ xml << options[:extra]
422
+ end unless options[:extra].to_s.empty?
423
+ xml.target!
424
+ end.html_safe
425
+ end
426
+
427
+ def normal_field(xml, object, method, label, type='text_field')
428
+ xml.p do
429
+ xml.label(:for => "#{object}_#{method}") do
430
+ xml << label
431
+ end
432
+ xml.br
433
+ case type
434
+ when 'text_field'
435
+ xml << text_field(object, method)
436
+ when 'text_area'
437
+ xml << text_area(object, method)
438
+ when 'in_place_field'
439
+ xml << in_place_editor_field(object, method)
440
+ when 'in_place_editor'
441
+ xml << in_place_editor(object, method)
442
+ when 'in_place_select_field'
443
+ xml << in_place_select_field(object, method)
444
+ when 'other'
445
+ yield(xml)
446
+ end
447
+ end
448
+ end
449
+
450
+ def many_to_many_field(xml, object, method, parent_method, label, all_values, options={})
451
+ xml ||= Builder::XmlMarkup.new(:indent=>2)
452
+ #@controller = (@controller or options[:controller])
453
+ options = {:item_name => 'name', :m2m_selected => 'm2m_selected'}.merge(options)
454
+ @object = instance_variable_get("@#{object}")
455
+ @values = @object.send(method)
456
+ parent_id = "#{object}_id".to_sym
457
+ child_id = "#{method.to_s.singularize}_id".to_sym
458
+ raise @values.inspect unless @values.is_a?(Array)
459
+ normal_field(xml, object, method, label, 'other') do |xml|
460
+ xml.div(:class => options[:m2m_selected]) do
461
+ @values.each do |item|
462
+ xml.div do
463
+ xml << item.send(options[:item_name])
464
+ xml << '&nbsp;'
465
+ xml << link_to(_('Remove'), url_for(:controller => 'businesses', :action => "remove_#{method.to_s.singularize}_from_#{object}", parent_id => @object[:id], child_id => item[:id]))
466
+ end
467
+ end
468
+ end
469
+ s = "select_#{object}_#{method}_box"
470
+ #url = url_for({ :action => "add_#{method.to_s.singularize}_to_#{object}" })
471
+ #url += "/'+$('#{s}').options[$('#{s}').selectedIndex].value+'/to_#{object}/#{@object[:id]}"
472
+ #url = url_for({ :action => "add_#{method.singularize}_to_#{object}", parent_id => @object[:id].to_i, child_id => "'+$('#{s}').options[$('#{s}').selectedIndex].value+'", :escape => false })
473
+ url = url_for({ :action => "add_#{method.singularize}_to_#{object}", parent_id => @object[:id].to_i, child_id => "__js__", parent_method => "__js2__" })
474
+ if all_values.empty?
475
+ xml << "<input type='text' id='#{object}_#{parent_method}' />"
476
+ xml << text_field(object, method)
477
+ url.gsub!("__js__", "'+$('#{object}_#{method}').value+'")
478
+ url.gsub!("__js2__", "'+$('#{object}_#{parent_method}').value+'")
479
+ xml << "<input type='button' align='absmiddle' alt='' border='0' value='Add' onclick=\"window.location='#{url}';return false;\" />"
480
+ else
481
+ url.gsub!("__js__", "'+$('#{s}').options[$('#{s}').selectedIndex].value+'")
482
+ xml << text_and_select_field(nil, object, method, all_values, nil, :button_value => _('Add'), :field_only => true, :text_field_type => 'tag', :img_js => "window.location='#{url}';return false;")
483
+ end
484
+ end
485
+ end
486
+
487
+ =begin
488
+ @columns = [
489
+ {:label => 'Status', :field => 'status', :format => nil},
490
+ {:label => 'Entered', :field => 'created_time', :format => Proc.new{|d| d.strftime("%Y.%d.%m") } },
491
+ {:label => 'Modified', :field => 'modified_time', :format => Proc.new{|d| d.strftime("%Y.%d.%m") } },
492
+ {:label => 'Name', :field => 'fullname', :format => nil},
493
+ {:label => 'House', :field => 'house', :format => nil},
494
+ {:label => 'Category', :field => 'category', :format => nil},
495
+ ]
496
+ =end
497
+
498
+ def sortable_list(xml, table_id, columns, data)
499
+ xml = Builder::XmlMarkupr.new(:indent=>2, :margin=>4) if xml.nil?
500
+ xml.table(:id => table_id, :cellpadding => 0, :cellspacing => 0) do
501
+ xml.tr(:class => 'header') do
502
+ columns.each { |c|
503
+ xml.th do
504
+ xml.a(:href => url_for(:action => 'list', :sort => c[:field], :order => ((@params[:sort] == c[:field] and @params[:order] == 'asc') ? 'desc' : 'asc'))) do
505
+ xml << c[:label]
506
+ end
507
+ end
508
+ }
509
+ xml.th(:colspan => 3) { xml << "" }
510
+ end
511
+ for inquiry in data
512
+ xml.tr do # xml.tr(:class => "row #{inquiry['status']}") do
513
+ columns.each { |c|
514
+ f = c[:field]
515
+ if c[:format].nil?
516
+ xml.td inquiry[f]
517
+ else
518
+ xml.td c[:format].call(inquiry[f])
519
+ end
520
+ }
521
+ xml.td { xml << link_to(img_tag('src' => '/images/viewmag.png'), :action => 'show', :id => inquiry) } if false
522
+ xml.td { xml << link_to(img_tag('src' => '/images/edit.png'), :action => 'edit', :id => inquiry) }
523
+ xml.td { xml << link_to(img_tag('src' => '/images/trashcan_empty.png'), {:action => 'destroy', :id => inquiry}, :post => true, :confirm => "Are you sure?") }
524
+ end
525
+ # xml.tr(:class => "row #{inquiry['status']} desc") do
526
+ # xml.td(:colspan => columns.size + 3) do
527
+ # xml.p do
528
+ # unless inquiry['phone'].empty?
529
+ # xml << 'Phone: ' + inquiry['phone']
530
+ # xml.br
531
+ # end
532
+ # unless inquiry['email'].empty?
533
+ # xml << 'Email: ' + inquiry['email']
534
+ # xml.br
535
+ # end
536
+ # xml << 'Description: ' + inquiry['description'] unless inquiry['description'].empty?
537
+ # end
538
+ # end
539
+ # end
540
+ end
541
+ end
542
+ end
543
+ end
544
+ end
545
+
546
+ module ActionController
547
+ module Macros
548
+ module ManyToMany #:nodoc:
549
+ def self.append_features(base) #:nodoc:
550
+ super
551
+ base.extend(ClassMethods)
552
+ end
553
+
554
+ # Example:
555
+ #
556
+ # # Controller
557
+ # class BusinessesController < ApplicationController
558
+ # many_to_many_for :business, :subcategories, :label => _('Subcategory'), :m2m_field_options => {:item_name => 'name_with_category'}
559
+ # end
560
+ #
561
+ # # View
562
+ # xml.div(:id => 'm2m_business_subcategories') do
563
+ # many_to_many_field(xml, 'business', 'subcategories', _('Subcategory'), @subcategories, :item_name => 'name_with_category')
564
+ # end
565
+ #
566
+ module ClassMethods
567
+ def many_to_many_for(object, attribute, options = {})
568
+ #include SMKLib::HtmlUtils
569
+ options = {:max_size => 10, :label => attribute.to_s.capitalize}.merge(options)
570
+ parent_model, child_model = object.to_s.camelize.constantize, attribute.to_s.singularize.camelize.constantize
571
+ @controller = self
572
+
573
+ define_method("add_#{attribute.to_s.singularize}_to_#{object}") do
574
+ instance_variable_set("@#{object}", parent_model.find(params[:business_id]))
575
+ collection = instance_variable_get("@#{object}").send(attribute.to_s)
576
+ if collection.size >= options[:max_size]
577
+ flash[:alert] = _("Can't add. This %s already has %d %s.") % [object, options[:max_size], attribute]
578
+ else
579
+ if params[:subcategory_id].to_i > 0
580
+ collection << child_model.find(params[:subcategory_id]) rescue flash[:alert] = _('%s already linked to this %s.') % [attribute.to_s.capitalize.singularize, object]
581
+ else
582
+ c = Category.find_or_create_by_name(params[:categories])
583
+ collection << child_model.find_or_create_by_name_and_category_id(params[:subcategory_id], c[:id])
584
+ end
585
+ end
586
+ redirect_to :action => 'edit', :id => instance_variable_get("@#{object}")[:id]
587
+ # many_to_many_field(nil, object, attribute, options[:label], child_model.find_all, options[:m2m_field_options].merge(:controller => self))
588
+ end
589
+
590
+ define_method("remove_#{attribute.to_s.singularize}_from_#{object}") do
591
+ instance_variable_set("@#{object}", parent_model.find(params[:business_id]))
592
+ instance_variable_get("@#{object}").send(attribute).delete(child_model.find(params[:subcategory_id]))
593
+ redirect_to :action => 'edit', :id => instance_variable_get("@#{object}")[:id]
594
+ #raise many_to_many_field(nil, object, attribute, options[:label], child_model.find_all, options[:m2m_field_options].merge(:controller => self))
595
+ end
596
+ end
597
+ end
598
+ end
599
+ end
600
+ end
601
+
602
+ ActionController::Base.class_eval do
603
+ include ActionController::Macros::ManyToMany
604
+ end