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,28 @@
1
+
2
+ class ActiveRecord::Base
3
+ def self.has_movable(association_id, options = {}, &extension)
4
+ define_method('last_order_index') do
5
+ max = -6500
6
+ self.send(association_id.to_s).each { |p| max = p.order_index.to_i if p.order_index.to_i > max.to_i }
7
+ max < -1 ? -1 : max
8
+ end
9
+
10
+ define_method('update_order_indexes') do |moved_page_id, above_page_id|
11
+ moved_page = self.send(association_id.to_s).find(moved_page_id)
12
+ above_page = self.send(association_id.to_s).find(above_page_id)
13
+ next_next_order_index = above_page.order_index.to_i + 2
14
+ self.send(association_id.to_s).find(:all, :conditions => ['order_index > ?', above_page.order_index]).each { |p|
15
+ p.order_index = next_next_order_index
16
+ p.save
17
+ next_next_order_index +=1
18
+ }
19
+ moved_page.order_index = above_page.order_index.to_i + 1
20
+ moved_page.save
21
+ end
22
+
23
+ end
24
+
25
+ def self.is_movable_within_a(association_id, options = {}, &extension)
26
+ before_create { |model| model.order_index = model.send(association_id.to_s).last_order_index + 1 }
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ class Object
2
+
3
+ def self.run_once(unique_string, &block)
4
+ $run_once_guard ||= {}
5
+ return if $run_once_guard[unique_string]
6
+ raise "No block given" unless block_given?
7
+ yield
8
+ $run_once_guard[unique_string] = true
9
+ end
10
+
11
+ def to_class
12
+ self.class.const_get self.to_s
13
+ end
14
+
15
+ def in?(array)
16
+ array.include? self
17
+ end
18
+
19
+ # based on http://www.rubygarden.org/ruby?Make_A_Deep_Copy_Of_An_Object
20
+ def deep_clone
21
+ Marshal::load(Marshal.dump(self))
22
+ end
23
+
24
+ def ignore_exception(*exceptions, &block)
25
+ begin
26
+ yield
27
+ rescue Exception => e
28
+ skip = false
29
+ exceptions.each { |ex| skip = true if e.kind_of? ex }
30
+ raise e unless skip
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env ruby
2
+ # License and Copyright {{{
3
+ # Copyright (c) 2003 Thomas Hurst <freaky@aagh.net>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+ # }}}
23
+
24
+ # PHP serialize() and unserialize() workalikes
25
+ # First Released: 2003-06-02 (1.0.0)
26
+ # Prev Release: 2003-06-16 (1.0.1), by Thomas Hurst <tom@hur.st>
27
+ # This Release: 2004-09-17 (1.0.2), by Thomas Hurst <tom@hur.st>
28
+ # Switch all {}'s to explicit Hash.new's.
29
+ #
30
+ # These two methods should, for the most part, be functionally identical
31
+ # to the respective PHP functions;
32
+ # http://www.php.net/serialize, http://www.php.net/unserialize
33
+ #
34
+ #
35
+ # string = PHP.serialize(mixed var[, bool assoc])
36
+ # Returns a string representing the argument in a form PHP.unserialize
37
+ # and PHP's unserialize() should both be able to load.
38
+ #
39
+ # Array, Hash, Fixnum, Float, True/FalseClass, NilClass, String and Struct
40
+ # are supported; as are objects which support the to_assoc method, which
41
+ # returns an array of the form [['attr_name', 'value']..]. Anything else
42
+ # will raise a TypeError.
43
+ #
44
+ # If 'assoc' is specified, Array's who's first element is a two value
45
+ # array will be assumed to be an associative array, and will be serialized
46
+ # as a PHP associative array rather than a multidimensional array.
47
+ #
48
+ #
49
+ #
50
+ # mixed = PHP.unserialize(string serialized, [hash classmap, [bool assoc]])
51
+ # Returns an object containing the reconstituted data from serialized.
52
+ #
53
+ # If a PHP array (associative; like an ordered hash) is encountered, it
54
+ # scans the keys; if they're all incrementing integers counting from 0,
55
+ # it's unserialized as an Array, otherwise it's unserialized as a Hash.
56
+ # Note: this will lose ordering. To avoid this, specify assoc=true,
57
+ # and it will be unserialized as an associative array: [[key,value],...]
58
+ #
59
+ # If a serialized object is encountered, the hash 'classmap' is searched for
60
+ # the class name (as a symbol). Since PHP classnames are not case-preserving,
61
+ # this *must* be a .capitalize()d representation. The value is expected
62
+ # to be the class itself; i.e. something you could call .new on.
63
+ #
64
+ # If it's not found in 'classmap', the current constant namespace is searched,
65
+ # and failing that, a new Struct(classname) is generated, with the arguments
66
+ # for .new specified in the same order PHP provided; since PHP uses hashes
67
+ # to represent attributes, this should be the same order they're specified
68
+ # in PHP, but this is untested.
69
+ #
70
+ # each serialized attribute is sent to the new object using the respective
71
+ # {attribute}=() method; you'll get a NameError if the method doesn't exist.
72
+ #
73
+ # Array, Hash, Fixnum, Float, True/FalseClass, NilClass and String should
74
+ # be returned identically (i.e. foo == PHP.unserialize(PHP.serialize(foo))
75
+ # for these types); Struct should be too, provided it's in the namespace
76
+ # Module.const_get within unserialize() can see, or you gave it the same
77
+ # name in the Struct.new(<structname>), otherwise you should provide it in
78
+ # classmap.
79
+ #
80
+ # Note: StringIO is required for unserialize(); it's loaded as needed
81
+
82
+ module PHP
83
+ def PHP.serialize(var, assoc = false) # {{{
84
+ s = ''
85
+ case var
86
+ when Array
87
+ s << "a:#{var.size}:{"
88
+ if assoc and var.first.is_a?(Array) and var.first.size == 2
89
+ var.each { |k,v|
90
+ s << PHP.serialize(k) << PHP.serialize(v)
91
+ }
92
+ else
93
+ var.each_with_index { |v,i|
94
+ s << "i:#{i};#{PHP.serialize(v)}"
95
+ }
96
+ end
97
+
98
+ s << '}'
99
+
100
+ when Hash
101
+ s << "a:#{var.size}:{"
102
+ var.each do |k,v|
103
+ s << "#{PHP.serialize(k)}#{PHP.serialize(v)}"
104
+ end
105
+ s << '}'
106
+
107
+ when Struct
108
+ # encode as Object with same name
109
+ s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{var.members.length}:{"
110
+ var.members.each do |member|
111
+ s << "#{PHP.serialize(member)}#{PHP.serialize(var[member])}"
112
+ end
113
+ s << '}'
114
+
115
+ when String
116
+ s << "s:#{var.length}:\"#{var}\";"
117
+
118
+ when Fixnum # PHP doesn't have bignums
119
+ s << "i:#{var};"
120
+
121
+ when Float
122
+ s << "d:#{var};"
123
+
124
+ when NilClass
125
+ s << 'N;'
126
+
127
+ when FalseClass, TrueClass
128
+ s << "b:#{var ? 1 :0};"
129
+
130
+ else
131
+ if var.respond_to?(:to_assoc)
132
+ v = var.to_assoc
133
+ # encode as Object with same name
134
+ s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{v.length}:{"
135
+ v.each do |k,v|
136
+ s << "#{PHP.serialize(k.to_s)}#{PHP.serialize(v)}"
137
+ end
138
+ s << '}'
139
+ else
140
+ raise TypeError, "Unable to serialize type #{var.class}"
141
+ end
142
+ end
143
+
144
+ s
145
+ end # }}}
146
+
147
+ def PHP.unserialize(string, classmap = nil, assoc = false) # {{{
148
+ require 'stringio'
149
+ string = StringIO.new(string)
150
+ def string.read_until(char)
151
+ val = ''
152
+ while (c = self.read(1)) != char
153
+ val << c
154
+ end
155
+ val
156
+ end
157
+
158
+ classmap ||= Hash.new
159
+
160
+ do_unserialize(string, classmap, assoc)
161
+ end
162
+
163
+ def PHP.do_unserialize(string, classmap, assoc)
164
+ val = nil
165
+ # determine a type
166
+ type = string.read(2)[0,1]
167
+ case type
168
+ when 'a' # associative array, a:length:{[index][value]...}
169
+ count = string.read_until('{').to_i
170
+ val = vals = Array.new
171
+ count.times do |i|
172
+ vals << [do_unserialize(string, classmap, assoc), do_unserialize(string, classmap, assoc)]
173
+ end
174
+ string.read(1) # skip the ending }
175
+
176
+ unless assoc
177
+ # now, we have an associative array, let's clean it up a bit...
178
+ # arrays have all numeric indexes, in order; otherwise we assume a hash
179
+ array = true
180
+ i = 0
181
+ vals.each do |key,value|
182
+ if key != i # wrong index -> assume hash
183
+ array = false
184
+ break
185
+ end
186
+ i += 1
187
+ end
188
+
189
+ if array
190
+ vals.collect! do |key,value|
191
+ value
192
+ end
193
+ else
194
+ val = Hash.new
195
+ vals.each do |key,value|
196
+ val[key] = value
197
+ end
198
+ end
199
+ end
200
+
201
+ when 'O' # object, O:length:"class":length:{[attribute][value]...}
202
+ # class name (lowercase in PHP, grr)
203
+ len = string.read_until(':').to_i + 3 # quotes, seperator
204
+ klass = string.read(len)[1...-2].capitalize.intern # read it, kill useless quotes
205
+
206
+ # read the attributes
207
+ attrs = []
208
+ len = string.read_until('{').to_i
209
+
210
+ len.times do
211
+ attr = (do_unserialize(string, classmap, assoc))
212
+ attrs << [attr.intern, (attr << '=').intern, do_unserialize(string, classmap, assoc)]
213
+ end
214
+ string.read(1)
215
+
216
+ val = nil
217
+ # See if we need to map to a particular object
218
+ if classmap.has_key?(klass)
219
+ val = classmap[klass].new
220
+ elsif Struct.const_defined?(klass) # Nope; see if there's a Struct
221
+ classmap[klass] = val = Struct.const_get(klass)
222
+ val = val.new
223
+ else # Nope; see if there's a Constant
224
+ begin
225
+ classmap[klass] = val = Module.const_get(klass)
226
+
227
+ val = val.new
228
+ rescue NameError # Nope; make a new Struct
229
+ classmap[klass] = val = Struct.new(klass.to_s, *attrs.collect { |v| v[0].to_s })
230
+ end
231
+ end
232
+
233
+ attrs.each do |attr,attrassign,v|
234
+ val.__send__(attrassign, v)
235
+ end
236
+
237
+ when 's' # string, s:length:"data";
238
+ len = string.read_until(':').to_i + 3 # quotes, separator
239
+ val = string.read(len)[1...-2] # read it, kill useless quotes
240
+
241
+ when 'i' # integer, i:123
242
+ val = string.read_until(';').to_i
243
+
244
+ when 'd' # double (float), d:1.23
245
+ val = string.read_until(';').to_f
246
+
247
+ when 'N' # NULL, N;
248
+ val = nil
249
+
250
+ when 'b' # bool, b:0 or 1
251
+ val = (string.read(2)[0] == ?1 ? true : false)
252
+
253
+ else
254
+ raise TypeError, "Unable to unserialize type '#{type}'"
255
+ end
256
+
257
+ val
258
+ end # }}}
259
+ end
260
+
261
+
262
+ if $0 == __FILE__
263
+ require 'tempfile'
264
+
265
+ class Foo
266
+ attr_accessor :foo, :bar, :wibble
267
+ def initialize
268
+ @foo = 'a'
269
+ @bar = 'b'
270
+ @wibble = 'c'
271
+ end
272
+
273
+ def to_assoc
274
+ [['foo', @foo], ['bar', @bar], ['wibble', @wibble]]
275
+ end
276
+ end
277
+
278
+ puts "The following should all be equivilent, taking into account our limitations."
279
+ puts " 1. Is the original Ruby data structure."
280
+ puts " >> Is our generated serialized string."
281
+ puts " << Is the serialized string after PHP's eaten it."
282
+ puts " 2. Is the reconstructed Ruby data structure"
283
+ puts "--"
284
+ T = Struct.new(:structures,:ass)
285
+ [
286
+ {5 => nil, "foo" => 1, "nana" => ["x","y","z"], 3 => false, "bar" => 2.4, 4 => true},
287
+ T.new('Kick','Dude!'),
288
+ ['a','b','c',['z','y','x'],{'hash' => 'smoke'},"\"\\''\""],
289
+ "He said \"Damnit, I'm lame\", and then I kicked his ass.",
290
+ Foo.new
291
+ ].each do |v|
292
+ print " 1. "
293
+ p v
294
+ out = PHP.serialize(v)
295
+
296
+ tmp = Tempfile.new('php')
297
+ tmp.print(out)
298
+ tmp.close
299
+
300
+ puts " >> #{out}"
301
+ ret = `php -r 'echo serialize(unserialize(join("\n", file("#{tmp.path}"))));'`
302
+ puts " << #{ret}"
303
+ print " 2. "
304
+ p PHP.unserialize(ret)
305
+ puts " == #{out == ret ? 'Perfect!' : 'Differ!'}", ''
306
+ tmp.close!
307
+ end
308
+ end
309
+
@@ -0,0 +1,6 @@
1
+ module SMKLib
2
+ class Railtie < Rails::Railtie
3
+ # rake_tasks do { require ... }
4
+ end
5
+ end
6
+
@@ -0,0 +1,198 @@
1
+ ################################################
2
+ #
3
+ # Usage:
4
+ #
5
+ # somekool_scaffold :agents, :ignore_columns => [:last_edit_at], :foreign_keys => [{:key => :sub_of_agent_id, :model => Agent, :title => _('Sub of')}], :css_class => :table_list_view
6
+ #
7
+ #
8
+ #
9
+ #
10
+ #
11
+ ################################################
12
+
13
+ module ActionView
14
+ module Helpers
15
+ module JavaScriptMacrosHelper
16
+ def in_place_collection_dropdown(field_id, options = {})
17
+ function = "new Ajax.InPlaceCollectionEditor("
18
+ function << "'#{field_id}', "
19
+ function << "'#{url_for(options[:url])}'"
20
+
21
+ js_options = {}
22
+ js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
23
+ js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
24
+ js_options['loadingText'] = %('#{options[:loading_text]}') if options[:loading_text]
25
+ #js_options['rows'] = options[:rows] if options[:rows]
26
+ #js_options['cols'] = options[:cols] if options[:cols]
27
+ #js_options['size'] = options[:size] if options[:size]
28
+ js_options['collection'] = options[:collection] if options[:collection]
29
+ js_options['value'] = options[:value] if options[:value]
30
+ js_options['externalControl'] = "'#{options[:external_control]}'" if options[:external_control]
31
+ js_options['loadTextURL'] = "'#{url_for(options[:load_text_url])}'" if options[:load_text_url]
32
+ js_options['ajaxOptions'] = options[:options] if options[:options]
33
+ js_options['evalScripts'] = options[:script] if options[:script]
34
+ js_options['callback'] = "function(form) { return #{options[:with]} }" if options[:with]
35
+ function << (', ' + options_for_javascript(js_options)) unless js_options.empty?
36
+
37
+ function << ')'
38
+
39
+ javascript_tag(function)
40
+ end
41
+
42
+
43
+ def in_place_editor_dropdown(object, method, tag_options = {}, in_place_editor_options = {})
44
+ tag = ::ActionView::Helpers::InstanceTag.new(object, method, self)
45
+ raise tag.inspect if tag_options[:crash]
46
+ front_val = tag_options.delete(:front_val)
47
+ tag_options = {:tag => "span", :id => "#{object}_#{method}_#{tag.object.id}_in_place_editor", :class => "in_place_editor_field"}.merge!(tag_options)
48
+ in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{object}_#{method}", :id => tag.object.id })
49
+ (front_val.to_s.empty? ? tag.to_content_tag(tag_options.delete(:tag), tag_options) : tag.content_tag(tag_options.delete(:tag), front_val, tag_options)) +
50
+ in_place_collection_dropdown(tag_options[:id], in_place_editor_options)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ class ActionController::Base
57
+ # Get a metaclass for this class
58
+ #def self.metaclass; class << self; self; end; end
59
+
60
+ def self.somekool_scaffold(model, options = {})
61
+ #raise Object.const_get(model.to_s.singularize.capitalize).inspect
62
+ instance_eval do
63
+ # required for in_place_editor_field
64
+ include ActionView::Helpers::TagHelper
65
+ include ActionView::Helpers::JavaScriptHelper
66
+ include ActionView::Helpers::JavaScriptMacrosHelper
67
+
68
+ # get model object
69
+ model_sym = model.to_s.singularize
70
+ object_model = Object.const_get(model_sym.capitalize)
71
+ model_sym = model_sym.downcase.to_sym
72
+
73
+ # call in_plate_edit_for all columns
74
+ columns = object_model.content_columns.collect do |c|
75
+ if options[:ignore_columns].include? c.name.to_sym
76
+ nil
77
+ else
78
+ in_place_edit_for model_sym, c.name.to_sym
79
+ c
80
+ end
81
+ end.compact
82
+ options[:foreign_keys].each do |fk|
83
+ in_place_edit_for model_sym, fk[:key].to_sym
84
+
85
+ define_method("set_#{model_sym}_#{fk[:key]}".to_sym) do
86
+ a = object_model.find(params[:id])
87
+ a[fk[:key].to_sym] = params[:value]
88
+ a.save
89
+ render :text => (a[fk[:key].to_sym].nil? ? _('NULL') : "#{a[fk[:key].to_sym]} - #{fk[:model].find(a[fk[:key].to_sym]).to_s}".nbsp)
90
+ end
91
+ end
92
+
93
+ # simple action index
94
+ define_method(:index) do
95
+ redirect_to :action => 'list'
96
+ end
97
+
98
+ # main action
99
+ define_method(:list) do
100
+ xml = Builder::XmlMarkup.new(:indent=>2)
101
+ xml.a(:class => "#{model_sym}_add_link", :href => "/#{model_sym.to_s.pluralize}/add") do
102
+ xml.img(:title => 'add_button', :alt => 'add_button', :border => 0, :src => '/smklib/images/add.png')
103
+ xml << _('Add new %s') % model_sym.to_s
104
+ end
105
+ xml.table(:cellpadding => 0, :cellspacing => 0, :border => 0, :class => "somekool_scaffold list #{model} #{options[:css_class]}") do
106
+ xml.tr do
107
+ xml.th _('ID') unless options[:no_id]
108
+ columns.each do |c|
109
+ xml.th { xml << c.human_name.to_s.nbsp}
110
+ end
111
+ options[:foreign_keys].each do |fk|
112
+ fk[:collection] = ([[nil, _('NULL')]] + fk[:model].find_all.collect { |item| [item.id, item.to_s] }).inspect.gsub(/nil/, 'null')
113
+ xml.th fk[:title]
114
+ end if options[:foreign_keys]
115
+ xml.th { xml << _('Delete') } unless options[:no_delete]
116
+ end
117
+ object_model.find_all.each_with_index do |row, row_index|
118
+ instance_variable_set("@#{model_sym}", row)
119
+ xml.tr(:class => row_index % 2 == 0 ? 'even' : 'odd') do
120
+ xml.td do
121
+ xml << instance_variable_get("@#{model_sym}")[:id].to_s
122
+ end unless options[:no_id]
123
+ columns.each do |c|
124
+ xml.td do
125
+ #xml << row.send(c.name)
126
+ instance_variable_get("@#{model_sym}")[c.name.to_sym] = " " if instance_variable_get("@#{model_sym}")[c.name.to_sym].to_s.empty?
127
+ xml << in_place_editor_field(model_sym, c.name)
128
+ end
129
+ end
130
+ options[:foreign_keys].each do |fk|
131
+ begin
132
+ front_val = "#{row[fk[:key]]} - #{fk[:model].find(row[fk[:key]]).to_s}".nbsp
133
+ rescue
134
+ front_val = _('NULL')
135
+ end
136
+ xml.td do
137
+ xml << in_place_editor_dropdown(model_sym, fk[:key], {:front_val => front_val}, {:value => row[fk[:key]], :collection => fk[:collection]})
138
+ end
139
+ #xml.td fk[:model].find(:first, :conditions => ["#{fk[:key]} = ?", row[fk[:key]]]).inspect ## same
140
+ end if options[:foreign_keys]
141
+ xml.td do
142
+ xml.a(:class => "#{model_sym}_delete_link", :href => "/#{model_sym.to_s.pluralize}/destroy/#{row.id}") do
143
+ xml.img(:title => 'delete_button', :alt => 'delete_button', :border => 0, :src => '/smklib/images/trashcan_empty.png')
144
+ end
145
+ end unless options[:no_delete]
146
+ end
147
+ end
148
+ end
149
+ render :text => xml.target!, :layout => 'main'
150
+ end
151
+ define_method(:add) do
152
+ xml = Builder::XmlMarkup.new(:indent=>2)
153
+ xml.form(:action => "/#{model_sym.to_s.pluralize}/create") do
154
+ xml.table(:cellpadding => 0, :cellspacing => 0, :border => 0, :class => "somekool_scaffold add #{model} #{options[:css_class]}") do
155
+ columns.each do |c|
156
+ xml.tr do
157
+ xml.th { xml << c.human_name.to_s.nbsp}
158
+ xml.td { xml.input(:type => 'text', :name => "#{model_sym}[#{c.name}]") }
159
+ end
160
+ end
161
+ options[:foreign_keys].each do |fk|
162
+ fk[:collection] = ([[nil, _('NULL')]] + fk[:model].find_all.collect { |item| [item.id, item.to_s] })
163
+ xml.tr do
164
+ xml.th fk[:title]
165
+ xml.td do
166
+ xml.select(:name => "#{model_sym}[#{fk[:key]}]") do
167
+ fk[:collection].each do |opt|
168
+ xml.option(:value => opt[0]) { xml << opt[1] }
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end if options[:foreign_keys]
174
+ xml.tr do
175
+ xml.td(:colspan => 2) { xml.input(:type => 'submit', :value => _('Add'))}
176
+ end
177
+ end
178
+ end
179
+ render :text => xml.target!, :layout => 'main'
180
+ end
181
+ define_method(:create) do
182
+ object_model.create(params[model_sym])
183
+ flash[:notice] = _('New %s created') % model_sym.to_s
184
+ redirect_to_path "/#{model_sym.to_s.pluralize}/list"
185
+ end
186
+ #define_method(:edit) do
187
+ #end
188
+ #define_method(:update) do
189
+ #end
190
+ define_method(:destroy) do
191
+ object_model.find(params[:id]).destroy
192
+ flash[:notice] = _('%s %s has been destroyed') % [model_sym.to_s, params[:id].to_s]
193
+ redirect_to_path "/#{model_sym.to_s.pluralize}/list"
194
+ end
195
+ end
196
+ end
197
+ end
198
+