hobo-jquery 1.3.0pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.gitmodules +3 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.markdown +54 -0
  4. data/TODO.markdown +0 -0
  5. data/hobo-jquery.gemspec +9 -0
  6. data/jquery/javascripts/jquery-1.5.2.min.js +16 -0
  7. data/jquery/javascripts/jquery-ui-1.8.11.custom.min.js +783 -0
  8. data/jquery/stylesheets/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  9. data/jquery/stylesheets/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  10. data/jquery/stylesheets/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  11. data/jquery/stylesheets/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  12. data/jquery/stylesheets/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  13. data/jquery/stylesheets/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  14. data/jquery/stylesheets/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  15. data/jquery/stylesheets/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  16. data/jquery/stylesheets/smoothness/images/ui-icons_222222_256x240.png +0 -0
  17. data/jquery/stylesheets/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  18. data/jquery/stylesheets/smoothness/images/ui-icons_454545_256x240.png +0 -0
  19. data/jquery/stylesheets/smoothness/images/ui-icons_888888_256x240.png +0 -0
  20. data/jquery/stylesheets/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  21. data/jquery/stylesheets/smoothness/jquery-ui-1.8.11.custom.css +573 -0
  22. data/lib/doc.dryml +75 -0
  23. data/lib/doc.rb +15 -0
  24. data/lib/doc_generator.dryml +79 -0
  25. data/lib/generators/hobo_jquery/install_generator.rb +23 -0
  26. data/lib/hobo-jquery.rb +6 -0
  27. data/lib/hobo-jquery/railtie.rb +9 -0
  28. data/lib/html2markdown.pl +7 -0
  29. data/lib/render.rake +39 -0
  30. data/lib/tasks/hobo-contrib.rake +2 -0
  31. data/public/javascripts/event.simulate.js +64 -0
  32. data/public/javascripts/hobo-jquery.js +663 -0
  33. data/public/stylesheets/hobo-jquery.css +16 -0
  34. data/rails/init.rb +0 -0
  35. data/taglibs/hobo-jquery.dryml +613 -0
  36. data/update-docs.sh +16 -0
  37. metadata +89 -0
