talia_core 0.4.22 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/VERSION.yml +2 -2
  2. data/config/database.yml +9 -9
  3. data/config/rdfstore.yml +4 -4
  4. data/config/talia_core.yml +5 -2
  5. data/generators/generator_helpers.rb +66 -25
  6. data/generators/talia_admin/talia_admin_generator.rb +9 -9
  7. data/generators/talia_admin/templates/app/controllers/admin/admin_site_controller.rb +5 -0
  8. data/generators/talia_admin/templates/app/controllers/admin/custom_templates_controller.rb +7 -0
  9. data/generators/talia_admin/templates/app/controllers/admin/front_controller.rb +19 -0
  10. data/generators/talia_admin/templates/app/controllers/admin/talia_collections_controller.rb +7 -0
  11. data/generators/talia_admin/templates/app/controllers/admin/talia_sources_controller.rb +17 -0
  12. data/generators/talia_admin/templates/app/controllers/admin/users_controller.rb +7 -0
  13. data/generators/talia_admin/templates/app/controllers/users_controller.rb +7 -0
  14. data/generators/talia_admin/templates/app/helpers/admin/front_helper.rb +2 -0
  15. data/generators/talia_admin/templates/app/helpers/admin/talia_sources_helper.rb +7 -0
  16. data/generators/talia_admin/templates/app/helpers/users_helper.rb +2 -0
  17. data/generators/talia_admin/templates/app/models/guest.rb +7 -0
  18. data/generators/talia_admin/templates/app/models/talia_collection.rb +56 -0
  19. data/generators/talia_admin/templates/app/models/talia_source.rb +43 -0
  20. data/generators/talia_admin/templates/app/models/user.rb +59 -0
  21. data/generators/talia_admin/templates/app/models/user_mailer.rb +14 -0
  22. data/generators/talia_admin/templates/app/viewhints/talia_source_hints.rb +3 -0
  23. data/generators/talia_admin/templates/app/views/admin/custom_templates/index.dryml +7 -0
  24. data/generators/talia_admin/templates/app/views/admin/front/index.dryml +29 -0
  25. data/generators/talia_admin/templates/app/views/admin/front/summary.dryml +103 -0
  26. data/generators/talia_admin/templates/app/views/admin/talia_collections/index.dryml +7 -0
  27. data/generators/talia_admin/templates/app/views/admin/talia_sources/index.dryml +17 -0
  28. data/generators/talia_admin/templates/app/views/admin/talia_sources/show.dryml +8 -0
  29. data/generators/talia_admin/templates/app/views/admin/users/index.dryml +7 -0
  30. data/generators/talia_admin/templates/app/views/taglibs/admin_site.dryml +91 -0
  31. data/generators/talia_admin/templates/app/views/taglibs/application.dryml +9 -0
  32. data/generators/talia_admin/templates/app/views/taglibs/auto/admin/rapid/cards.dryml +34 -0
  33. data/generators/talia_admin/templates/app/views/taglibs/auto/admin/rapid/forms.dryml +71 -0
  34. data/generators/talia_admin/templates/app/views/taglibs/auto/admin/rapid/pages.dryml +543 -0
  35. data/generators/talia_admin/templates/app/views/taglibs/auto/rapid/cards.dryml +37 -0
  36. data/generators/talia_admin/templates/app/views/taglibs/auto/rapid/forms.dryml +35 -0
  37. data/generators/talia_admin/templates/app/views/taglibs/auto/rapid/pages.dryml +175 -0
  38. data/generators/talia_admin/templates/app/views/taglibs/front_site.dryml +1 -0
  39. data/generators/talia_admin/templates/app/views/taglibs/themes/clean/clean.dryml +10 -0
  40. data/generators/talia_admin/templates/app/views/user_mailer/forgot_password.erb +10 -0
  41. data/generators/talia_admin/templates/config/hobo_initializer.rb +1 -0
  42. data/generators/talia_admin/templates/migrations/create_users.rb +22 -0
  43. data/generators/talia_admin/templates/public/hobothemes/clean/images/101-3B5F87-ACD3E6.png +0 -0
  44. data/generators/talia_admin/templates/public/hobothemes/clean/images/30-3E547A-242E42.png +0 -0
  45. data/generators/talia_admin/templates/public/hobothemes/clean/images/30-DBE1E5-FCFEF5.png +0 -0
  46. data/generators/talia_admin/templates/public/hobothemes/clean/images/300-ACD3E6-fff.png +0 -0
  47. data/generators/talia_admin/templates/public/hobothemes/clean/images/50-ACD3E6-fff.png +0 -0
  48. data/generators/talia_admin/templates/public/hobothemes/clean/images/fieldbg.gif +0 -0
  49. data/generators/talia_admin/templates/public/hobothemes/clean/images/pencil.png +0 -0
  50. data/generators/talia_admin/templates/public/hobothemes/clean/images/small_close.png +0 -0
  51. data/generators/talia_admin/templates/public/hobothemes/clean/images/spinner.gif +0 -0
  52. data/generators/talia_admin/templates/public/hobothemes/clean/stylesheets/clean.css +326 -0
  53. data/generators/talia_admin/templates/public/hobothemes/clean/stylesheets/rapid-ui.css +100 -0
  54. data/generators/talia_admin/templates/public/javascripts/IE7.js +2 -0
  55. data/generators/talia_admin/templates/public/javascripts/blank.gif +0 -0
  56. data/generators/talia_admin/templates/public/javascripts/dryml-support.js +132 -0
  57. data/generators/talia_admin/templates/public/javascripts/hobo-rapid.js +937 -0
  58. data/generators/talia_admin/templates/public/javascripts/ie7-recalc.js +166 -0
  59. data/generators/talia_admin/templates/public/javascripts/lowpro.js +43 -25
  60. data/generators/talia_admin/templates/public/stylesheets/application.css +0 -0
  61. data/generators/talia_admin/templates/public/stylesheets/hobo-rapid.css +94 -0
  62. data/generators/talia_admin/templates/public/stylesheets/reset.css +95 -0
  63. data/generators/talia_admin/templates/test/fixtures/users.yml +7 -0
  64. data/generators/talia_admin/templates/test/functional/admin/{custom_templates_controller_test.rb → front_controller_test.rb} +1 -1
  65. data/generators/talia_admin/templates/test/functional/users_controller_test.rb +8 -0
  66. data/generators/talia_admin/templates/test/unit/user_test.rb +8 -0
  67. data/generators/talia_base/talia_base_generator.rb +24 -17
  68. data/generators/talia_oai/USAGE +0 -0
  69. data/generators/talia_oai/talia_oai_generator.rb +19 -0
  70. data/generators/talia_oai/templates/oai_controller.rb +9 -0
  71. data/generators/talia_oai/templates/oai_initializer.rb +16 -0
  72. data/lib/custom_template.rb +24 -0
  73. data/lib/talia_core/active_source_parts/class_methods.rb +1 -1
  74. data/lib/talia_core/active_source_parts/finders.rb +4 -0
  75. data/lib/talia_core/collection.rb +4 -0
  76. data/lib/talia_core/oai/active_source_model.rb +113 -0
  77. data/lib/talia_core/oai/active_source_oai_adapter/class_methods.rb +17 -0
  78. data/lib/talia_core/oai/active_source_oai_adapter.rb +106 -0
  79. data/lib/talia_core/semantic_property.rb +1 -0
  80. data/lib/talia_core/semantic_relation.rb +1 -0
  81. data/test/talia_core/active_source_test.rb +13 -0
  82. metadata +71 -59
  83. data/generators/talia_admin/templates/controllers/admin/background_controller.rb +0 -52
  84. data/generators/talia_admin/templates/controllers/admin/custom_templates_controller.rb +0 -25
  85. data/generators/talia_admin/templates/controllers/admin/locales_controller.rb +0 -15
  86. data/generators/talia_admin/templates/controllers/admin/roles_controller.rb +0 -8
  87. data/generators/talia_admin/templates/controllers/admin/sources_controller.rb +0 -9
  88. data/generators/talia_admin/templates/controllers/admin/translations_controller.rb +0 -61
  89. data/generators/talia_admin/templates/controllers/admin/users_controller.rb +0 -10
  90. data/generators/talia_admin/templates/controllers/admin_controller.rb +0 -7
  91. data/generators/talia_admin/templates/helpers/admin/background_helper.rb +0 -60
  92. data/generators/talia_admin/templates/helpers/admin/custom_templates_helper.rb +0 -5
  93. data/generators/talia_admin/templates/helpers/admin/locales_helper.rb +0 -2
  94. data/generators/talia_admin/templates/helpers/admin/roles_helper.rb +0 -2
  95. data/generators/talia_admin/templates/helpers/admin/sources_helper.rb +0 -65
  96. data/generators/talia_admin/templates/helpers/admin/translations_helper.rb +0 -51
  97. data/generators/talia_admin/templates/helpers/admin/users_helper.rb +0 -11
  98. data/generators/talia_admin/templates/helpers/admin_helper.rb +0 -30
  99. data/generators/talia_admin/templates/migrations/populate_users.rb +0 -14
  100. data/generators/talia_admin/templates/models/role.rb +0 -3
  101. data/generators/talia_admin/templates/public/javascripts/backend.js +0 -24
  102. data/generators/talia_admin/templates/public/stylesheets/talia_core/backend.css +0 -463
  103. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/body_bg.gif +0 -0
  104. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/footer_bg.gif +0 -0
  105. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/header.jpg +0 -0
  106. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/header_bg.gif +0 -0
  107. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/menu.jpg +0 -0
  108. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/menu_bg.gif +0 -0
  109. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/opednid.gif +0 -0
  110. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/page_bg.jpg +0 -0
  111. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/triangolino.gif +0 -0
  112. data/generators/talia_admin/templates/public/stylesheets/talia_core/images/backend/triangolino_full.gif +0 -0
  113. data/generators/talia_admin/templates/test/functional/admin/locales_controller_test.rb +0 -35
  114. data/generators/talia_admin/templates/test/functional/admin/sources_controller_test.rb +0 -109
  115. data/generators/talia_admin/templates/test/functional/admin/translations_controller_test.rb +0 -93
  116. data/generators/talia_admin/templates/test/functional/admin/users_controller_test.rb +0 -67
  117. data/generators/talia_admin/templates/test/functional/admin_controller_test.rb +0 -15
  118. data/generators/talia_admin/templates/views/admin/background/_finished.html.erb +0 -9
  119. data/generators/talia_admin/templates/views/admin/background/_pending.html.erb +0 -1
  120. data/generators/talia_admin/templates/views/admin/background/_progress.html.erb +0 -15
  121. data/generators/talia_admin/templates/views/admin/background/_running.html.erb +0 -11
  122. data/generators/talia_admin/templates/views/admin/background/environment.html.erb +0 -12
  123. data/generators/talia_admin/templates/views/admin/background/show.html.erb +0 -17
  124. data/generators/talia_admin/templates/views/admin/background/stderr.html.erb +0 -6
  125. data/generators/talia_admin/templates/views/admin/background/stdin.html.erb +0 -6
  126. data/generators/talia_admin/templates/views/admin/background/stdout.html.erb +0 -6
  127. data/generators/talia_admin/templates/views/admin/custom_templates/_content_form_column.rhtml +0 -2
  128. data/generators/talia_admin/templates/views/admin/custom_templates/_template_type_form_column.rhtml +0 -2
  129. data/generators/talia_admin/templates/views/admin/index.html.erb +0 -5
  130. data/generators/talia_admin/templates/views/admin/locales/new.html.erb +0 -9
  131. data/generators/talia_admin/templates/views/admin/sources/_show.html.erb +0 -13
  132. data/generators/talia_admin/templates/views/admin/translations/_new_translation.html.erb +0 -7
  133. data/generators/talia_admin/templates/views/admin/translations/_translation.html.erb +0 -10
  134. data/generators/talia_admin/templates/views/admin/translations/edit.html.erb +0 -16
  135. data/generators/talia_admin/templates/views/layouts/admin.html.erb +0 -56
  136. data/generators/talia_base/templates/script/setup_talia_backend +0 -43
