hobo_rapid 1.4.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/README.markdown +5 -0
  2. data/VERSION +1 -0
  3. data/app/controllers/dev_controller.rb +25 -0
  4. data/app/helpers/hobo_rapid_helper.rb +197 -0
  5. data/hobo_rapid.gemspec +26 -0
  6. data/lib/hobo_rapid/railtie.rb +6 -0
  7. data/lib/hobo_rapid.rb +13 -0
  8. data/taglibs/buttons/buttons.dryml +1 -0
  9. data/taglibs/buttons/create_button.dryml +40 -0
  10. data/taglibs/buttons/delete-button.dryml +75 -0
  11. data/taglibs/buttons/remote_method_button.dryml +34 -0
  12. data/taglibs/buttons/transition_button.dryml +71 -0
  13. data/taglibs/buttons/transition_link.dryml +22 -0
  14. data/taglibs/buttons/update_button.dryml +29 -0
  15. data/taglibs/cache/cache.dryml +1 -0
  16. data/taglibs/cache/nested_cache.dryml +332 -0
  17. data/taglibs/cards/card.dryml +7 -0
  18. data/taglibs/cards/cards.dryml +1 -0
  19. data/taglibs/cards/search_card.dryml +4 -0
  20. data/taglibs/editors/click_editor.dryml +60 -0
  21. data/taglibs/editors/editors.dryml +27 -0
  22. data/taglibs/editors/live_editor.dryml +37 -0
  23. data/taglibs/forms/error_messages.dryml +21 -0
  24. data/taglibs/forms/form.dryml +101 -0
  25. data/taglibs/forms/formlet.dryml +48 -0
  26. data/taglibs/forms/forms.dryml +5 -0
  27. data/taglibs/forms/submit.dryml +14 -0
  28. data/taglibs/hobo_rapid.dryml +14 -0
  29. data/taglibs/html/a.dryml +156 -0
  30. data/taglibs/html/aside.dryml +5 -0
  31. data/taglibs/html/doctype.dryml +39 -0
  32. data/taglibs/html/empty_tag.dryml +23 -0
  33. data/taglibs/html/footer.dryml +5 -0
  34. data/taglibs/html/header.dryml +5 -0
  35. data/taglibs/html/html.dryml +18 -0
  36. data/taglibs/html/if_ie.dryml +11 -0
  37. data/taglibs/html/image.dryml +11 -0
  38. data/taglibs/html/javascript.dryml +12 -0
  39. data/taglibs/html/section.dryml +12 -0
  40. data/taglibs/html/section_group.dryml +11 -0
  41. data/taglibs/html/stylesheet.dryml +5 -0
  42. data/taglibs/html/table.dryml +174 -0
  43. data/taglibs/i18n/ht.dryml +48 -0
  44. data/taglibs/i18n/human_attribute_name +18 -0
  45. data/taglibs/i18n/human_collection_name.dryml +69 -0
  46. data/taglibs/i18n/i18n.dryml +1 -0
  47. data/taglibs/i18n/model_name_human.dryml +16 -0
  48. data/taglibs/i18n/t.dryml +12 -0
  49. data/taglibs/inputs/after_submit.dryml +32 -0
  50. data/taglibs/inputs/check_many.dryml +25 -0
  51. data/taglibs/inputs/collection_input.dryml +10 -0
  52. data/taglibs/inputs/hidden_field.dryml +48 -0
  53. data/taglibs/inputs/hot_input.dryml +50 -0
  54. data/taglibs/inputs/input.dryml +76 -0
  55. data/taglibs/inputs/input_all.dryml +14 -0
  56. data/taglibs/inputs/input_for.dryml +38 -0
  57. data/taglibs/inputs/input_for_date.dryml +86 -0
  58. data/taglibs/inputs/input_for_enum_string.dryml +26 -0
  59. data/taglibs/inputs/input_many.dryml +131 -0
  60. data/taglibs/inputs/inputs.dryml +1 -0
  61. data/taglibs/inputs/name_one.dryml +74 -0
  62. data/taglibs/inputs/or_cancel.dryml +8 -0
  63. data/taglibs/inputs/select_input.dryml +13 -0
  64. data/taglibs/inputs/select_many.dryml +58 -0
  65. data/taglibs/inputs/select_menu.dryml +23 -0
  66. data/taglibs/inputs/select_one.dryml +46 -0
  67. data/taglibs/inputs/sortable_input_many.dryml +31 -0
  68. data/taglibs/inputs/sti_type_input.dryml +6 -0
  69. data/taglibs/lists/collection.dryml +26 -0
  70. data/taglibs/lists/empty_collection_message.dryml +13 -0
  71. data/taglibs/lists/feckless_fieldset.dryml +94 -0
  72. data/taglibs/lists/field_list.dryml +21 -0
  73. data/taglibs/lists/field_list_v1.dryml +64 -0
  74. data/taglibs/lists/labelled_item_list.dryml +11 -0
  75. data/taglibs/lists/lists.dryml +2 -0
  76. data/taglibs/lists/with_fields.dryml +98 -0
  77. data/taglibs/pages/account.dryml +30 -0
  78. data/taglibs/pages/account_disabled.dryml +19 -0
  79. data/taglibs/pages/flash_message.dryml +23 -0
  80. data/taglibs/pages/forgot_password.dryml +62 -0
  81. data/taglibs/pages/login.dryml +57 -0
  82. data/taglibs/pages/not_found_page.dryml +18 -0
  83. data/taglibs/pages/page_nav.dryml +17 -0
  84. data/taglibs/pages/pages.dryml +12 -0
  85. data/taglibs/pages/permission_denied_page.dryml +24 -0
  86. data/taglibs/plus/collection_preview.dryml +23 -0
  87. data/taglibs/plus/filter_menu.dryml +103 -0
  88. data/taglibs/plus/gravatar.dryml +12 -0
  89. data/taglibs/plus/live_search.dryml +40 -0
  90. data/taglibs/plus/plus.dryml +1 -0
  91. data/taglibs/plus/sortable_collection.dryml +36 -0
  92. data/taglibs/plus/table_plus.dryml +63 -0
  93. data/taglibs/views/a_or_an.dryml +10 -0
  94. data/taglibs/views/collection_name.dryml +28 -0
  95. data/taglibs/views/comma_list.dryml +2 -0
  96. data/taglibs/views/count.dryml +99 -0
  97. data/taglibs/views/links_for_collection.dryml +2 -0
  98. data/taglibs/views/name.dryml +30 -0
  99. data/taglibs/views/nil_view.dryml +10 -0
  100. data/taglibs/views/record_flags.dryml +5 -0
  101. data/taglibs/views/type_name.dryml +24 -0
  102. data/taglibs/views/view.dryml +79 -0
  103. data/taglibs/views/view_for.dryml +42 -0
  104. data/taglibs/views/views.dryml +1 -0
  105. data/taglibs/views/you.dryml +150 -0
  106. data/vendor/assets/javascripts/hobo_rapid.js +1 -0
  107. data/vendor/assets/stylesheets/feckless-fieldset.css +40 -0
  108. data/vendor/assets/stylesheets/hobo-rapid.css +94 -0
  109. data/vendor/assets/stylesheets/hobo_rapid.css +1 -0
  110. metadata +174 -0