@@ -0,0 +1,16 @@
1
+
2
+ // stolen from clean.css
3
+
4
+ ul.hjq-input-many {list-style-type: none;}
5
+
6
+ ul.hjq-input-many > li { overflow:hidden; zoom:1;}
7
+ ul.hjq-input-many .item {float:left;}
8
+ ul.hjq-input-many div.buttons {float:left; margin-left:10px;}
9
+
10
+ li.input-many-template { display:none; }
11
+
12
+ .hjq-combobox .ui-button { margin-left: -1px; }
13
+ .hjq-combobox .ui-button-icon-only .ui-button-text { padding: 0; }
14
+ .hjq-combobox button.ui-button-icon-only { width: 20px; }
15
+ .hjq-combobox .ui-autocomplete-input { margin-right: 0; }
16
+ .hjq-combobox {white-space: nowrap;}
File without changes
@@ -0,0 +1,613 @@
1
+ <!--
2
+
3
+ This is the jQuery plugin for Hobo. This plugin provides the jQuery UI widgets in a Hobo friendly manner, provides useful tags and functions for jQuery javascript coding, and provides additional composite tags.
4
+
5
+ To use, see the [installation instructions](http://cookbook.hobocentral.net/plugins/hobo-jquery) [(alternate link)](README.html)
6
+
7
+ The jQuery UI tags support all of the [options that the corresponding jQuery UI widgets provide](http://docs.jquery.com/UI). For example:
8
+
9
+ <hjq-datepicker dateFormat="yy-mm-dd" />
10
+
11
+ Options that expect a type other than string can be provided by passing a ruby object:
12
+
13
+ <hjq-datepicker dayNamesMin="&['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa']" />
14
+
15
+ Events are also supported. Pass in a global Javascript function name:
16
+
17
+ <hjq-datepicker onSelect="hjq.util.log" />
18
+
19
+ -->
20
+
21
+ <!--
22
+ This is Hobo jQuery's standard tag annotation method. This annotation is automatically added to Hobo jQuery tags, but you may wish to use it in your own tag definitions.
23
+
24
+ The current implementation uses HTML comments for annotation. This may change in the future, if somebody can suggest a better mechanism. [Here is a good discussion of the alternatives.](http://www.1729.com/blog/HtmlAnnotations.html). Two more possibilities are http://www.w3.org/TR/xhtml-rdfa-primer and http://ejohn.org/blog/html-5-data-attributes/.
25
+
26
+ To use it, invoke this tag directly before the tag to annotate. To get the annotations, call hjq.getAnnotations with _this_ set to the element that has been annotated. For example:
27
+
28
+ <def tag="mytag">
29
+ <annotate-tag option_a="42"/>
30
+ <div class="hjq-annotated">
31
+ ...
32
+ </div>
33
+ </def>
34
+
35
+ jQuery(this).find('.hjq-annotated').each(function() {
36
+ var annotations = hjq.getAnnotations.call(this);
37
+ ...
38
+ });
39
+ -->
40
+ <def tag="annotate-tag">
41
+ <%= "<!-- json_annotation (#{attributes.to_json}); -->".html_safe %>
42
+ </def>
43
+
44
+ <!--
45
+ These are the Hobo jQuery and jQuery assets required by Hobo jQuery. You will probably wish to add this to your page definition:
46
+
47
+ <extend tag="page">
48
+ <old-page merge>
49
+ <custom-scripts:>
50
+ <hjq-assets/>
51
+ </custom-scripts>
52
+ </old-page>
53
+ </extend>
54
+ -->
55
+ <def tag="hjq-assets">
56
+ <jquery-assets/>
57
+ <hobo-jquery-assets/>
58
+ </def>
59
+
60
+ <def tag="jquery-assets">
61
+ <stylesheet name='smoothness/jquery-ui-1.8.11.custom.css' />
62
+ <javascript name="jquery-1.5.2.min.js" />
63
+ <javascript name="jquery-ui-1.8.11.custom.min.js" />
64
+ </def>
65
+
66
+ <def tag="hobo-jquery-assets">
67
+ <stylesheet name='hobo-jquery.css' />
68
+ <javascript name="hobo-jquery.js" />
69
+ <javascript name="event.simulate.js" />
70
+ <script type="text/javascript">
71
+ jQuery.noConflict();
72
+ jQuery(document).ready(function() {
73
+ hjq.initialize.call(document);
74
+ });
75
+ </script>
76
+ </def>
77
+
78
+
79
+ <!--
80
+ This is the [jQuery-UI datepicker](http://docs.jquery.com/UI/Datepicker). All options and events are supported.
81
+
82
+ It's probably easiest to add this to your application.dryml:
83
+
84
+ <def tag="input" for="Date">
85
+ <hjq-datepicker dateFormat="yy-mm-dd" merge />
86
+ </def>
87
+
88
+ It's probably useful to set global options such as dateFormat in application.dryml and set local options such as yearRange in the tag invocation:
89
+
90
+ <input:birthdate yearRange="1900:#{Date.today.year}" />
91
+
92
+ -->
93
+ <def attrs="name" tag="hjq-datepicker">
94
+ <%
95
+ options, attrs = attributes.partition_hash(['altField', 'altFormat', 'appendText', 'buttonImage', 'buttonImageOnly', 'buttonText', 'changeMonth', 'changeYear', 'closeText', 'constrainInput', 'currentText', 'dateFormat', 'dayNames', 'dayNamesMin', 'dayNamesShort', 'defaultDate', 'duration', 'firstDay', 'gotoCurrent', 'hideIfNoPrevNext', 'isRTL', 'maxDate', 'minDate', 'monthNames', 'monthNamesShort', 'navigationAsDateFormat', 'nextText', 'numberOfMonths', 'prevText', 'selectOtherMonths','shortYearCutoff', 'showAnim', 'showButtonPanel', 'showCurrentAtPos', 'showMonthAfterYear', 'showOn', 'showOptions', 'showOtherMonths', 'stepMonths', 'yearRange'])
96
+ events, html_attrs = attrs.partition_hash(['beforeShow', 'beforeShowDay', 'onChangeMonthYear', 'onClose', 'onSelect'])
97
+ %>
98
+ <annotate-tag tag="hjq-datepicker" init="hjq.datepicker.init" options="&options" events="&events" />
99
+ <% html_attrs = add_classes(html_attrs, "hjq-annotated"); %>
100
+ <%= text_field_tag(name, this, html_attrs) %>
101
+ </def>
102
+
103
+
104
+ <!--
105
+ This is the [jQuery-UI autocompleter](http://jqueryui.com/demos/autocomplete/). All options and events provided by the jQuery autocompleter are supported in this Hobo tag. Refer to the jquery autocomplete documentation for more details.
106
+
107
+ This tag works very similarly to the `name-one` tag used in Hobo. The main reason I use `hjq-autocomplete` is that the scriptaculous widget used by Hobo for `name-one` does not work well in Internet Explorer: https://prototype.lighthouseapp.com/projects/8887/tickets/33
108
+
109
+ If you do not provide a `source` option, this tag will essentially provide a default similar to this:
110
+
111
+ <hjq-autocomplete:project source="&Project.find(:all, :limit => 5000).map {|p| p.name}" />
112
+
113
+ This will provide a local autocomplete, putting all possible options into your HTML. This is useful when you have too many options to make a `select-one` infeasible, but will overload your server and crash your browser if you have millions of options.
114
+
115
+ To do a server side autocomplete, use a path for your source option:
116
+
117
+ <hjq-autocomplete:project source="&query_projects_path" delay="500" minLength="3" />
118
+
119
+ And add something like this to your controller:
120
+
121
+ index_action :query do
122
+ render :json => Project.name_contains(params[:term]).limit(100).*.name
123
+ end
124
+ -->
125
+ <def tag="hjq-autocomplete">
126
+ <%
127
+ options, attrs = attributes.partition_hash(['disabled', 'appendTo', 'autoFocus', 'delay', 'minLength', 'position', 'source'])
128
+ events, html_attrs = attrs.partition_hash(['create', 'search', 'open', 'focus', 'select', 'close', 'change'])
129
+ options["source"] ||= begin
130
+ complete_target = this_field_reflection.klass
131
+ complete_target.find(:all, :limit => 5000).*.send(complete_target.name_attribute)
132
+ end
133
+
134
+ %>
135
+ <annotate-tag tag="hjq-autocomplete" init="hjq.autocomplete.init" options="&options" events="&events" />
136
+ <% html_attrs = add_classes(html_attrs, "hjq-annotated")
137
+ html_attrs["name"] ||= param_name_for_this
138
+ html_attrs["value"] ||= name(:no_wrapper => true, :if_present => true)
139
+ %>
140
+ <%= text_field_tag(name, this, html_attrs) %>
141
+
142
+ </def>
143
+
144
+
145
+ <!-- Creates an enhanced version of the [jQuery-UI demo combobox](http://jqueryui.com/demos/autocomplete/#combobox). This will turn any select (even a select-many) into a combobox.
146
+
147
+ NOTE: backwards compatibility with the Hobo&Rails prototype/lowpro handlers requires event.simulate.js. You may need to rerun your hobo_query:install generator to copy this file into your application.
148
+
149
+ Standard usage:
150
+
151
+ <hjq-combobox>
152
+ <select .../>
153
+ </hjq-combobox>
154
+
155
+ If you don't supply the select, it will use Hobo's `<input>` tag. This is usually the right default.
156
+
157
+ You can tell Hobo to always use a combobox for select-one, select-many, select-input and/or select-menu. Notice that we also changed the limit - 100 is the default, which makes sense for a select, but not for a combobox.
158
+
159
+ <extend tag="select-one">
160
+ <hjq-combobox tag="select-one">
161
+ <old-select-one limit="5000" merge/>
162
+ </hjq-combobox>
163
+ </extend>
164
+
165
+ To make the behavior more like the demo's, turn off autoFill and clearButton:
166
+
167
+ <hjq-combobox autoFill="&false" clearButton="&false"/>
168
+
169
+ Combobox is a new control that is built on autocomplete. The autocomplete options may not be used directly. If you need any of them let me know, they should not be too hard to add if you volunteer to test them.
170
+
171
+ ### Attributes
172
+
173
+ - `autoFill` (default: true): select first value rather than clearing if there's a match
174
+
175
+ - `clearButton` (default: true): add a "clear" button
176
+
177
+ - `adjustWidth` (default: true): if true, will set the autocomplete width the same as the old select. (requires jQuery 1.4.4 to work on IE8)
178
+
179
+ - uiStyle (default: false): if true, will add classes so that the autocomplete input takes a jQuery-UI style. If false, the standard Hobo style is used.
180
+
181
+ ### Events
182
+
183
+ - `selected` (default: `$(this).simulate('change');`): this is a jQuery-UI event handler. The default value for this option fires the `onchange` handler on the underlying `select` in a manner compatible with both prototype.js and jQuery. For this to work, event.simulate.js must be loaded, and jQuery must be put into noConflict mode. hobo-jquery should be doing both of these for you if you are using it as recommended in the README.
184
+
185
+ -->
186
+
187
+ <def tag="hjq-combobox" options="tag">
188
+ <%
189
+ options, attrs = attributes.partition_hash(['autoFill', 'clearButton', 'adjustWidth', 'uiStyle'])
190
+ events, html_attrs = attrs.partition_hash(['selected'])
191
+ %>
192
+ <annotate-tag tag="hjq-combobox" init="hjq.combobox.init" options="&options" events="&events"/>
193
+ <% tag ||= 'input' %>
194
+ <span class="hjq-combobox hjq-annotated" param="default">
195
+ <input merge param />
196
+ </span>
197
+ </def>
198
+
199
+
200
+ <!-- Creates a sub-section of the form which the user can repeat using (+) and (-) buttons, in order to allow an entire `has_many` collection to be created/edited in a single form.
201
+
202
+ Most of the enhancements from hjq-input-many have been folded back into hjq-input-many, but there are a few remaining differences:
203
+
204
+ - it supports delayed initialization, required for some Javascript elements like the jQuery UI datepicker
205
+ - the name of the main parameter is `item` rather than `default`
206
+ - hjq-input-many allows you to provide javascript callbacks. input-many fires
207
+ rapid:add, rapid:change and rapid:remove events that can be hooked.
208
+
209
+ ### Example
210
+
211
+ Say you are creating a new `Category` in your online shop, and you want to create some initial products *in the same form*, you can add the following to your form:
212
+
213
+ <hjq-input-many:products fields="name, price" />
214
+
215
+ You'll often want to provide the `item` parameter:
216
+
217
+ <hjq-input-many:products><item:><field-list fields="name, price" /></item:></hjq-input-many>
218
+
219
+ A fully worked up example of nested hjq-input-many's may be found in [agility/jquery-test](http://github.com/tablatom/agility/blob/jquery-test/app/views/projects/nested_has_many_test.dryml)
220
+
221
+ ### Attributes
222
+
223
+ - `minimum`: the minimum number of items in the collection. Currently only '0' and '1' are supported values. The default is '0'.
224
+
225
+ - `fields`: passed down to the `field-list` tag in the default `item`.
226
+
227
+ - `template`: the default values for new items. Normally this functionality is better provided by Model.new, but it's here if you need it.
228
+
229
+ - `add-hook`: a javascript function to call after an item has been added. The context will be set to the new item.
230
+
231
+ - `remove-hook`: a javascript function to call before an item is removed. The context will be set to the item. If the hook returns false, the remove is cancelled.
232
+
233
+
234
+ -->
235
+ <def tag="hjq-input-many" attrs="minimum, fields, template, add-hook, remove-hook" polymorphic >
236
+ <%
237
+ # helper function to create id's on buttons to facilitate testing
238
+ def underize(s)
239
+ s.gsub(/\[/,"_").gsub(/\]/,"")
240
+ end
241
+ %>
242
+ <set empty="&this.empty?"/>
243
+ <annotate-tag tag="hjq-input-many" init="hjq.input_many.init" merge-attrs="minimum, add-hook, remove-hook" />
244
+ <ul class="hjq-input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this} hjq-annotated" merge-attrs="&attributes - attrs_for(:hjq_input_many_item)">
245
+ <% template ||= this.try.new_candidate || this.member_class.new %>
246
+ <% minimum ||= 0 ; minimum = minimum.to_i %>
247
+ <fake-field-context fake-field="-1" context="&template">
248
+ <li class="input-many-template" id="#{param_name_for_this}">
249
+ <hjq-input-many-item param="item" merge-attrs="fields" />
250
+ <div class="buttons">
251
+ <button param="remove-item" id="#{underize param_name_for_this}_remove">-</button>
252
+ <button param="add-item" id="#{underize param_name_for_this}_add">+</button>
253
+ </div>
254
+ </li>
255
+ </fake-field-context>
256
+ <li class="empty #{'hidden' unless this.empty? and minimum==0}" id="#{param_name_for_this}[-1]_empty">
257
+ <!-- HACK way to signal an empty collection to the controller -->
258
+ <input type="hidden" class="empty-input" id="#{param_name_for_this}" name="#{param_name_for_this}" value="" disabled="&(!this.empty? || minimum>0)" />
259
+ <fake-field-context fake-field="-1" context="&template">
260
+ <div param="empty-message">
261
+ <ht key="#{this.class.to_s.underscore}.collection.empty_message">
262
+ No <%= this.class.name.titleize.downcase.pluralize %>.
263
+ </ht>
264
+ </div>
265
+ <div class="buttons">
266
+ <button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
267
+ <button param="add-item" id="#{underize param_name_for_this}_add">+</button>
268
+ </div>
269
+ </fake-field-context>
270
+ </li>
271
+ <fake-field-context fake-field="0" context="&template">
272
+ <li if="&(this_parent.empty? && minimum>0)" id="#{param_name_for_this}">
273
+ <hjq-input-many-item param="item" merge-attrs="fields" />
274
+ <div class="buttons">
275
+ <button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
276
+ <button param="add-item" id="#{underize param_name_for_this}_add">+</button>
277
+ </div>
278
+ </li>
279
+ </fake-field-context>
280
+ <li repeat class="#{'record-with-errors' unless this.errors.empty?}" id="#{param_name_for_this}">
281
+ <error-messages without-heading class="sub-record"/>
282
+ <hidden-id-field/>
283
+ <hjq-input-many-item param="item" merge-attrs="fields" />
284
+ <div class="buttons">
285
+ <button param="remove-item" class="#{'hidden' if this_parent.length<=minimum}" id="#{underize param_name_for_this}_remove">-</button>
286
+ <button param="add-item" class="#{'hidden' if not last_item?}" id="#{underize param_name_for_this}_add">+</button>
287
+ </div>
288
+ </li>
289
+ </ul>
290
+ </def>
291
+
292
+ <!--
293
+ This is the default item used by `<hjq-input-many>`. Redefine or extend if desired.
294
+ -->
295
+ <def tag="hjq-input-many-item" attrs="fields">
296
+ <card param="default">
297
+ <header: replace />
298
+ <body:><field-list merge-attrs="fields" /></body:>
299
+ </card>
300
+ </def>
301
+
302
+ <!--
303
+ `<formlet>` works like an AJAX `<form>` without actually being an AJAX `<form>`. This is useful in scenarios where forms aren't allowed or are problematic, like inside of another form.
304
+
305
+ ### Attributes
306
+
307
+ - standard Hobo HTML attributes (id, part, et cetera). All supported.
308
+
309
+ - standard Hobo AJAX attributes: (see [the cookbook](http://cookbook.hobocentral.net/api_taglibs/rapid_forms) )
310
+
311
+ - `update`: (optional). The DOM ID's of parts to update. Note that you get an AJAX formlet whether this is provided or not.
312
+
313
+ - `message`: the spinner message
314
+
315
+ - `spinner-next-to`: DOM id of an element to position the ajax progress spinner next to.
316
+
317
+ - `before`, `success`, `error`, `complete`: These callbacks are slightly different from the standard form callbacks. They are called with "this" set to the formlet. Also, you may pass in a function name rather than a javascript fragment. If the before callback returns false, it will cancel the submission.
318
+
319
+ - `confirm`: a message to be displayed in a JavaScript confirm dialog. By default there is no confirm dialog
320
+
321
+ - not supported: `type, script, params, reset-form, refocus-form, result-update`. None of these are particularly hard to support. Email or post a message to the list if you need them.
322
+
323
+ - standard Hobo Form attributes (all supported):
324
+
325
+ - `hidden-fields`
326
+
327
+ - `action`
328
+
329
+ - `method`
330
+
331
+ - `web-method`
332
+
333
+ - `lifecycle`
334
+
335
+ - `owner`
336
+ -->
337
+ <def tag="formlet" polymorphic attrs="update, hidden-fields, action, method, web-method, lifecycle, owner">
338
+ <%
339
+ #FIXME: code very similar to <form>. refactor out common code.
340
+
341
+ ajax_attrs, html_attrs = attributes.partition_hash(Hobo::Rapid::Helper::AJAX_ATTRS)
342
+
343
+ ajax_options = {}
344
+
345
+ new_record = this.try.new_record?
346
+
347
+ method = if method.nil?
348
+ (action || web_method || new_record) ? "post" : "put"
349
+ else
350
+ method.downcase
351
+ end
352
+
353
+ ajax_options[:url] = action || begin
354
+ target = if owner
355
+ collection_name = this.class.reverse_reflection(owner).name
356
+ this.send(owner).send(collection_name)
357
+ else
358
+ this
359
+ end
360
+ action = web_method || lifecycle
361
+ object_url(target, action, :method => "post")
362
+ end
363
+
364
+ if action.nil? && (ajax_options[:url].nil? ||
365
+ (lifecycle.nil? && new_record && !this.creatable_by?(current_user)) ||
366
+ (lifecycle.nil? && !new_record && !can_edit?))
367
+ Dryml.last_if = false
368
+ logger.info("blank!!!")
369
+ ""
370
+ else
371
+ if method == "put"
372
+ # browsers don't support put -- use post and add the Rails _method hack
373
+ http_method_hidden = hidden_field_tag("_method", "PUT")
374
+ ajax_options[:type] = "post"
375
+ else
376
+ ajax_options[:type] = method
377
+ end
378
+
379
+ ajax_attrs[:update] = if update
380
+ update.split(/,\s*/)
381
+ else
382
+ []
383
+ end
384
+
385
+ hiddens = nil
386
+ body = with_form_context do
387
+ # It is important to evaluate parameters.default first, in order to populate scope.form_field_names
388
+ b = parameters.default
389
+ hiddens = self.hidden_fields(:fields => hidden_fields) if new_record
390
+ b
391
+ end
392
+
393
+ auth_token = if method.nil? || method == 'get' || !protect_against_forgery?
394
+ ''
395
+ else
396
+ element(:input, {:type => "hidden",
397
+ :name => request_forgery_protection_token.to_s,
398
+ :value => form_authenticity_token}, nil, true, true)
399
+ end
400
+
401
+ unless method == "get"
402
+ page_path = if (request.post? || request.put?) && params[:page_path]
403
+ params[:page_path]
404
+ else
405
+ request.fullpath
406
+ end
407
+ page_path_hidden = hidden_field_tag("page_path", page_path)
408
+ end
409
+
410
+ hiddens_div = element(:div, {:class => "hidden-fields"}, [http_method_hidden, page_path_hidden, auth_token, hiddens].safe_join)
411
+
412
+ body = [hiddens_div, body].safe_join
413
+
414
+ if action.nil? # don't add automatic css classes if the action was specified
415
+ if web_method
416
+ add_classes!(html_attrs, "#{type_id.dasherize}-#{web_method}-form")
417
+ else
418
+ add_classes!(html_attrs, "#{'new ' if new_record}#{type_id.dasherize}")
419
+ end
420
+ end
421
+
422
+ Dryml.last_if = true
423
+ end
424
+
425
+ add_classes!(html_attrs, "formlet")
426
+ %>
427
+ <annotate-tag tag="formlet" ajax-attrs="&ajax_attrs" ajax-options="&ajax_options" />
428
+ <%= element("div", html_attrs, body) %>
429
+ </def>
430
+
431
+ <!--
432
+ Submit button for use with `<formlet>`. Submit won't work, because `<formlet>` isn't a form.
433
+ -->
434
+ <def tag="formlet-submit" attrs="label, image, method">
435
+ <% label ||= 'submit' %>
436
+ <if test='&image'>
437
+ <input class='image-button formlet-submit-button' type='image' src='&image' onClick='hjq.formlet.submit.call(this)' merge-attrs />
438
+ </if>
439
+ <else>
440
+ <input class='button formlet-submit-button' type='button' value='#{label}' onClick='hjq.formlet.submit.call(this)' merge-attrs />
441
+ </else>
442
+ </def>
443
+
444
+ <!--
445
+ Submit button for use with the `<form>`. This uses hjq.form.submit to
446
+ submit the form via jquery form plugin ajax, rather than using the
447
+ standard HTTP or Hobo form submission mechanisms. The main advantage
448
+ this has is that the jquery form plugin supports ajax submission of
449
+ attachments.
450
+
451
+ You must have the jquery form plugin installed. It is not installed
452
+ automatically by hobo-jquery.
453
+
454
+ FIXME: this function HARD CODES it's ajax options, in particular
455
+ update="attachments-div". It does not (yet) get the parameters from
456
+ the form, nor does it get them through the standard hobo-jquery
457
+ mechanism. update in hobo-jquery.js.
458
+ -->
459
+ <def tag="hjq-submit" attrs="label, image, method">
460
+ <% label ||= 'submit' %>
461
+ <if test='&image'>
462
+ <input class='image-button hjq-form-submit-button' type='image' src='&image' onClick='hjq.form.submit.call(this)' merge-attrs />
463
+ </if>
464
+ <else>
465
+ <input class='button hjq-form-submit-button' type='button' value='#{label}' onClick='hjq.form.submit.call(this)' merge-attrs />
466
+ </else>
467
+ </def>
468
+
469
+ <!--
470
+ The JQuery UI [dialog](http://jqueryui.com/demos/dialog/). All options and methods are supported.
471
+
472
+ The default for [autoOpen](http://jqueryui.com/demos/dialog/#option-autoOpen) has been changed to `"&false"`. To display the dialog set autoOpen to `"&true"`, use a `<hjq-dialog-open-button>`, use the [jQuery dialog open method](http://jqueryui.com/demos/dialog/#method-open), or use an `<hjq-dialog-and-button>` instead.
473
+
474
+ The default for [position](http://jqueryui.com/demos/dialog/#option-position) is also set so that the dialog appears where it is defined.
475
+
476
+ The [buttons](http://jqueryui.com/demos/dialog/#option-buttons) option has been changed slightly. Instead of an Hash, it expects a list of pairs. (Unlike Javascript Objects, Ruby Hash's do not preserve order). Because of quoting issues, it's usually easier to assign the list to a variable and then include the list:
477
+
478
+ <% buttons = [ ["cancel", "jQuery(this).dialog('close')"] ] %>
479
+ <hjq-dialog buttons="&buttons" />
480
+
481
+ or
482
+
483
+ <hjq-dialog id="story-dialog" buttons='&{"cancel" => "jQuery(this).dialog(\"close\")"}' />
484
+
485
+ or
486
+
487
+ <hjq-dialog id="story-dialog" buttons='&{"cancel" => "hjq.dialog.close"}' />
488
+
489
+ Because Ruby Hash's do not preserve order, you may use a list of pairs instead of a Hash.
490
+
491
+ JQuery UI is sometimes picky about types. For instance, the width option must be a number, not a string. By default Hobo options are strings, but you can easily send a number by using the ampersand to switch to ruby mode:
492
+
493
+ <hjq-dialog width="&640"/>
494
+
495
+ The following functions are predefined for use in dialog buttons:
496
+
497
+ * `hjq.dialog.close`: close the dialog box
498
+ * `hjq.dialog.submit_formlet`: submits all formlets inside the dialog
499
+ * `hjq.dialog.submit_formlet_and_close`: submits any enclosed formlets, then closes the dialog.
500
+ -->
501
+ <def tag="hjq-dialog" attrs="buttons">
502
+ <% #FIXME: dry these names
503
+ options, attrs = attributes.partition_hash(["autoOpen", "bgiframe", "closeOnEscape", "dialogClass", "draggable", "height", "hide", "maxHeight", "maxWidth", "minWidth", "modal", "position", "resizable", "show", "stack", "title", "width", "zIndex"])
504
+ events, html_attrs = attrs.partition_hash(["beforeclose", "open", "focus", "dragStart", "drag", "dragStop", "resizeStart", "resize", "resizeStop", "close"])
505
+ options["autoOpen"]=false unless options.has_key?("autoOpen")
506
+ %>
507
+ <span class="hjq-dialog-position"></span>
508
+ <annotate-tag tag="hjq-dialog" init="hjq.dialog.init" options="&options" events="&events" buttons="&buttons" />
509
+ <% html_attrs = add_classes(html_attrs, "hjq-annotated hidden hjq-dialog"); %>
510
+ <div merge-attrs="&html_attrs" param="default" />
511
+ </def>
512
+
513
+ <!--
514
+ When pressed, will open a `<hjq-dialog>`.
515
+
516
+ ### Attributes
517
+ * `dialog` (required):a CSS selector (AKA jQuery selector) to locate the dialog in the DOM. Remember, if you're passing an ID, place a '#' in front of the ID.
518
+ * supports all `<button>` attributes except for `onclick`
519
+
520
+ ### Example
521
+ <hjq-dialog id="hello-dialog">Hello!</hjq-dialog>
522
+ <hjq-dialog-open-button dialog="#hello-dialog">Press Me!</hjq-dialog-open-button>
523
+ -->
524
+ <def tag="hjq-dialog-open-button" attrs="dialog">
525
+ <button onclick="hjq.dialog_opener.click(this, jQuery('#{dialog}')); return false;" param="default" class="hjq-dialog-open-button" merge />
526
+ </def>
527
+
528
+ <!--
529
+ Defines an `<hjq-dialog>` and a `<hjq-dialog-open-button>` and links the two together.
530
+
531
+ ### Attributes
532
+ * `id` (optional): the DOM id to use for the dialog
533
+ -->
534
+ <def tag="hjq-dialog-and-button">
535
+ <% # we can't use jQuery traversal functions since the dialog will move around in the DOM %>
536
+ <% attributes["id"]="hjq-dialog-#{(rand*1000000000).to_i}" unless attributes.has_key?("id") %>
537
+ <hjq-dialog param merge />
538
+ <hjq-dialog-open-button dialog="##{attributes['id']}" param />
539
+ </def>
540
+
541
+ <!--
542
+ Defines a `<select-one>` and a button that brings up an `<hjq-dialog>` containing a form allowing you to enter a new item. After the form is filled in and closed, the `<select-one>` is changed to select the new item.
543
+
544
+ ### Caveats
545
+
546
+ `select-one-or-new-dialog` is a very simple control that depends on a large number of Hobo and hobo-jquery components to be working correctly in the standard manner. These include ajax, a standard form context, and formlets.
547
+
548
+ #### sort order
549
+
550
+ The javascript for this control is a quick hack that assumes the new item created will be the last item in the select. If you've defined a sort order, this assumption will be broken. It will also be broken if there are more items than the select-one limit.
551
+
552
+ #### parameterizing parts
553
+
554
+ Because the select-one is a part, it cannot be parameterized. However, you can adjust its definition by defining the polymorphic input for the type:
555
+
556
+ <def tag="input" for="StoryStatus">
557
+ <select-one include-none="&false" />
558
+ </def>
559
+
560
+ #### controller actions
561
+
562
+ For Hobo ajax to work, standard controller actions are required. If you have customized the sub-item's create action, you may break select-one-or-new-dialog.
563
+
564
+ There is one case where you have to customize the create action. For Ajax to work, Hobo has to resurrect the context. This is straightforward if the parent context exists in the database. If it does not, then Hobo uses whatever 'this' is created by the controller action. In our case, this is usually the wrong thing.
565
+
566
+ Example:
567
+
568
+ class Story
569
+ hobo_model
570
+ fields do
571
+ name :string
572
+ end
573
+ belongs_to :story_status
574
+ # plus permissions, etc...
575
+ end
576
+
577
+ app/views/stories/new.dryml:
578
+
579
+ <new-page>
580
+ <field-list:>
581
+ <story-status-view:>
582
+ <select-one-or-new-dialog/>
583
+ </story-status-view:>
584
+ </field-list:>
585
+ </new-page>
586
+
587
+ app/controllers/story_statuses_controller.rb:
588
+
589
+ class StoryStatusesController < ApplicationController
590
+ def create
591
+ hobo_create do
592
+ if request.xhr?
593
+ self.this = Story.new
594
+ end
595
+ end
596
+ end
597
+ end
598
+
599
+
600
+ -->
601
+ <def tag="select-one-or-new-dialog">
602
+ <% dialog_id="hjq-dialog-#{typed_id.gsub(':', '-')}" %>
603
+ <% part_id = "select-one-#{typed_id.gsub(':', '-')}" %>
604
+ <input part="select-one-or-new-dialog-select" id="#{part_id}"/>
605
+ <hjq-dialog-open-button dialog="##{dialog_id}" param>
606
+ New <% this_field.humanize %>
607
+ </hjq-dialog-open-button>
608
+ <hjq-dialog param buttons='&[["cancel", "hjq.dialog.close"], ["ok", "hjq.dialog.submit_formlet_and_close"]]' title="New #{this_field.humanize}" id="&dialog_id" >
609
+ <formlet with="&this_type.new" update="&part_id" success="jQuery('##{part_id} select').val(jQuery('##{part_id} option:last').val())">
610
+ <field-list />
611
+ </formlet>
612
+ </hjq-dialog>
613
+ </def>