@@ -0,0 +1,937 @@
1
+ Object.extend = function(destination) {
2
+ $A(arguments).slice(1).each(function (src) {
3
+ for (var property in src) {
4
+ destination[property] = src[property];
5
+ }
6
+ })
7
+ return destination
8
+ }
9
+
10
+ Object.merge = function() {
11
+ return Object.extend.apply(this, [{}].concat($A(arguments)))
12
+ }
13
+
14
+ var Hobo = {
15
+
16
+ searchRequest: null,
17
+ uidCounter: 0,
18
+ ipeOldValues: {},
19
+ spinnerMinTime: 500, // milliseconds
20
+
21
+ uid: function() {
22
+ Hobo.uidCounter += 1
23
+ return "uid" + Hobo.uidCounter
24
+ },
25
+
26
+ updatesForElement: function(el) {
27
+ el = $(el)
28
+ var updates = Hobo.getClassData(el, 'update')
29
+ return updates ? updates.split(':') : []
30
+ },
31
+
32
+ ajaxSetFieldForElement: function(el, val, options) {
33
+ var updates = Hobo.updatesForElement(el)
34
+ var params = Hobo.fieldSetParam(el, val)
35
+ var p = el.getAttribute("hobo-ajax-params")
36
+ if (p) params = params + "&" + p
37
+
38
+ var opts = Object.merge(options || {}, { params: params, message: el.getAttribute("hobo-ajax-message")})
39
+ Hobo.ajaxRequest(Hobo.putUrl(el), updates, opts)
40
+ },
41
+
42
+ ajaxUpdateParams: function(updates, resultUpdates) {
43
+ var params = []
44
+ var i = 0
45
+ if (updates.length > 0) {
46
+ updates.each(function(id_or_el) {
47
+ var el = $(id_or_el)
48
+ if (el) { // ignore update of parts that do not exist
49
+ var partDomId = el.id
50
+ if (!hoboParts[partDomId]) { throw "Update of dom-id that is not a part: " + partDomId }
51
+ params.push("render["+i+"][part_context]=" + encodeURIComponent(hoboParts[partDomId]))
52
+ params.push("render["+i+"][id]=" + partDomId)
53
+ i += 1
54
+ }
55
+ })
56
+ params.push("page_path=" + hoboPagePath)
57
+ }
58
+
59
+ if (resultUpdates) {
60
+ resultUpdates.each(function (resultUpdate) {
61
+ params.push("render["+i+"][id]=" + resultUpdate.id)
62
+ params.push("render["+i+"][result]=" + resultUpdate.result)
63
+ if (resultUpdate.func) {
64
+ params.push("render["+i+"][function]=" + resultUpdate.func)
65
+ }
66
+ i += 1
67
+ })
68
+ }
69
+ return params.join('&')
70
+ },
71
+
72
+ ajaxRequest: function(url_or_form, updates, options) {
73
+ options = Object.merge({ asynchronous:true,
74
+ evalScripts:true,
75
+ resetForm: false,
76
+ refocusForm: false,
77
+ message: "Saving..."
78
+ }, options)
79
+ if (typeof url_or_form == "string") {
80
+ var url = url_or_form
81
+ var form = false
82
+ } else {
83
+ var form = url_or_form
84
+ var url = form.action
85
+ }
86
+ var params = []
87
+
88
+ if (typeof(formAuthToken) != "undefined") {
89
+ params.push(formAuthToken.name + "=" + formAuthToken.value)
90
+ }
91
+
92
+ updateParams = Hobo.ajaxUpdateParams(updates, options.resultUpdate)
93
+ if (updateParams != "") { params.push(updateParams) }
94
+
95
+ if (options.params) {
96
+ params.push(options.params)
97
+ delete options.params
98
+ }
99
+
100
+ if (form) {
101
+ params.push(Form.serialize(form))
102
+ }
103
+
104
+ if (options.message != false) Hobo.showSpinner(options.message, options.spinnerNextTo)
105
+
106
+ var complete = function() {
107
+ if (options.message != false) Hobo.hideSpinner();
108
+ if (options.onComplete) options.onComplete.apply(this, arguments)
109
+ if (form && options.refocusForm) Form.focusFirstElement(form)
110
+ Event.addBehavior.reload()
111
+ }
112
+ var success = function() {
113
+ if (options.onSuccess) options.onSuccess.apply(this, arguments)
114
+ if (form && options.resetForm) form.reset();
115
+ }
116
+ if (options.method && options.method.toLowerCase() == "put") {
117
+ delete options.method
118
+ params.push("_method=PUT")
119
+ }
120
+
121
+ if (!options.onFailure) {
122
+ options.onFailure = function(response) {
123
+ alert(response.responseText)
124
+ }
125
+ }
126
+
127
+ new Ajax.Request(url, Object.merge(options, { parameters: params.join("&"), onComplete: complete, onSuccess: success }))
128
+ },
129
+
130
+ hide: function() {
131
+ for (i = 0; i < arguments.length; i++) {
132
+ if ($(arguments[i])) {
133
+ Element.addClassName(arguments[i], 'hidden')
134
+ }
135
+ }
136
+ },
137
+
138
+ show: function() {
139
+ for (i = 0; i < arguments.length; i++) {
140
+ if ($(arguments[i])) {
141
+ Element.removeClassName(arguments[i], 'hidden')
142
+ }
143
+ }
144
+ },
145
+
146
+ toggle: function() {
147
+ for (i = 0; i < arguments.length; i++) {
148
+ if ($(arguments[i])) {
149
+ if(Element.hasClassName(arguments[i], 'hidden')) {
150
+ Element.removeClassName(arguments[i], 'hidden')
151
+ } else {
152
+ Element.addClassName(arguments[i], 'hidden')
153
+ }
154
+ }
155
+ }
156
+ },
157
+
158
+ onFieldEditComplete: function(el, newValue) {
159
+ el = $(el)
160
+ var oldValue = Hobo.ipeOldValues[el.id]
161
+ delete Hobo.ipeOldValues[el.id]
162
+
163
+ var blank = el.getAttribute("hobo-blank-message")
164
+ if (blank && newValue.strip().length == 0) {
165
+ el.update(blank)
166
+ } else {
167
+ el.update(newValue)
168
+ }
169
+
170
+ var modelId = Hobo.getModelId(el)
171
+ if (oldValue) {
172
+ $$(".model:" + modelId).each(function(e) {
173
+ if (e != el && e.innerHTML == oldValue) e.update(newValue)
174
+ })
175
+ }
176
+ },
177
+
178
+ _makeInPlaceEditor: function(el, options) {
179
+ var old
180
+ var updates = Hobo.updatesForElement(el)
181
+ var id = el.id
182
+ if (!id) { id = el.id = Hobo.uid() }
183
+ var updateParams = Hobo.ajaxUpdateParams(updates, [{id: id,
184
+ result: 'new_field_value',
185
+ func: "Hobo.onFieldEditComplete"}])
186
+ var opts = {okButton: false,
187
+ cancelLink: false,
188
+ submitOnBlur: true,
189
+ evalScripts: true,
190
+ htmlResponse: false,
191
+ ajaxOptions: { method: "put" },
192
+ onEnterHover: null,
193
+ onLeaveHover: null,
194
+ callback: function(form, val) {
195
+ old = val
196
+ el.setAttribute("hobo-edit-text", val)
197
+ return (Hobo.fieldSetParam(el, val) + "&" + updateParams)
198
+ },
199
+ onFailure: function(_, resp) {
200
+ alert(resp.responseText); el.innerHTML = old
201
+ },
202
+ onEnterEditMode: function() {
203
+ var blank_message = el.getAttribute("hobo-blank-message")
204
+ var editable_text = el.getAttribute("hobo-edit-text")
205
+ if (editable_text) {
206
+ el.innerHTML = editable_text
207
+ }
208
+ if (el.innerHTML.gsub("&nbsp;", " ") == blank_message) {
209
+ el.innerHTML = ""
210
+ } else {
211
+ Hobo.ipeOldValues[el.id] = el.innerHTML
212
+ }
213
+ }
214
+ }
215
+ Object.extend(opts, options)
216
+ return new Ajax.InPlaceEditor(el, Hobo.putUrl(el), opts)
217
+ },
218
+
219
+
220
+ doSearch: function(el) {
221
+ el = $(el)
222
+ var spinner = $(el.getAttribute("search-spinner") || "search-spinner")
223
+ var search_results = $(el.getAttribute("search-results") || "search-results")
224
+ var search_results_panel = $(el.getAttribute("search-results-panel") || "search-results-panel")
225
+ var url = el.getAttribute("search-url") || (urlBase + "/search")
226
+
227
+ var clear = function() { Hobo.hide(search_results_panel); el.clear() }
228
+
229
+ // Close window on [Escape]
230
+ Event.observe(el, 'keypress', function(ev) {
231
+ if (ev.keyCode == 27) clear()
232
+ });
233
+
234
+ Event.observe(search_results_panel.down('.close-button'), 'click', clear)
235
+
236
+ var value = $F(el)
237
+ if (Hobo.searchRequest) { Hobo.searchRequest.transport.abort() }
238
+ if (value.length >= 3) {
239
+ if (spinner) Hobo.show(spinner);
240
+ Hobo.searchRequest = new Ajax.Updater(search_results,
241
+ url,
242
+ { asynchronous:true,
243
+ evalScripts:true,
244
+ onSuccess:function(request) {
245
+ if (spinner) Hobo.hide(spinner)
246
+ if (search_results_panel) {
247
+ Hobo.show(search_results_panel)
248
+ }
249
+ },
250
+ method: "get",
251
+ parameters:"query=" + value });
252
+ } else {
253
+ Hobo.updateElement(search_results, '')
254
+ Hobo.hide(search_results_panel)
255
+ }
256
+ },
257
+
258
+
259
+ putUrl: function(el) {
260
+ var spec = Hobo.modelSpecForElement(el)
261
+ return urlBase + "/" + Hobo.pluralise(spec.name) + "/" + spec.id + "?_method=PUT"
262
+ },
263
+
264
+
265
+ urlForId: function(id) {
266
+ var spec = Hobo.parseModelSpec(id)
267
+ var url = urlBase + "/" + Hobo.pluralise(spec.name)
268
+ if (spec.id) { url += "/" + spec.id }
269
+ return url
270
+ },
271
+
272
+
273
+ fieldSetParam: function(el, val) {
274
+ var spec = Hobo.modelSpecForElement(el)
275
+ var res = spec.name + '[' + spec.field + ']=' + encodeURIComponent(val)
276
+ if (typeof(formAuthToken) != "undefined") {
277
+ res = res + "&" + formAuthToken.name + "=" + formAuthToken.value
278
+ }
279
+ return res
280
+ },
281
+
282
+
283
+ fadeObjectElement: function(el) {
284
+ var fadeEl = Hobo.objectElementFor(el)
285
+ new Effect.Fade(fadeEl, { duration: 0.5, afterFinish: function (ef) {
286
+ ef.element.remove()
287
+ } });
288
+ Hobo.showEmptyMessageAfterLastRemove(fadeEl)
289
+ },
290
+
291
+
292
+ removeButton: function(el, url, updates, options) {
293
+ if (options.fade == null) { options.fade = true; }
294
+ if (options.confirm == null) { options.confirm = "Are you sure?"; }
295
+
296
+ if (options.confirm == false || confirm(options.confirm)) {
297
+ var objEl = Hobo.objectElementFor(el)
298
+ Hobo.showSpinner('Removing');
299
+ function complete() {
300
+ if (options.fade) { Hobo.fadeObjectElement(objEl) }
301
+ Hobo.hideSpinner()
302
+ }
303
+ if (updates && updates.length > 0) {
304
+ new Hobo.ajaxRequest(url, updates, { method:'delete', message: "Removing...", onComplete: complete});
305
+ } else {
306
+ var ajaxOptions = {asynchronous:true, evalScripts:true, method:'delete', onComplete: complete}
307
+ if (typeof(formAuthToken) != "undefined") {
308
+ ajaxOptions.parameters = formAuthToken.name + "=" + formAuthToken.value
309
+ }
310
+ new Ajax.Request(url, ajaxOptions);
311
+ }
312
+ }
313
+ },
314
+
315
+
316
+ ajaxUpdateField: function(element, field, value, updates) {
317
+ var objectElement = Hobo.objectElementFor(element)
318
+ var url = Hobo.putUrl(objectElement)
319
+ var spec = Hobo.modelSpecForElement(objectElement)
320
+ var params = spec.name + '[' + field + ']=' + encodeURIComponent(value)
321
+ new Hobo.ajaxRequest(url, updates, { method:'put', message: "Saving...", params: params });
322
+ },
323
+
324
+
325
+ showEmptyMessageAfterLastRemove: function(el) {
326
+ var empty
327
+ var container = $(el.parentNode)
328
+ if (container.getElementsByTagName(el.nodeName).length == 1 &&
329
+ (empty = container.next('.empty-collection-message'))) {
330
+ new Effect.Appear(empty, {delay:0.3})
331
+ }
332
+ },
333
+
334
+
335
+ getClassData: function(el, name) {
336
+ var match = el.className.match(new RegExp("(^| )" + name + "::(\\S+)($| )"))
337
+ return match && match[2]
338
+ },
339
+
340
+
341
+ getModelId: function(el) {
342
+ return Hobo.getClassData(el, 'model')
343
+ },
344
+
345
+
346
+ modelSpecForElement: function(el) {
347
+ var id = Hobo.getModelId(el)
348
+ return id && Hobo.parseModelSpec(id)
349
+ },
350
+
351
+
352
+ parseModelSpec: function(id) {
353
+ m = id.gsub('-', '_').match(/^([^:]+)(?::([^:]+)(?::([^:]+))?)?$/)
354
+ if (m) return { name: m[1], id: m[2], field: m[3] }
355
+ },
356
+
357
+
358
+ objectElementFor: function(el) {
359
+ var m
360
+ while(el.getAttribute) {
361
+ id = Hobo.getModelId(el)
362
+ if (id) m = id.match(/^[^:]+:[^:]+$/);
363
+ if (m) break;
364
+ el = el.parentNode;
365
+ }
366
+ if (m) return el;
367
+ },
368
+
369
+ modelIdFor: function(el) {
370
+ var e = Hobo.objectElementFor(el)
371
+ return e && Hobo.getModelId(e)
372
+ },
373
+
374
+
375
+ showSpinner: function(message, nextTo) {
376
+ clearTimeout(Hobo.spinnerTimer)
377
+ Hobo.spinnerHideAt = new Date().getTime() + Hobo.spinnerMinTime;
378
+ if (t = $('ajax-progress-text')) {
379
+ if (!message || message.length == 0) {
380
+ t.hide()
381
+ } else {
382
+ Element.update(t, message);
383
+ t.show()
384
+ }
385
+ }
386
+ if (e = $('ajax-progress')) {
387
+ if (nextTo) {
388
+ var e_nextTo = $(nextTo);
389
+ var pos = e_nextTo.cumulativeOffset()
390
+ e.style.top = pos.top - e_nextTo.offsetHeight + "px"
391
+ e.style.left = (pos.left + e_nextTo.offsetWidth + 5) + "px"
392
+ }
393
+ e.style.display = "block";
394
+ }
395
+ },
396
+
397
+
398
+ hideSpinner: function() {
399
+ if (e = $('ajax-progress')) {
400
+ var remainingTime = Hobo.spinnerHideAt - new Date().getTime()
401
+ if (remainingTime <= 0) {
402
+ e.visualEffect('Fade')
403
+ } else {
404
+ Hobo.spinnerTimer = setTimeout(function () { e.visualEffect('Fade') }, remainingTime)
405
+ }
406
+ }
407
+ },
408
+
409
+
410
+ updateElement: function(id, content) {
411
+ // TODO: Do we need this method?
412
+ Element.update(id, content)
413
+ },
414
+
415
+ getStyle: function(el, styleProp) {
416
+ if (el.currentStyle)
417
+ var y = el.currentStyle[styleProp];
418
+ else if (window.getComputedStyle)
419
+ var y = document.defaultView.getComputedStyle(el, null).getPropertyValue(styleProp);
420
+ return y;
421
+ },
422
+
423
+ partFor: function(el) {
424
+ while (el) {
425
+ if (el.id && hoboParts[el.id]) { return el }
426
+ el = el.parentNode
427
+ }
428
+ return null
429
+ },
430
+
431
+ pluralise: function(s) {
432
+ return pluralisations[s] || s + "s"
433
+ },
434
+
435
+ addUrlParams: function(params, options) {
436
+ params = $H(window.location.search.toQueryParams()).merge(params)
437
+
438
+ if (options.remove) {
439
+ var remove = (options.remove instanceof Array) ? options.remove : [options.remove]
440
+ remove.each(function(k) { params.unset(k) })
441
+ }
442
+
443
+ return window.location.href.sub(/(\?.*|$)/, "?" + params.toQueryString())
444
+ },
445
+
446
+
447
+ fixSectionGroup: function(e) {
448
+ rows = e.childElements().map(function(e, i) {
449
+ cells = e.childElements().map(function(e, i) {
450
+ return e.outerHTML.sub("<DIV", "<td valign='top'").sub(/<\/DIV>$/i, "</td>")
451
+ }).join('')
452
+
453
+ var attrs = e.outerHTML.match(/<DIV([^>]+)/)[1]
454
+ return "<tr" + attrs + ">" + cells + "</tr>"
455
+ }).join("\n")
456
+
457
+ var attrs = e.outerHTML.match(/<DIV([^>]+)/)[1]
458
+
459
+ var table= "<table cellpadding='0' cellspacing='0' border='0' style='border-collapse: collapse; border-spacing: 0'" + attrs + ">" +
460
+ rows + "</table>"
461
+ e.outerHTML = table
462
+ },
463
+
464
+ makeHtmlEditor: function(textarea) {
465
+ // do nothing - plugins can overwrite this method
466
+ }
467
+
468
+
469
+ }
470
+
471
+ Element.findContaining = function(el, tag) {
472
+ el = $(el)
473
+ tag = tag.toLowerCase()
474
+ e = el.parentNode
475
+ while (el) {
476
+ if (el.nodeName.toLowerCase() == tag) {
477
+ return el;
478
+ }
479
+ e = el.parentNode
480
+ }
481
+ return null;
482
+ }
483
+
484
+ Element.prototype.childWithClass = function(klass) {
485
+ var ret=null;
486
+ this.childElements().each(function(el2) {
487
+ if(ret==null && el2.hasClassName(klass)) ret=el2;
488
+ });
489
+ return ret;
490
+ }
491
+
492
+ // Add an afterEnterEditMode hook to in-place-editor
493
+ origEnterEditMode = Ajax.InPlaceEditor.prototype.enterEditMode
494
+ Ajax.InPlaceEditor.prototype.enterEditMode = function(evt) {
495
+ origEnterEditMode.bind(this)(evt)
496
+ if (this.afterEnterEditMode) this.afterEnterEditMode()
497
+ return false
498
+ }
499
+
500
+ // Fix Safari in-place-editor bug
501
+ Ajax.InPlaceEditor.prototype.removeForm = function() {
502
+ if (!this._form) return;
503
+
504
+ if (this._form.parentNode) { try { Element.remove(this._form); } catch (e) {}}
505
+ this._form = null;
506
+ this._controls = { };
507
+ }
508
+
509
+ // Silence errors from IE :-(
510
+ Field.scrollFreeActivate = function(field) {
511
+ setTimeout(function() {
512
+ try {
513
+ Field.activate(field);
514
+ } catch(e) {}
515
+ }, 1);
516
+ }
517
+
518
+
519
+ Element.Methods.$$ = function(e, css) {
520
+ return new Selector(css).findElements(e)
521
+ }
522
+
523
+
524
+ HoboBehavior = Class.create({
525
+
526
+ initialize: function(mainSelector, features) {
527
+ this.mainSelector = mainSelector
528
+ this.features = features
529
+ this.addEvents(mainSelector, features.events)
530
+ if (features.initialize) {
531
+ document.observe("dom:loaded", features.initialize);
532
+ }
533
+ },
534
+
535
+ addEvents: function(parentSelector, events) {
536
+ var self = this
537
+
538
+ for (selector in events) {
539
+ fullSelector = parentSelector + ' ' + selector
540
+ var rhs = events[selector]
541
+ if (Object.isString(rhs)) {
542
+ this.addBehavior(fullSelector, this.features[rhs])
543
+ } else {
544
+ this.addEvents(fullSelector, rhs)
545
+ }
546
+ }
547
+
548
+ },
549
+
550
+ addBehavior: function(selector, handler) {
551
+ var self = this
552
+ behavior = {}
553
+ behavior[selector] = function(ev) {
554
+ self.features.element = this.up(self.mainSelector)
555
+ handler.call(self.features, ev, this)
556
+ }
557
+ Event.addBehavior(behavior)
558
+ }
559
+
560
+ })
561
+
562
+
563
+ new HoboBehavior("ul.input-many", {
564
+
565
+ events: {
566
+ "> li > div.buttons": {
567
+ ".add-item:click": 'addOne',
568
+ ".remove-item:click": 'removeOne'
569
+ }
570
+ },
571
+
572
+ initialize: function(ul) {
573
+ // disable all elements inside our template, and mark them so we can find them later.
574
+ $$(".input-many-template input:enabled, .input-many-template select:enabled, .input-many-template textarea:enabled, .input-many-template button:enabled").each(function(input) {
575
+ input.disabled = true;
576
+ input.addClassName("input_many_template_input");
577
+ });
578
+ },
579
+
580
+ // given this==the input-many, returns a lambda that updates the name & id for an element
581
+ getNameUpdater: function(new_index) {
582
+ var name_prefix = Hobo.getClassData(this, 'input-many-prefix');
583
+ var id_prefix = name_prefix.replace(/\[/g, "_").replace(/\]/g, "");
584
+ var name_re = RegExp("^" + RegExp.escape(name_prefix)+ "\[\-?[0-9]+\]");
585
+ var name_sub = name_prefix + '[' + new_index.toString() + ']';
586
+ var id_re = RegExp("^" + RegExp.escape(id_prefix)+ "_\-?[0-9]+");
587
+ var id_sub = id_prefix + '_' + new_index.toString();
588
+ var class_re = RegExp(RegExp.escape(name_prefix)+ "\[\-?[0-9]+\]");
589
+ var class_sub = name_sub;
590
+
591
+ return function() {
592
+ if(this.name) {
593
+ this.name = this.name.replace(name_re, name_sub);
594
+ }
595
+ if (id_prefix==this.id.slice(0, id_prefix.length)) {
596
+ this.id = this.id.replace(id_re, id_sub);
597
+ } else {
598
+ // silly rails. text_area_tag and text_field_tag use different conventions for the id.
599
+ if(name_prefix==this.id.slice(0, name_prefix.length)) {
600
+ this.id = this.id.replace(name_re, name_sub);
601
+ } /* else {
602
+ hjq.util.log("hjq.input_many.update_id: id_prefix "+id_prefix+" didn't match input "+this.id);
603
+ } */
604
+ }
605
+ if (class_re.test(this.className)) {
606
+ this.className = this.className.replace(class_re, class_sub);
607
+ }
608
+ return this;
609
+ };
610
+ },
611
+
612
+ // given this==an input-many item, get the submit index
613
+ getIndex: function() {
614
+ return Number(this.id.match(/\[([0-9])+\]$/)[1]);
615
+ },
616
+
617
+ /* For some reason, select() and down() and all those useful functions aren't working for us. Roll our own replacement. */
618
+ recurse_elements_with_class: function(el, klass, f) {
619
+ var that=this;
620
+ if(klass==null || el.hasClassName(klass)) {
621
+ f(el);
622
+ }
623
+ el.childElements().each(function(el2) {that.recurse_elements_with_class.call(that, el2, klass, f);});
624
+ },
625
+
626
+ addOne: function(ev, el) {
627
+ Event.stop(ev);
628
+ var ul = el.up('ul.input-many'), li = el.up('li.input-many-li');
629
+
630
+ var template = ul.down("li.input-many-template");
631
+ var clone = $(template.cloneNode(true));
632
+ clone.removeClassName("input-many-template");
633
+ // length-2 because ignore the template li and the empty li
634
+ var name_updater = this.getNameUpdater.call(ul, ul.childElements().length-2);
635
+
636
+ function reenable_inputs(el) {
637
+ if(el.hasClassName("input_many_template_input")) {
638
+ el.disabled = false;
639
+ el.removeClassName("input_many_template_input");
640
+ }
641
+ el.childElements().each(function(el2) {
642
+ if(!el2.hasClassName("input-many-template")) reenable_inputs(el2);
643
+ });
644
+ }
645
+ reenable_inputs(clone);
646
+
647
+ // update id & name
648
+ this.recurse_elements_with_class.call(this, clone, null, function(el) {
649
+ name_updater.call(el);
650
+ });
651
+
652
+ // do the add with anim
653
+ clone.setStyle("display", "none")
654
+ li.insert({after: clone});
655
+ new Effect.BlindDown(clone, {duration: 0.3})
656
+
657
+ // visibility
658
+ if(li.hasClassName("empty")) {
659
+ li.addClassName("hidden");
660
+ li.childWithClass("empty-input").disabled = true;
661
+ } else {
662
+ // now that we've added an element after us, we should only have a '-' button
663
+ li.childWithClass("buttons").childWithClass("remove-item").removeClassName("hidden");
664
+ li.childWithClass("buttons").childWithClass("add-item").addClassName("hidden");
665
+ }
666
+
667
+ Event.addBehavior.reload();
668
+
669
+ ul.fire("rapid:add", { element: clone })
670
+ ul.fire("rapid:change", { element: clone })
671
+
672
+ return;
673
+ },
674
+
675
+ removeOne: function(ev, el) {
676
+ Event.stop(ev);
677
+ var that = this;
678
+ var ul = el.up('ul.input-many'), li = el.up('li.input-many-li')
679
+ var minimum = parseInt(Hobo.getClassData(ul, 'minimum'));
680
+
681
+ ul.fire("rapid:remove", { element: li })
682
+
683
+ // rename everybody from me onwards
684
+ var i=this.getIndex.call(li)
685
+ var n=li.next();
686
+ for(; n; i+=1, n=n.next()) {
687
+ var name_updater = this.getNameUpdater.call(ul, i);
688
+ this.recurse_elements_with_class.call(this, n, null, function(el) {name_updater.call(el);});
689
+ }
690
+
691
+ // adjust +/- buttons on the button element as appropriate
692
+ var last=ul.childElements()[ul.childElements().length-1];
693
+ if(last==li) {
694
+ last = last.previous();
695
+ }
696
+
697
+ if(last.hasClassName("empty")) {
698
+ last.removeClassName("hidden");
699
+ this.recurse_elements_with_class.call(this, last, "empty-input", function(el) {el.disabled=false;});
700
+ } else {
701
+ // if we've reached the minimum, we don't want to add the '-' button
702
+ if(ul.childElements().length-3 <= minimum||0) {
703
+ last.childWithClass("buttons").childWithClass("remove-item").addClassName("hidden");
704
+ } else {
705
+ last.childWithClass("buttons").childWithClass("remove-item").removeClassName("hidden");
706
+ }
707
+ last.childWithClass("buttons").childWithClass("add-item").removeClassName("hidden");
708
+ }
709
+
710
+ new Effect.BlindUp(li, { duration: 0.3, afterFinish: function (ef) {
711
+ li.remove()
712
+ } });
713
+
714
+ ul.fire("rapid:change")
715
+ },
716
+
717
+
718
+
719
+ })
720
+
721
+
722
+ SelectManyInput = Behavior.create({
723
+
724
+ initialize : function() {
725
+ // onchange doesn't bubble in IE6 so...
726
+ Event.observe(this.element.down('select'), 'change', this.addOne.bind(this))
727
+ },
728
+
729
+ addOne : function() {
730
+ var select = this.element.down('select')
731
+ var selected = select.options[select.selectedIndex]
732
+ if ($F(select) != "") {
733
+ var newItem = $(DOM.Builder.fromHTML(this.element.down('.item-proto').innerHTML.strip()))
734
+ this.element.down('.items').appendChild(newItem);
735
+ newItem.down('span').innerHTML = selected.innerHTML
736
+ this.itemAdded(newItem, selected)
737
+ var optgroup = new Element("optgroup", {alt:selected.value, label:selected.text})
738
+ optgroup.addClassName("disabled-option")
739
+ selected.replace(optgroup)
740
+ select.value = ""
741
+ Event.addBehavior.reload()
742
+ this.element.fire("rapid:add", { element: newItem })
743
+ this.element.fire("rapid:change", { element: newItem })
744
+ }
745
+ },
746
+
747
+ onclick : function(ev) {
748
+ var el = Event.element(ev);
749
+ if (el.match(".remove-item")) { this.removeOne(el.parentNode) }
750
+ },
751
+
752
+ removeOne : function(el) {
753
+ var element = this.element
754
+ new Effect.BlindUp(el,
755
+ { duration: 0.3,
756
+ afterFinish: function (ef) {
757
+ ef.element.remove()
758
+ element.fire("rapid:remove", { element: el })
759
+ element.fire("rapid:change", { element: el })
760
+ } } )
761
+ var label = el.down('span').innerHTML
762
+ var optgroup = element.down("optgroup[label="+label+"]")
763
+ var option = new Element("option", {value:optgroup.readAttribute("alt")})
764
+ option.innerHTML = optgroup.readAttribute("label")
765
+ optgroup.replace(option)
766
+ },
767
+
768
+ itemAdded: function(item, option) {
769
+ this.hiddenField(item).value = option.value
770
+ },
771
+
772
+ hiddenField: function(item) {
773
+ return item.down('input[type=hidden]')
774
+ //return item.getElementsByClassName("hidden-field")[0]
775
+ }
776
+
777
+
778
+ })
779
+
780
+ NameManyInput = Object.extend(SelectManyInput, {
781
+ addOne : function() {
782
+ var select = this.element.down('select')
783
+ var selected = select.options[select.selectedIndex]
784
+ if (selected.value != "") {
785
+ var newItem = $(DOM.Builder.fromHTML(this.element.down('.item-proto').innerHTML.strip()))
786
+ this.element.down('.items').appendChild(newItem);
787
+ newItem.down('span').innerHTML = selected.innerHTML
788
+ this.itemAdded(newItem, selected)
789
+ selected.disabled = true
790
+ select.value = ""
791
+ Event.addBehavior.reload()
792
+ }
793
+ }
794
+ })
795
+
796
+
797
+ AutocompleteBehavior = Behavior.create({
798
+ initialize : function() {
799
+ var match = this.element.className.match(/complete-on::([\S]+)/)
800
+ var target = match[1].split('::')
801
+ var typedId = target[0]
802
+ var completer = target[1]
803
+
804
+ var spec = Hobo.parseModelSpec(typedId)
805
+ var url = urlBase + "/" + Hobo.pluralise(spec.name) + "/complete_" + completer
806
+ var parameters = spec.id ? "id=" + spec.id : ""
807
+ new Ajax.Autocompleter(this.element,
808
+ this.element.next('.completions-popup'),
809
+ url,
810
+ {paramName:'query', method:'get', parameters: parameters});
811
+ }
812
+ })
813
+
814
+
815
+
816
+ Event.addBehavior.reassignAfterAjax = true;
817
+ Event.addBehavior({
818
+
819
+ 'div.section-group' : function() {
820
+ if (Prototype.Browser.IE) Hobo.fixSectionGroup(this);
821
+ },
822
+
823
+ 'div.select-many.input' : SelectManyInput(),
824
+
825
+ 'textarea.html' : function() {
826
+ Hobo.makeHtmlEditor(this)
827
+ },
828
+
829
+ 'form.filter-menu select:change': function(event) {
830
+ var paramName = this.getAttribute('name')
831
+ var params = {}
832
+ var remove = [ 'page' ]
833
+ if ($F(this) == '') {
834
+ remove.push(paramName)
835
+ } else {
836
+ params[paramName] = $F(this)
837
+ }
838
+ location.href = Hobo.addUrlParams(params, {remove: remove})
839
+ },
840
+
841
+ '.autocompleter' : AutocompleteBehavior(),
842
+
843
+ '.string.in-place-edit, .datetime.in-place-edit, .date.in-place-edit, .integer.in-place-edit, .float.in-place-edit, .decimal.in-place-edit' :
844
+ function (ev) {
845
+
846
+ var ipe = Hobo._makeInPlaceEditor(this)
847
+ ipe.getText = function() {
848
+ return this.element.innerHTML.gsub(/<br\s*\/?>/, "\n").unescapeHTML()
849
+ }
850
+ },
851
+
852
+ '.text.in-place-edit, .markdown.in-place-edit, .textile.in-place-edit' : function (ev) {
853
+ var ipe = Hobo._makeInPlaceEditor(this, {rows: 2})
854
+ ipe.getText = function() {
855
+ return this.element.innerHTML.gsub(/<br\s*\/?>/, "\n").unescapeHTML()
856
+ }
857
+ },
858
+
859
+ ".html.in-place-edit" : function (ev) {
860
+ if (Hobo.makeInPlaceHtmlEditor) {
861
+ Hobo.makeInPlaceHtmlEditor(this)
862
+ } else {
863
+ var options = {
864
+ rows: 2, handleLineBreaks: false, okButton: true, cancelLink: true, okText: "Save", submitOnBlur: false
865
+ }
866
+ var ipe = Hobo._makeInPlaceEditor(this, options)
867
+ ipe.getText = function() {
868
+ // Be careful! we're not calling unescapeHTML() here!
869
+ return this.element.innerHTML
870
+ }
871
+ }
872
+ },
873
+
874
+ "select.integer.editor" : function(e) {
875
+ var el = this
876
+ el.onchange = function() {
877
+ Hobo.ajaxSetFieldForElement(el, $F(el))
878
+ }
879
+ },
880
+
881
+ "input.live-search[type=search]" : function(e) {
882
+ var element = this
883
+ new Form.Element.Observer(element, 1.0, function() { Hobo.doSearch(element) })
884
+ }
885
+
886
+
887
+ });
888
+
889
+ ElementSet = Class.create(Enumerable, {
890
+
891
+ initialize: function(array) {
892
+ this.items = array
893
+ },
894
+
895
+ _each: function(fn) {
896
+ return this.items.each(fn)
897
+ },
898
+
899
+ selectChildren: function(selector) {
900
+ return new ElementSet(this.items.invoke('selectChildren', selector).pluck('items').flatten())
901
+ },
902
+
903
+ child: function(selector) {
904
+ return this.selectChildren(selector).first()
905
+ },
906
+
907
+ select: function(selector) {
908
+ return new ElementSet(this.items.invoke('select', selector).flatten())
909
+ },
910
+
911
+ down: function(selector) {
912
+ for (var i = 0; i < this.items.length; i++) {
913
+ var match = this.items[i].down(selector)
914
+ if (match) return match
915
+ }
916
+ return null
917
+ },
918
+
919
+ size: function() {
920
+ return this.items.length
921
+ },
922
+
923
+ first: function() {
924
+ return this.items.first()
925
+ },
926
+
927
+ last: function() {
928
+ return this.items.last()
929
+ }
930
+
931
+ })
932
+
933
+ Element.addMethods({
934
+ selectChildren: function(element, selector) {
935
+ return new ElementSet(Selector.matchElements(element.childElements(), selector))
936
+ }
937
+ })