@@ -0,0 +1,62 @@
1
+ <!-- The page that initiates the forgotten password process. Contains a single text-input where the user can provide
2
+ their email address -->
3
+ <def tag="forgot-password-page">
4
+ <simple-page title="#{t 'hobo.forgot_password.title', :default=>['Forgotten Password'] }" merge>
5
+
6
+ <body: class="forgot-password-page" param/>
7
+
8
+ <content: param>
9
+ <header param="content-header">
10
+ <h2 param="heading"><t key="hobo.forgot_password.heading">Forgotten Password</t></h2>
11
+ <p><t key="hobo.forgot_password.text">Enter the email address you signed up with. If we have it in
12
+ our records we'll send you an email allowing you to choose a new
13
+ password.</t></p>
14
+ </header>
15
+
16
+ <section param="content-body">
17
+ <form action="" class="forgot-password" param>
18
+ <labelled-item-list param>
19
+ <labelled-item>
20
+ <item-label param="email-address-label"><t key="hobo.forgot_password.email_address">Email Address</t></item-label>
21
+ <item-value>
22
+ <input type="text" name="email_address" id="email-address" class="email-address" param="email-address-input" />
23
+ </item-value>
24
+ </labelled-item>
25
+ </labelled-item-list>
26
+ <div param="actions">
27
+ <submit label="#{t 'hobo.actions.send', :default=>['Send'] }" param/>
28
+ </div>
29
+ </form>
30
+ </section>
31
+ </content:>
32
+
33
+ </simple-page>
34
+ </def>
35
+
36
+
37
+ <!-- Second page in the forgotten password process. Informs the user that the email has been sent "If the e-mail address you
38
+ entered is in our records". This is to avoid a privacy concern that the forgotten-password mechanism can be otherwise used to tell
39
+ if a given email is associated with an account or not. -->
40
+ <def tag="forgot-password-email-sent-page">
41
+ <simple-page title="#{t 'hobo.forgot_password_sent.title', :default=>['Forgotten Password - Sent'] }" merge>
42
+ <body: class="forgot-password-page" param/>
43
+
44
+ <content: param>
45
+ <header param="content-header">
46
+ <h2 param><t key="hobo.forgot_password_sent.heading">Forgotten Password - Sent</t></h2>
47
+ </header>
48
+
49
+ <section param="content-body">
50
+ <p param="message">
51
+ <t key="hobo.forgot_password_sent.text" email-address="#{params[:email_address]}">
52
+ If the e-mail address you entered, <%= h params[:email_address] %>,
53
+ is in our records, you will
54
+ receive an e-mail from us with instructions for resetting your
55
+ password. If you don't receive this e-mail, please check your
56
+ junk mail folder.</t></p>
57
+ <p><a href="#{base_url}/"><t key="hobo.actions.back">Back to</t> <app-name/></a></p>
58
+ </section>
59
+ </content:>
60
+
61
+ </simple-page>
62
+ </def>
@@ -0,0 +1,57 @@
1
+ <!-- Simple log-in form.
2
+
3
+ ### Attributes
4
+
5
+ - `user-model`: The User class
6
+ - all other attributes are passed on to `<form>`. You will probably need to set the action attribute: `action="&user_login_path"`.
7
+ -->
8
+ <def tag="login-form" attrs="user-model">
9
+ <% user_model ||= self.try.model -%>
10
+ <form action="&request.fullpath" class="login" merge>
11
+ <labelled-item-list param>
12
+ <labelled-item>
13
+
14
+ <item-label param="login-label"><t key="hobo.login.#{user_model.login_attribute}"><%= user_model.login_attribute.to_s.titleize %></t></item-label>
15
+ <item-value><input type="text" name="login" id="login" class="string" param="login-input" /></item-value>
16
+ </labelled-item>
17
+
18
+ <labelled-item>
19
+ <item-label param="password-label"><t key="hobo.login.password">Password</t></item-label>
20
+ <item-value>
21
+ <input type="password" name="password" id="password" class="string" param="password-input"/>
22
+ </item-value>
23
+ </labelled-item>
24
+
25
+ <labelled-item param="remember-me">
26
+ <item-label class="field-label" param="remember-me-label"><t key="hobo.login.remember_me">Remember me:</t></item-label>
27
+ <item-value>
28
+ <input type="checkbox" name="remember_me" id="remember-me" param="remember-me-input" checked/>
29
+ </item-value>
30
+ </labelled-item>
31
+ </labelled-item-list>
32
+ <div param="actions">
33
+ <submit label="#{t 'hobo.actions.login', :default=>['Log in'] }" param/><if test="&signup_url" class='nav-item'>
34
+ <t key="hobo.support.or">or</t> <a param="signup" href="&signup_url"><t key="hobo.login.signup">Sign up</t></a></if>
35
+ </div>
36
+ </form>
37
+ </def>
38
+
39
+ <!-- Simple log-in page -->
40
+ <def tag="login-page">
41
+ <% remember_me = true if remember_me.nil? %>
42
+ <simple-page title="#{t 'hobo.login.title', :default=>['Log in'] }" merge>
43
+
44
+ <body: class="login-page" param/>
45
+
46
+ <content: param>
47
+ <header param="content-header">
48
+ <h2 param="heading"><t key="hobo.login.heading">Log In</t></h2>
49
+ </header>
50
+
51
+ <section param="content-body">
52
+ <login-form param="form" user-model="&model"/>
53
+ <a href="&forgot_password_url" param="forgot-password" if="&forgot_password_url"><t key="hobo.login.forgot_password">Forgot your password?</t></a>
54
+ </section>
55
+ </content:>
56
+ </simple-page>
57
+ </def>
@@ -0,0 +1,18 @@
1
+ <!-- The page rendered by default in the case of a not-found error
2
+
3
+ ### Attributes
4
+
5
+ - `message` - The main message to display. Defaults to "The page you requested cannot be found."
6
+
7
+ -->
8
+ <def tag="not-found-page" attrs="message">
9
+ <% message ||= t("hobo.messages.not_found", :default=>["The page you requested cannot be found."]) %>
10
+ <page merge>
11
+ <body: class="not-found"/>
12
+ <content: param>
13
+ <header param="content-header">
14
+ <h2 param="heading"><message/></h2>
15
+ </header>
16
+ </content:>
17
+ </page>
18
+ </def>
@@ -0,0 +1,17 @@
1
+ <!--
2
+ A wrapper around the `will_paginate` helper. All [options to
3
+ `will_paginate`](https://github.com/mislav/will_paginate/wiki/the-will_paginate-view-helper)
4
+ are available as attributes. If you do not specify `params`, it will
5
+ be guessed. Standard Ajax form attributes are also supported. See
6
+ the `<a/>` tag for more information on using AJAX with links.
7
+ -->
8
+ <def tag="page-nav">
9
+ <% ajax_attrs, attributes = attributes.partition_hash(HoboRapidHelper::AJAX_ATTRS) %>
10
+ <%= will_paginate this, attributes.symbolize_keys.reverse_merge(
11
+ :inner_window => 2,
12
+ :previous_label => translate("hobo.actions.previous", :default=>"« Prev"),
13
+ :next_label =>translate("hobo.actions.next", :default=>"Next »"),
14
+ :params => recognize_page_path.slice(:controller,:action,:id),
15
+ :extra_attributes => (ajax_attrs.empty? ? {} : {"data-rapid" => {"a" => {"ajax_attrs" => ajax_attrs}}.to_json}),
16
+ :ignore_params => [:render, :render_options, :"_"]) %>
17
+ </def>
@@ -0,0 +1,12 @@
1
+ <!-- Rapid-Pages provides tags for working with entire pages, and
2
+ defines several pages such as the error pages and login pages. -->
3
+
4
+ <!-- nodoc. -->
5
+ <def tag="index-page" polymorphic/>
6
+ <!-- nodoc. -->
7
+ <def tag="new-page" polymorphic/>
8
+ <!-- nodoc. -->
9
+ <def tag="show-page" polymorphic/>
10
+ <!-- nodoc. -->
11
+ <def tag="edit-page" polymorphic/>
12
+
@@ -0,0 +1,24 @@
1
+ <!-- The page rendered by default in the case of a permission-denied error
2
+
3
+ ### Attributes
4
+
5
+ - `message` - The main message to display. Defaults to "That operation is not allowed"
6
+
7
+ -->
8
+ <def tag="permission-denied-page" attrs="message">
9
+ <% message ||= "That operation is not allowed" %>
10
+ <page merge>
11
+ <body: class="permission-denied"/>
12
+ <content: param>
13
+ <header param="content-header">
14
+ <h2 param="heading"><message/></h2>
15
+ <div class="debug" if="&Rails.env.development?">
16
+ <h3>Exception:</h3>
17
+ <pre><%= h @permission_error.pretty_inspect %></pre>
18
+ <h3>params:</h3>
19
+ <pre><%= h params.pretty_inspect %></pre>
20
+ </div>
21
+ </header>
22
+ </content:>
23
+ </page>
24
+ </def>
@@ -0,0 +1,23 @@
1
+ <!-- Deprecated: use collection-preview instead. -->
2
+ <def tag="preview-with-more"><collection-preview merge/></def>
3
+
4
+
5
+ <!-- Captures the common pattern of a list of "the first few" cards, along with a link to the rest. -->
6
+ <def tag="collection-preview" attrs="name">
7
+ <% model_class = this.member_class
8
+ name ||= model_class.name.downcase.pluralize -%>
9
+ <section class="#{name.dasherize} collection-preview" param="default">
10
+ <h3 param="heading">
11
+ <ht key="#{model_class.to_s.underscore}.collection.heading" count="&this.size">
12
+ <do param="heading-content"><%= name.pluralize.titleize %></do>
13
+ </ht>
14
+ </h3>
15
+ <a with="&model_class" action="new" if="&can_create?(model_class.new)" param="new-link">
16
+ <ht key="#{model_class.to_s.underscore}.actions.new">New <%= model_class.model_name.human %></ht>
17
+ </a>
18
+ <collection param/>
19
+ <unless test="&this.empty? || this.size == model_class.count">
20
+ <a param="show-all"><ht key="#{model_class.to_s.underscore}.actions.show_all" count="100">Show all <%= name.pluralize.titleize %>...</ht></a>
21
+ </unless>
22
+ </section>
23
+ </def>
@@ -0,0 +1,103 @@
1
+ <!-- A `<select>` menu intended to act as a filter for index pages.
2
+
3
+ ### Example
4
+
5
+ Filtering on state is a common use. Here's the dryml for Order:
6
+
7
+ <filter-menu param-name="state" options="&Order::Lifecycle.states.keys" />
8
+
9
+ And the controller action:
10
+
11
+ def index
12
+ # always validate data given in URL's!!!
13
+ params[:state]=nil unless Order::Lifecycle.states.include?(params[:state]._?.to_sym)
14
+ finder = params[:state] ? Order.send(params[:state]) : Order
15
+ hobo_index finder
16
+ end
17
+
18
+ See [Filtering stories by status](/tutorials/agility#filtering_stories_by_status) in the [Agility Tutorial](/tutorials/agility) for an example.
19
+
20
+ ### Attributes
21
+
22
+ Standard AJAX attributes such as 'update' are supported. If any are supplied, filter-menu uses Ajax rather than a page refresh.
23
+
24
+ - `param-name` - the name of the HTTP parameter to use for the filter
25
+ - `options` - an array of options or an array of arrays (useful for localized apps) for the menu.
26
+ It can be omitted if you provide the options as an array or array of arrays in the locale file.
27
+ - `no-filter` - The text of the first option which indicates no filter is in effect. Defaults to 'All'
28
+ - `first-value` - the value to be used with the first option. Typically not used,
29
+ meaning the option has a blank value.
30
+ - model - the model name (optional: needed if you use the "activerecord.attributes" namespace.
31
+
32
+ ### I18n
33
+
34
+ It lookups the options attributes in `activerecord.attributes.#{model}.filter_menu.#{param\_name}.options`
35
+ with fallback to `filter_menu.#{param_name}.options`.
36
+ The passed options are used as a default in case the lookup fails.
37
+ Besides the `activerecord.attributes.#{model}.filter_menu.#{param_name}.no_filter` or
38
+ `tags.filter_menu.default.no_filter` key is used as default of the attribute "no-filter"
39
+ (or "All" if no default is found)
40
+
41
+ ### I18n Example
42
+
43
+
44
+ es:
45
+ activerecord:
46
+ attributes:
47
+ <model_name>:
48
+ filter_menu:
49
+ period:
50
+ no_filter: Todos Períodos
51
+ options:
52
+ - [ "Hoy", "today" ]
53
+ - [ "Ayer", "yesterday" ]
54
+
55
+ or
56
+
57
+ es:
58
+ tags:
59
+ filter_menu:
60
+ period:
61
+ no_filter: Todos Períodos
62
+ options:
63
+ - [ "Hoy", "today" ]
64
+ - [ "Ayer", "yesterday" ]
65
+
66
+
67
+ TIME_PERIODS = %w[today yesterday]
68
+
69
+ <t-filter-menu param-name="period" options="&TIME_PERIODS" no-filter="All Periods"/>
70
+
71
+ with I18n.locale == :es
72
+
73
+ <select name="period">
74
+ <option value="">Todos Períodos</option>
75
+ <option value="today">Hoy</option>
76
+ <option value="yesterday">Ayer</option>
77
+ </select>
78
+
79
+ with I18n.locale == :en (i.e no locale file)
80
+
81
+ <select name="period">
82
+ <option value="">All Periods</option>
83
+ <option value="today">today</option>
84
+ <option value="yesterday">yesterday</option>
85
+ </select>
86
+
87
+ -->
88
+ <def tag="filter-menu" attrs="model, param-name, options, no-filter, id, first-value">
89
+ <% options = t("activerecord.attributes.#{model}.filter_menu.#{param_name}.options", :default=>[:"tags.filter_menu.#{param_name}.options", options])
90
+ raise ArgumentError, %(You must provide an "options" attribute, or set "activerecord.attributes.#{model}.filter_menu.#{param_name}.options" or "tags.filter_menu.#{param_name}.options" to an Array or to an Array of Arrays
91
+ in your locale file(s)) unless options.is_a?(Array)
92
+ no_filter = t("activerecord.attributes.#{model}.filter_menu.#{param_name}.no_filter", :default=>[:"tags.filter_menu.#{param_name}.no_filter", :"tags.filter_menu.default.no_filter", no_filter, "All"])
93
+ %>
94
+ <form action="&request.path" method="get" class="filter-menu" merge-attrs="&attributes" data-rapid="&data_rapid('filter-menu')">
95
+ <div>
96
+ <% opt = options.first.kind_of?(Array) ? options.*.last : options
97
+ selected = opt.detect {|o| o.to_s==params[param_name.gsub('-', '_')] } %>
98
+ <select-menu name="&param_name" options="&options" selected="&selected"
99
+ first-option="&no_filter" first-value="&first_value" key="&param_name" merge-params/>
100
+ <hidden-fields for-query-string skip="page,#{param_name}"/>
101
+ </div>
102
+ </form>
103
+ </def>
@@ -0,0 +1,12 @@
1
+ <!-- Renders a gravatar (see [gravatar.com](http://gravatar.com)) image in side a link to `this`. Requires `this` to have an `email_address` field. Normally called with a user record in context.
2
+
3
+ ### Attributes
4
+
5
+ - `size` - Size in pixels of the image. Defaults to 80.
6
+ - `rating` - The rating allowed. Defaults to 'g'. See [gravatar.com](http://gravatar.com) for information on ratings.
7
+
8
+ -->
9
+ <def tag="gravatar" attrs="size, rating">
10
+ <% size ||= 80; rating ||= 'g'; digest = Digest::MD5.hexdigest(this.email_address) -%>
11
+ <a class="gravatar"><img class="gravatar" src="http://www.gravatar.com/avatar/#{digest}?s=#{size}&r=#{rating}" merge-attrs/></a>
12
+ </def>
@@ -0,0 +1,40 @@
1
+ <!-- Provides an ajax-powered *find-as-you-type* live search field which is hooked up to Hobo's site-side search feature.
2
+
3
+ Customizing the display of results can be done through customizing the "box" param and the `search-results` tag. The current definition of `<search-results>` expects results to be returned in `@search_results`.
4
+
5
+ TODO: currently you have to press 'return' to initiate the search. This should be easy to fix in hjq-live-search.js - the hard part will probably be in doing it in a way that works in all possible browsers.
6
+
7
+ ### attributes
8
+
9
+ - action: the path to send the query. Default is "/search" (actually, site_search_path)
10
+ - query_param: the name of the param to use for the search. Default is "query"
11
+
12
+ All other attributes are merged into the form, so standard form attributes are supported.
13
+ -->
14
+ <def tag="live-search" attrs="query-param, panel-tag">
15
+ <% attributes[:action] ||= site_search_path %>
16
+ <% attributes[:update] ||= "search-results-part" %>
17
+ <% attributes[:message] ||= t("hobo.live_search.spinner_message", :default => "Querying...") %>
18
+ <% query_param ||= "query" %>
19
+ <div class="search" data-rapid='{"live-search": {}}'>
20
+ <form merge-attrs param refocus-form>
21
+ <input type="hidden" value="2" name="search_version"/>
22
+ <label for="&query_param" param><t key="hobo.live_search.label">Search</t></label><input type="search" name="&query_param" class="live-search" param/>
23
+ </form>
24
+ <% panel_tag ||= 'dialog-box' %>
25
+ <% title = t("hobo.live_search.results_label", :default => "Search Results") %>
26
+ <call-tag tag="&panel_tag" position="&{:my => 'right top'}" width="&600" height="&800" id="search-results-box" with="&nil" title="&title" param="box">
27
+ <do part="search-results-part">
28
+ <search-results/>
29
+ </do>
30
+ </call-tag>
31
+ </div>
32
+ </def>
33
+
34
+ <!-- redefining or extending this tag will allow you to customize the display of search results -->
35
+ <def tag="search-results">
36
+ <section with="&@search_results || []" data-rapid='{"search-results": {}}'>
37
+ <collection/>
38
+ <else><t key="hobo.live_search.no_results">Your search returned no matches.</t></else>
39
+ </section>
40
+ </def>
@@ -0,0 +1 @@
1
+ <!-- Tags that define higher level interactive ‘widgets’ -->
@@ -0,0 +1,36 @@
1
+ <!-- An enhanced version of Rapid's `<collection>` tag that supports drag-and-drop re-ordering.
2
+
3
+ Each item in the collection has a `<div class="ordering-handle" param="handle">` added, which can be used to drag the item up and down.
4
+
5
+ ### Attributes
6
+
7
+ `sortable-collection` supports all of the options and events defined by [jQuery UI's Sortable](http://jqueryui.com/demos/sortable/).
8
+
9
+ Sortable also supports all of the standard [Hobo AJAX callbacks](http://cookbook.hobocentral.net/api_taglibs/rapid_forms).
10
+
11
+ The jQuery UI option `update` is used internally to save the new order via Ajax. If you modify this option, your function may call `$(this).hjq_sortable_collection('update')` to retain this functionality.
12
+
13
+ ### Controller support
14
+
15
+ This tag assumes the controller has a `reorder` action and the model has a `position_column` method. This action is added automatically by Hobo's model-controller if the model declares `acts_as_list`. See also [Drag and Drop Reordering](/manual/controllers#drag_and_drop_reordering) in the [Controllers and Routing](/manual/controllers) chapter of the manual.
16
+ -->
17
+ <def tag="sortable-collection">
18
+ <%
19
+ options, attrs = attributes.partition_hash(['disabled', 'appendTo', 'axis', 'cancel', 'connectWith', 'containment', 'cursor', 'cursorAt', 'delay', 'distance', 'dropOnEmpty', 'forceHelperSize', 'forcePlaceholderSize', 'grid', 'handle', 'helper', 'items', 'opacity', 'placeholder', 'revert', 'scroll', 'scrollSensitivity', 'scrollSpeed', 'tolerance', 'zIndex'])
20
+ events, html_attrs = attrs.partition_hash(['create', 'start', 'sort', 'change', 'beforeStop', 'stop', 'update', 'receive', 'remove', 'over', 'out', 'activate', 'deactivate'])
21
+ ajax_attrs, html_attrs = attrs.partition_hash(HoboRapidHelper::AJAX_CALLBACKS)
22
+
23
+ singular_name = this.member_class.name.underscore
24
+ route_method = subsite ? "#{subsite}_reorder_#{singular_name.pluralize}_url" : "reorder_#{singular_name.pluralize}_url"
25
+ reorder_url = send(route_method)
26
+
27
+ add_classes!(html_attrs, "sortable-collection")
28
+ add_data_rapid!(html_attrs, "sortable-collection", :options => options, :events => events, :ajax_attrs => ajax_attrs, :reorder_url => reorder_url, :reorder_parameter => "#{singular_name}_ordering")
29
+ %>
30
+ <collection class="sortable" merge-attrs="&html_attrs" merge-params>
31
+ <item: param>
32
+ <div class="ordering-handle" param="handle" if="&can_edit?(this.position_column)">&uarr;<br/>&darr;</div>
33
+ <do param="default"><card param/></do>
34
+ </item:>
35
+ </collection>
36
+ </def>
@@ -0,0 +1,63 @@
1
+ <!-- An enhanced version of Rapid's `<table>` that has support for column sorting, searching and pagination.
2
+
3
+ This tag calls `<table merge-params>`, so the parameters for `<table>` are also available.
4
+
5
+ The enhancements made available in this tag require controller support.
6
+
7
+ An [worked example](/tutorials/agility#improve_the_project_page_with_a_searchable_sortable_table) of this tag is available in the [Agility Tutorial](/tutorials/agility)
8
+
9
+ ### Attributes
10
+
11
+ All attributes supported by `<table>` and `<with-fields>` are supported.
12
+
13
+ AJAX attributes are passed through to the sort links and the search form.
14
+
15
+ sort-field: defaults to @sort_field, if that is available. `parse_sort_params` sets this variable
16
+
17
+ sort-direction: 'asc' or 'dec'. defaults to @sort_direction, if that is available. `parse_sort_params` sets this variable
18
+
19
+ sort-columns: a hash that allow you to map fields to sortable columns. The default is {"this" => "name"} (or whatever column that has the :name option set on it).
20
+
21
+ -->
22
+ <def tag="table-plus" attrs="sort-field, sort-direction, sort-columns" >
23
+ <% sort_field ||= @sort_field; sort_direction ||= @sort_direction; sort_columns ||= {} %>
24
+ <% sort_columns['this'] ||= this.member_class.try.name_attribute %>
25
+ <% ajax_attrs, attributes = attributes.partition_hash(HoboRapidHelper::AJAX_ATTRS) %>
26
+ <div class="table-plus" merge-attrs="&attributes - attrs_for(:with_fields) - attrs_for(:table)">
27
+ <div class="header" param="header">
28
+ <div class="search">
29
+ <form param="search-form" method="get" action="" with="&nil" merge-attrs="&ajax_attrs" >
30
+ <hidden-fields for-query-string skip="page, search"/>
31
+ <span><t key="hobo.table_plus.search">Search</t></span>
32
+ <input class="search" type="search" name="search" value="&params[:search]"/>
33
+ <submit label="&t('hobo.table_plus.submit_label', :default=>'Go')" class="search-button" param="search-submit"/>
34
+ </form>
35
+ </div>
36
+ </div>
37
+
38
+ <table merge-attrs="&attributes & (attrs_for(:table) + attrs_for(:with_fields))" empty merge-params>
39
+ <field-heading-row:>
40
+ <with-field-names merge-attrs="&all_attributes & attrs_for(:with_fields)">
41
+ <% col = sort_columns[scope.field_path] || scope.field_path
42
+ sort = sort_field == col && sort_direction == 'asc' ?
43
+ "-#{col}" : col
44
+ sort_url = url_for_page_path(query_parameters_filtered('except' => 'page').merge(:sort => sort))
45
+ col_heading_name = this.member_class.try.human_attribute_name(scope.field_name, :default=> scope.field_name.titleize) %>
46
+
47
+ <th param="#{scope.field_name}-heading">
48
+ <a href="&sort_url" class="column-sort"
49
+ param="#{scope.field_name}-heading-link" merge-attrs="&ajax_attrs" ><%= col_heading_name %></a>
50
+ <if test="&col == sort_field">
51
+ <do param="up-arrow" if="&sort_direction == 'desc'">&uarr;</do>
52
+ <do param="down-arrow" if="&sort_direction == 'asc'">&darr;</do>
53
+ </if>
54
+ </th>
55
+ </with-field-names>
56
+ <th if="&all_parameters[:controls]" class="controls"></th>
57
+ </field-heading-row>
58
+ </table>
59
+ <empty-collection-message param="empty-message"/>
60
+
61
+ <page-nav param if="&this.respond_to?(:page_count) || this.respond_to?(:total_pages)" merge-attrs="&ajax_attrs" />
62
+ </div>
63
+ </def>
@@ -0,0 +1,10 @@
1
+ <!-- Capitalised version of `<a-or-an>` -->
2
+ <def tag="A-or-An" attrs="word"><%=
3
+ (word =~ /^[aeiou]/i ? "An " : "A ") + word
4
+ %></def>
5
+
6
+ <!-- Deprecated. It's harder than you think to do this (e.g. an umbrella, an user)
7
+ -->
8
+ <def tag="a-or-an" attrs="word"><%=
9
+ (word =~ /^[aeiou]/i ? "an " : "a ") + word
10
+ %></def>
@@ -0,0 +1,28 @@
1
+ <!-- Renders a human readable name of a collection
2
+
3
+ Use this tag for en locale only. Use human-collection-name for i18n.
4
+
5
+ ### Details
6
+
7
+ - Uses `this.origin_attribute` as the name.
8
+ - Falls back to `<type-name>` otherwise.
9
+ - By default the name is titleised and plural.
10
+
11
+ ### Attributes
12
+
13
+ - singular: singularise the name
14
+ - lowercase: render the name in all lower case
15
+ - dasherize: render the name in lower case with dashes instead of spaces.
16
+
17
+ -->
18
+ <def tag="collection-name" attrs="singular, lowercase, dasherize"><%=
19
+ if (attr = this.try.origin_attribute)
20
+ name = attr.to_s
21
+ name = dasherize ? name.underscore.dasherize : name.titleize
22
+ name = name.singularize if singular
23
+ name = name.downcase if lowercase
24
+ name
25
+ else
26
+ type_name(:plural => !singular, :lowercase => lowercase, :dasherize => dasherize)
27
+ end
28
+ %></def>
@@ -0,0 +1,2 @@
1
+ <!-- Renders a collection of strings joined with ", ", or some other string passed in the `join` attribute -->
2
+ <def tag="comma-list" attrs="join"><%= this.join(join || ", ") %></def>
@@ -0,0 +1,99 @@
1
+ <!--
2
+ A convenience tag used to output a count summary with a correctly localized and pluralised label. Works with any kind of collection such as an `ActiveRecord` association or an array.
3
+
4
+ ### Usage
5
+
6
+ <count:comments/> -> <span class="count">1 Comment</span>
7
+
8
+ <count:viewings/> -> <span class="count">3 Viewings</span>
9
+
10
+ The label can be customised using the `label` attribute, e.g.
11
+
12
+ <count:comments label="blog post comment"/> -> <span class="count">12 blog post comments</span>
13
+
14
+ You can pass a summary attribute, which will generate a complete localized sentence. It allows 2 options:
15
+
16
+ - boolean (e.g. `<count summary/>`): it will lookup the 'tags.count.default' key in the locale file. If the lookup fails, it will fallback to the english default sentences consistent with the count.
17
+ - String (e.g. `<count summary="offer"/>`): it will lookup the 'tags.count.offer' key in the locale file. If the lookup fails, it will fallback to the english default sentences consistent with the count.
18
+
19
+ ### Examples
20
+
21
+ it:
22
+ tags:
23
+ count:
24
+ default:
25
+ zero: "Non ci sono {{label}}"
26
+ one: "C'è solo 1 {{label}}"
27
+ other: "Ci sono {{count}} {{label}}"
28
+ choice:
29
+ zero: "Non ci sono {{label}} da scegliere"
30
+ one: "Puoi scegliere solo una {{label}}"
31
+ other: "Puoi scegliere tra {{count}} {{label}}"
32
+
33
+ with :en locale and boolean summary (internal defaults)
34
+
35
+ <count:comments summary/> -> <span class="count">There is 1 Comment</span>
36
+ <count:viewings summary/> -> <span class="count">There are 3 Viewings</span>
37
+
38
+ (note: just add the locale english strings to use like the following examples)
39
+
40
+ with :it locale and boolean summary (key "tags.count.default")
41
+
42
+ <count:comments summary/>
43
+ -> count => 0 -> <span class="count">Non ci sono Commenti</span>
44
+ -> count => 1 -> <span class="count">C'è solo 1 Commento</span>
45
+ -> count => 5 -> <span class="count">Ci sono 5 Commenti</span>
46
+
47
+ with :it locale and summary="choice" (key "tags.count.choice")
48
+
49
+ <count:comments summary="choice"/>
50
+ -> count => 0 -> <span class="count">Non ci sono Commenti da scegliere</span>
51
+ -> count => 1 -> <span class="count">Puoi scegliere solo 1 Commento</span>
52
+ -> count => 5 -> <span class="count">Puoi scegliere tra 5 Commenti</span>
53
+
54
+ ### Additional Notes
55
+
56
+ * The `prefix` attribute is deprecated: use summary instead.
57
+
58
+ * Use the `lowercase` attribute to force the generated label to be lowercase:
59
+
60
+ <count:comments lowercase/> -> <span class="count">1 comment</span>
61
+ * Use the `if-any` attribute to output nothing if the count is zero. This can be followed by an `<else>` tag to handle the empty case:
62
+
63
+ <count:comments if-any/><else>There are no comments</else>
64
+ -->
65
+ <def tag="count" attrs="summary, label, if-any, lowercase, prefix"><span class="count"><%=
66
+ raise Exception.new("asked for count of a string") if this.is_a?(String)
67
+ Rails.logger.warn('The "prefix" attribute is deprecated: please, use a locale string') unless prefix.blank?
68
+
69
+ c = collection_count
70
+
71
+ # generated label will be pluralized
72
+ label ||= case
73
+ when this.is_a?(Class)
74
+ this.model_name.human(:count=>c)
75
+ when (attr = this.try.origin_attribute)
76
+ (this_parent || this.origin).class.human_attribute_name(attr, :count=>c)
77
+ else
78
+ this.member_class.model_name.human(:count=>c)
79
+ end
80
+
81
+ label = label.downcase if lowercase
82
+
83
+ Dryml.last_if = c > 0 if if_any
84
+ if if_any && c == 0
85
+ ""
86
+ else
87
+ if summary.blank? # old behaviour
88
+ main = label.blank? ? c : "#{c} #{label}"
89
+ if prefix.in? %w(are is)
90
+ prefix = c == 1 ? "is" : "are"
91
+ end
92
+ (prefix ? "#{prefix} #{main}" : main.to_s)
93
+ else
94
+ key = summary.kind_of?(String) ? summary : "default"
95
+ default = c == 1 ? "There is 1 #{label}" : "There are #{c} #{label}"
96
+ t "tags.count.#{key}", {:count=>c, :label=>label, :default=>default}.merge(attributes)
97
+ end
98
+ end
99
+ %></span></def>
@@ -0,0 +1,2 @@
1
+ <!-- Renders a comma separated list of links (`<a>`), or "(none)" if the list is empty -->
2
+ <def tag="links-for-collection"><%= this.empty? ? "(none)" : context_map { a }.safe_join(", ") %></def>