active_scaffold_clarity 3.0.24.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (284) hide show
  1. data/CHANGELOG +179 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +73 -0
  4. data/frontends/default/images/add.gif +0 -0
  5. data/frontends/default/images/arrow_down.gif +0 -0
  6. data/frontends/default/images/arrow_up.gif +0 -0
  7. data/frontends/default/images/close.gif +0 -0
  8. data/frontends/default/images/close_touch.png +0 -0
  9. data/frontends/default/images/config.png +0 -0
  10. data/frontends/default/images/cross.png +0 -0
  11. data/frontends/default/images/gears.png +0 -0
  12. data/frontends/default/images/indicator-small.gif +0 -0
  13. data/frontends/default/images/indicator.gif +0 -0
  14. data/frontends/default/images/magnifier.png +0 -0
  15. data/frontends/default/javascripts/jquery/active_scaffold.js +1049 -0
  16. data/frontends/default/javascripts/jquery/jquery.editinplace.js +743 -0
  17. data/frontends/default/javascripts/prototype/active_scaffold.js +1031 -0
  18. data/frontends/default/javascripts/prototype/dhtml_history.js +870 -0
  19. data/frontends/default/javascripts/prototype/form_enhancements.js +117 -0
  20. data/frontends/default/javascripts/prototype/rico_corner.js +370 -0
  21. data/frontends/default/stylesheets/stylesheet-ie.css +35 -0
  22. data/frontends/default/stylesheets/stylesheet.css +1093 -0
  23. data/frontends/default/views/_action_group.html.erb +24 -0
  24. data/frontends/default/views/_add_existing_form.html.erb +30 -0
  25. data/frontends/default/views/_base_form.html.erb +51 -0
  26. data/frontends/default/views/_create_form.html.erb +8 -0
  27. data/frontends/default/views/_create_form_on_list.html.erb +6 -0
  28. data/frontends/default/views/_field_search.html.erb +32 -0
  29. data/frontends/default/views/_form.html.erb +26 -0
  30. data/frontends/default/views/_form_association.html.erb +19 -0
  31. data/frontends/default/views/_form_association_footer.html.erb +47 -0
  32. data/frontends/default/views/_form_attribute.html.erb +20 -0
  33. data/frontends/default/views/_form_hidden_attribute.html.erb +2 -0
  34. data/frontends/default/views/_form_messages.html.erb +5 -0
  35. data/frontends/default/views/_horizontal_subform.html.erb +29 -0
  36. data/frontends/default/views/_horizontal_subform_header.html.erb +11 -0
  37. data/frontends/default/views/_horizontal_subform_record.html.erb +38 -0
  38. data/frontends/default/views/_human_conditions.html.erb +1 -0
  39. data/frontends/default/views/_list.html.erb +18 -0
  40. data/frontends/default/views/_list_actions.html.erb +15 -0
  41. data/frontends/default/views/_list_calculations.html.erb +16 -0
  42. data/frontends/default/views/_list_column_headings.html.erb +12 -0
  43. data/frontends/default/views/_list_header.html.erb +10 -0
  44. data/frontends/default/views/_list_inline_adapter.html.erb +10 -0
  45. data/frontends/default/views/_list_messages.html.erb +32 -0
  46. data/frontends/default/views/_list_pagination.html.erb +11 -0
  47. data/frontends/default/views/_list_pagination_links.html.erb +9 -0
  48. data/frontends/default/views/_list_record.html.erb +14 -0
  49. data/frontends/default/views/_list_record_columns.html.erb +8 -0
  50. data/frontends/default/views/_list_with_header.html.erb +32 -0
  51. data/frontends/default/views/_messages.html.erb +10 -0
  52. data/frontends/default/views/_render_field.js.rjs +14 -0
  53. data/frontends/default/views/_row.html.erb +12 -0
  54. data/frontends/default/views/_search.html.erb +34 -0
  55. data/frontends/default/views/_search_attribute.html.erb +10 -0
  56. data/frontends/default/views/_show.html.erb +8 -0
  57. data/frontends/default/views/_show_columns.html.erb +15 -0
  58. data/frontends/default/views/_update_actions.html.erb +9 -0
  59. data/frontends/default/views/_update_form.html.erb +6 -0
  60. data/frontends/default/views/_vertical_subform.html.erb +12 -0
  61. data/frontends/default/views/_vertical_subform_record.html.erb +38 -0
  62. data/frontends/default/views/action_confirmation.html.erb +13 -0
  63. data/frontends/default/views/add_existing.js.rjs +17 -0
  64. data/frontends/default/views/add_existing_form.html.erb +5 -0
  65. data/frontends/default/views/create.html.erb +5 -0
  66. data/frontends/default/views/delete.html.erb +13 -0
  67. data/frontends/default/views/destroy.js.rjs +23 -0
  68. data/frontends/default/views/edit_associated.js.rjs +11 -0
  69. data/frontends/default/views/field_search.html.erb +5 -0
  70. data/frontends/default/views/form_messages.js.rjs +1 -0
  71. data/frontends/default/views/list.html.erb +1 -0
  72. data/frontends/default/views/list.js.rjs +1 -0
  73. data/frontends/default/views/on_action_update.js.rjs +10 -0
  74. data/frontends/default/views/on_create.js.rjs +41 -0
  75. data/frontends/default/views/on_mark_all.js.rjs +12 -0
  76. data/frontends/default/views/on_update.js.rjs +28 -0
  77. data/frontends/default/views/render_field.js.rjs +1 -0
  78. data/frontends/default/views/search.html.erb +5 -0
  79. data/frontends/default/views/show.html.erb +5 -0
  80. data/frontends/default/views/update.html.erb +8 -0
  81. data/frontends/default/views/update_column.js.rjs +13 -0
  82. data/frontends/default/views/update_row.js.rjs +1 -0
  83. data/lib/active_scaffold.rb +386 -0
  84. data/lib/active_scaffold/actions/common_search.rb +22 -0
  85. data/lib/active_scaffold/actions/core.rb +191 -0
  86. data/lib/active_scaffold/actions/create.rb +149 -0
  87. data/lib/active_scaffold/actions/delete.rb +75 -0
  88. data/lib/active_scaffold/actions/field_search.rb +82 -0
  89. data/lib/active_scaffold/actions/list.rb +194 -0
  90. data/lib/active_scaffold/actions/mark.rb +75 -0
  91. data/lib/active_scaffold/actions/nested.rb +254 -0
  92. data/lib/active_scaffold/actions/search.rb +47 -0
  93. data/lib/active_scaffold/actions/show.rb +61 -0
  94. data/lib/active_scaffold/actions/subform.rb +27 -0
  95. data/lib/active_scaffold/actions/update.rb +151 -0
  96. data/lib/active_scaffold/active_record_permissions.rb +134 -0
  97. data/lib/active_scaffold/attribute_params.rb +212 -0
  98. data/lib/active_scaffold/bridges/ancestry/bridge.rb +5 -0
  99. data/lib/active_scaffold/bridges/ancestry/lib/ancestry_bridge.rb +39 -0
  100. data/lib/active_scaffold/bridges/bridge.rb +59 -0
  101. data/lib/active_scaffold/bridges/calendar_date_select/bridge.rb +16 -0
  102. data/lib/active_scaffold/bridges/calendar_date_select/lib/as_cds_bridge.rb +83 -0
  103. data/lib/active_scaffold/bridges/cancan/bridge.rb +12 -0
  104. data/lib/active_scaffold/bridges/cancan/lib/cancan_bridge.rb +107 -0
  105. data/lib/active_scaffold/bridges/carrierwave/bridge.rb +9 -0
  106. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge.rb +33 -0
  107. data/lib/active_scaffold/bridges/carrierwave/lib/carrierwave_bridge_helpers.rb +12 -0
  108. data/lib/active_scaffold/bridges/carrierwave/lib/form_ui.rb +45 -0
  109. data/lib/active_scaffold/bridges/carrierwave/lib/list_ui.rb +17 -0
  110. data/lib/active_scaffold/bridges/country_helper/bridge.rb +9 -0
  111. data/lib/active_scaffold/bridges/country_helper/lib/country_helper_bridge.rb +358 -0
  112. data/lib/active_scaffold/bridges/date_picker/bridge.rb +24 -0
  113. data/lib/active_scaffold/bridges/date_picker/lib/datepicker_bridge.rb +234 -0
  114. data/lib/active_scaffold/bridges/date_picker/public/javascripts/date_picker_bridge.js +22 -0
  115. data/lib/active_scaffold/bridges/dragonfly/bridge.rb +9 -0
  116. data/lib/active_scaffold/bridges/dragonfly/lib/dragonfly_bridge.rb +36 -0
  117. data/lib/active_scaffold/bridges/dragonfly/lib/dragonfly_bridge_helpers.rb +12 -0
  118. data/lib/active_scaffold/bridges/dragonfly/lib/form_ui.rb +27 -0
  119. data/lib/active_scaffold/bridges/dragonfly/lib/list_ui.rb +16 -0
  120. data/lib/active_scaffold/bridges/file_column/bridge.rb +11 -0
  121. data/lib/active_scaffold/bridges/file_column/lib/as_file_column_bridge.rb +46 -0
  122. data/lib/active_scaffold/bridges/file_column/lib/file_column_helpers.rb +59 -0
  123. data/lib/active_scaffold/bridges/file_column/lib/form_ui.rb +37 -0
  124. data/lib/active_scaffold/bridges/file_column/lib/list_ui.rb +26 -0
  125. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +43 -0
  126. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +9 -0
  127. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +15 -0
  128. data/lib/active_scaffold/bridges/paperclip/bridge.rb +12 -0
  129. data/lib/active_scaffold/bridges/paperclip/lib/form_ui.rb +27 -0
  130. data/lib/active_scaffold/bridges/paperclip/lib/list_ui.rb +16 -0
  131. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge.rb +38 -0
  132. data/lib/active_scaffold/bridges/paperclip/lib/paperclip_bridge_helpers.rb +26 -0
  133. data/lib/active_scaffold/bridges/record_select/bridge.rb +5 -0
  134. data/lib/active_scaffold/bridges/record_select/lib/record_select_bridge.rb +92 -0
  135. data/lib/active_scaffold/bridges/semantic_attributes/bridge.rb +5 -0
  136. data/lib/active_scaffold/bridges/semantic_attributes/lib/semantic_attributes_bridge.rb +20 -0
  137. data/lib/active_scaffold/bridges/shared/date_bridge.rb +209 -0
  138. data/lib/active_scaffold/bridges/tiny_mce/bridge.rb +5 -0
  139. data/lib/active_scaffold/bridges/tiny_mce/lib/tiny_mce_bridge.rb +61 -0
  140. data/lib/active_scaffold/bridges/validation_reflection/bridge.rb +8 -0
  141. data/lib/active_scaffold/bridges/validation_reflection/lib/validation_reflection_bridge.rb +21 -0
  142. data/lib/active_scaffold/config/base.rb +62 -0
  143. data/lib/active_scaffold/config/core.rb +220 -0
  144. data/lib/active_scaffold/config/create.rb +51 -0
  145. data/lib/active_scaffold/config/delete.rb +34 -0
  146. data/lib/active_scaffold/config/field_search.rb +75 -0
  147. data/lib/active_scaffold/config/form.rb +53 -0
  148. data/lib/active_scaffold/config/list.rb +188 -0
  149. data/lib/active_scaffold/config/mark.rb +35 -0
  150. data/lib/active_scaffold/config/nested.rb +45 -0
  151. data/lib/active_scaffold/config/search.rb +69 -0
  152. data/lib/active_scaffold/config/show.rb +35 -0
  153. data/lib/active_scaffold/config/subform.rb +35 -0
  154. data/lib/active_scaffold/config/update.rb +46 -0
  155. data/lib/active_scaffold/configurable.rb +29 -0
  156. data/lib/active_scaffold/constraints.rb +184 -0
  157. data/lib/active_scaffold/data_structures/action_columns.rb +137 -0
  158. data/lib/active_scaffold/data_structures/action_link.rb +175 -0
  159. data/lib/active_scaffold/data_structures/action_links.rb +185 -0
  160. data/lib/active_scaffold/data_structures/actions.rb +45 -0
  161. data/lib/active_scaffold/data_structures/column.rb +366 -0
  162. data/lib/active_scaffold/data_structures/columns.rb +75 -0
  163. data/lib/active_scaffold/data_structures/error_message.rb +24 -0
  164. data/lib/active_scaffold/data_structures/nested_info.rb +123 -0
  165. data/lib/active_scaffold/data_structures/set.rb +62 -0
  166. data/lib/active_scaffold/data_structures/sorting.rb +168 -0
  167. data/lib/active_scaffold/extensions/action_controller_rendering.rb +22 -0
  168. data/lib/active_scaffold/extensions/action_view_rendering.rb +111 -0
  169. data/lib/active_scaffold/extensions/action_view_resolver.rb +7 -0
  170. data/lib/active_scaffold/extensions/active_association_reflection.rb +13 -0
  171. data/lib/active_scaffold/extensions/active_record_offset.rb +12 -0
  172. data/lib/active_scaffold/extensions/array.rb +7 -0
  173. data/lib/active_scaffold/extensions/localize.rb +10 -0
  174. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +12 -0
  175. data/lib/active_scaffold/extensions/nil_id_in_url_params.rb +7 -0
  176. data/lib/active_scaffold/extensions/paginator_extensions.rb +26 -0
  177. data/lib/active_scaffold/extensions/reverse_associations.rb +62 -0
  178. data/lib/active_scaffold/extensions/routing_mapper.rb +34 -0
  179. data/lib/active_scaffold/extensions/to_label.rb +8 -0
  180. data/lib/active_scaffold/extensions/unsaved_associated.rb +61 -0
  181. data/lib/active_scaffold/extensions/unsaved_record.rb +20 -0
  182. data/lib/active_scaffold/extensions/usa_state.rb +46 -0
  183. data/lib/active_scaffold/finder.rb +345 -0
  184. data/lib/active_scaffold/helpers/association_helpers.rb +42 -0
  185. data/lib/active_scaffold/helpers/controller_helpers.rb +87 -0
  186. data/lib/active_scaffold/helpers/form_column_helpers.rb +321 -0
  187. data/lib/active_scaffold/helpers/human_condition_helpers.rb +59 -0
  188. data/lib/active_scaffold/helpers/id_helpers.rb +127 -0
  189. data/lib/active_scaffold/helpers/list_column_helpers.rb +375 -0
  190. data/lib/active_scaffold/helpers/pagination_helpers.rb +55 -0
  191. data/lib/active_scaffold/helpers/search_column_helpers.rb +254 -0
  192. data/lib/active_scaffold/helpers/show_column_helpers.rb +50 -0
  193. data/lib/active_scaffold/helpers/view_helpers.rb +379 -0
  194. data/lib/active_scaffold/locale/de.yml +111 -0
  195. data/lib/active_scaffold/locale/en.yml +115 -0
  196. data/lib/active_scaffold/locale/es.yml +115 -0
  197. data/lib/active_scaffold/locale/fr.yml +118 -0
  198. data/lib/active_scaffold/locale/hu.yml +63 -0
  199. data/lib/active_scaffold/locale/ja.yml +64 -0
  200. data/lib/active_scaffold/locale/ru.yml +119 -0
  201. data/lib/active_scaffold/marked_model.rb +38 -0
  202. data/lib/active_scaffold/paginator.rb +136 -0
  203. data/lib/active_scaffold/responds_to_parent.rb +70 -0
  204. data/lib/active_scaffold/version.rb +10 -0
  205. data/lib/active_scaffold_assets.rb +45 -0
  206. data/lib/active_scaffold_env.rb +13 -0
  207. data/lib/generators/active_scaffold/USAGE +29 -0
  208. data/lib/generators/active_scaffold/active_scaffold_generator.rb +20 -0
  209. data/lib/generators/active_scaffold_controller/USAGE +19 -0
  210. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +29 -0
  211. data/lib/generators/active_scaffold_controller/templates/controller.rb +4 -0
  212. data/lib/generators/active_scaffold_controller/templates/helper.rb +2 -0
  213. data/lib/generators/active_scaffold_setup/USAGE +10 -0
  214. data/lib/generators/active_scaffold_setup/active_scaffold_setup_generator.rb +59 -0
  215. data/public/blank.html +33 -0
  216. data/shoulda_macros/macros.rb +136 -0
  217. data/test/bridges/bridge_test.rb +47 -0
  218. data/test/config/base_test.rb +15 -0
  219. data/test/config/create_test.rb +55 -0
  220. data/test/config/list_test.rb +74 -0
  221. data/test/config/show_test.rb +43 -0
  222. data/test/config/update_test.rb +17 -0
  223. data/test/const_mocker.rb +36 -0
  224. data/test/data_structures/action_columns_test.rb +113 -0
  225. data/test/data_structures/action_link_test.rb +78 -0
  226. data/test/data_structures/action_links_test.rb +78 -0
  227. data/test/data_structures/actions_test.rb +25 -0
  228. data/test/data_structures/association_column_test.rb +42 -0
  229. data/test/data_structures/column_test.rb +185 -0
  230. data/test/data_structures/columns_test.rb +69 -0
  231. data/test/data_structures/error_message_test.rb +28 -0
  232. data/test/data_structures/set_test.rb +86 -0
  233. data/test/data_structures/sorting_test.rb +126 -0
  234. data/test/data_structures/standard_column_test.rb +24 -0
  235. data/test/data_structures/virtual_column_test.rb +23 -0
  236. data/test/extensions/active_record_test.rb +45 -0
  237. data/test/extensions/array_test.rb +12 -0
  238. data/test/helpers/form_column_helpers_test.rb +31 -0
  239. data/test/helpers/list_column_helpers_test.rb +31 -0
  240. data/test/helpers/pagination_helpers_test.rb +55 -0
  241. data/test/misc/active_record_permissions_test.rb +154 -0
  242. data/test/misc/attribute_params_test.rb +110 -0
  243. data/test/misc/configurable_test.rb +96 -0
  244. data/test/misc/constraints_test.rb +193 -0
  245. data/test/misc/finder_test.rb +93 -0
  246. data/test/misc/lang_test.rb +12 -0
  247. data/test/mock_app/app/controllers/application_controller.rb +10 -0
  248. data/test/mock_app/app/helpers/application_helper.rb +3 -0
  249. data/test/mock_app/config/boot.rb +110 -0
  250. data/test/mock_app/config/database.yml +16 -0
  251. data/test/mock_app/config/environment.rb +43 -0
  252. data/test/mock_app/config/environments/development.rb +17 -0
  253. data/test/mock_app/config/environments/production.rb +28 -0
  254. data/test/mock_app/config/environments/test.rb +28 -0
  255. data/test/mock_app/config/initializers/backtrace_silencers.rb +7 -0
  256. data/test/mock_app/config/initializers/inflections.rb +10 -0
  257. data/test/mock_app/config/initializers/mime_types.rb +5 -0
  258. data/test/mock_app/config/initializers/new_rails_defaults.rb +19 -0
  259. data/test/mock_app/config/initializers/session_store.rb +15 -0
  260. data/test/mock_app/config/locales/en.yml +5 -0
  261. data/test/mock_app/config/routes.rb +43 -0
  262. data/test/mock_app/db/test.sqlite3 +1 -0
  263. data/test/mock_app/public/blank.html +33 -0
  264. data/test/mock_app/public/images/active_scaffold/DO_NOT_EDIT +2 -0
  265. data/test/mock_app/public/images/active_scaffold/default/add.gif +0 -0
  266. data/test/mock_app/public/images/active_scaffold/default/arrow_down.gif +0 -0
  267. data/test/mock_app/public/images/active_scaffold/default/arrow_up.gif +0 -0
  268. data/test/mock_app/public/images/active_scaffold/default/close.gif +0 -0
  269. data/test/mock_app/public/images/active_scaffold/default/cross.png +0 -0
  270. data/test/mock_app/public/images/active_scaffold/default/indicator-small.gif +0 -0
  271. data/test/mock_app/public/images/active_scaffold/default/indicator.gif +0 -0
  272. data/test/mock_app/public/images/active_scaffold/default/magnifier.png +0 -0
  273. data/test/mock_app/public/javascripts/active_scaffold/DO_NOT_EDIT +2 -0
  274. data/test/mock_app/public/javascripts/active_scaffold/default/active_scaffold.js +532 -0
  275. data/test/mock_app/public/javascripts/active_scaffold/default/dhtml_history.js +867 -0
  276. data/test/mock_app/public/javascripts/active_scaffold/default/form_enhancements.js +117 -0
  277. data/test/mock_app/public/javascripts/active_scaffold/default/rico_corner.js +370 -0
  278. data/test/mock_app/public/stylesheets/active_scaffold/DO_NOT_EDIT +2 -0
  279. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet-ie.css +35 -0
  280. data/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet.css +842 -0
  281. data/test/model_stub.rb +55 -0
  282. data/test/run_all.rb +8 -0
  283. data/test/test_helper.rb +39 -0
  284. metadata +470 -0
@@ -0,0 +1,870 @@
1
+ /*
2
+ Copyright (c) 2007 Brian Dillard and Brad Neuberg:
3
+ Brian Dillard | Project Lead | bdillard@pathf.com | http://blogs.pathf.com/agileajax/
4
+ Brad Neuberg | Original Project Creator | http://codinginparadise.org
5
+
6
+ SVN r113 from http://code.google.com/p/reallysimplehistory
7
+ + Changes by Ed Wildgoose - MailASail
8
+ + Changed EncodeURIComponent -> EncodeURI
9
+ + Changed DecodeURIComponent -> DecodeURI
10
+ + Changed 'blank.html?' -> '/blank.html?'
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
13
+ (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
14
+ publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
15
+ so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
21
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+
25
+ /*
26
+ dhtmlHistory: An object that provides history, history data, and bookmarking for DHTML and Ajax applications.
27
+
28
+ dependencies:
29
+ * the historyStorage object included in this file.
30
+
31
+ */
32
+ window.dhtmlHistory = {
33
+
34
+ /*Public: User-agent booleans*/
35
+ isIE: false,
36
+ isOpera: false,
37
+ isSafari: false,
38
+ isKonquerer: false,
39
+ isGecko: false,
40
+ isSupported: false,
41
+
42
+ /*Public: Create the DHTML history infrastructure*/
43
+ create: function(options) {
44
+
45
+ /*
46
+ options - object to store initialization parameters
47
+ options.blankURL - string to override the default location of blank.html. Must end in "?"
48
+ options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
49
+ options.toJSON - function to override default JSON stringifier
50
+ options.fromJSON - function to override default JSON parser
51
+ options.baseTitle - pattern for title changes; example: "Armchair DJ [@@@]" - @@@ will be replaced
52
+ */
53
+
54
+ var that = this;
55
+
56
+ /*set user-agent flags*/
57
+ var UA = navigator.userAgent.toLowerCase();
58
+ var platform = navigator.platform.toLowerCase();
59
+ var vendor = navigator.vendor || "";
60
+ if (vendor === "KDE") {
61
+ this.isKonqueror = true;
62
+ this.isSupported = false;
63
+ } else if (typeof window.opera !== "undefined") {
64
+ this.isOpera = true;
65
+ this.isSupported = true;
66
+ } else if (typeof document.all !== "undefined") {
67
+ this.isIE = true;
68
+ this.isSupported = true;
69
+ } else if (vendor.indexOf("Apple Computer, Inc.") > -1) {
70
+ this.isSafari = true;
71
+ //this.isSupported = (platform.indexOf("mac") > -1);
72
+ this.isSupported = false;
73
+ } else if (UA.indexOf("gecko") != -1) {
74
+ this.isGecko = true;
75
+ this.isSupported = true;
76
+ }
77
+
78
+ if (this.isSupported) {
79
+ /*Set up the historyStorage object; pass in options bundle*/
80
+ window.historyStorage.setup(options);
81
+
82
+ /*Set up our base title if one is passed in*/
83
+ if (options && options.baseTitle) {
84
+ if (options.baseTitle.indexOf("@@@") < 0 && historyStorage.debugMode) {
85
+ throw new Error("Programmer error: options.baseTitle must contain the replacement parameter"
86
+ + " '@@@' to be useful.");
87
+ }
88
+ this.baseTitle = options.baseTitle;
89
+ }
90
+
91
+ /*Create Safari/Opera-specific code*/
92
+ if (this.isSafari && this.isSupported) {
93
+ this.createSafari();
94
+ } else if (this.isOpera) {
95
+ this.createOpera();
96
+ }
97
+
98
+ /*Get our initial location*/
99
+ var initialHash = this.getCurrentLocation();
100
+
101
+ /*Save it as our current location*/
102
+ this.currentLocation = initialHash;
103
+
104
+ /*Now that we have a hash, create IE-specific code*/
105
+ if (this.isIE) {
106
+ /*Optionally override the URL of IE's blank HTML file*/
107
+ if (options && options.blankURL) {
108
+ var u = options.blankURL;
109
+ /*assign the value, adding the trailing ? if it's not passed in*/
110
+ this.blankURL = (u.indexOf("?") != u.length - 1
111
+ ? u + "?"
112
+ : u
113
+ );
114
+ }
115
+ this.createIE(initialHash);
116
+ }
117
+
118
+ /*Add an unload listener for the page; this is needed for FF 1.5+ because this browser caches all dynamic updates to the
119
+ page, which can break some of our logic related to testing whether this is the first instance a page has loaded or whether
120
+ it is being pulled from the cache*/
121
+
122
+ var unloadHandler = function() {
123
+ that.firstLoad = null;
124
+ };
125
+
126
+ this.addEventListener(window,'unload',unloadHandler);
127
+
128
+ /*Determine if this is our first page load; for IE, we do this in this.iframeLoaded(), which is fired on pageload. We do it
129
+ there because we have no historyStorage at this point, which only exists after the page is finished loading in IE*/
130
+ if (this.isIE) {
131
+ /*The iframe will get loaded on page load, and we want to ignore this fact*/
132
+ this.ignoreLocationChange = true;
133
+ } else if (this.isSupported) {
134
+ if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
135
+ /*This is our first page load, so ignore the location change and add our special history entry*/
136
+ this.ignoreLocationChange = true;
137
+ this.firstLoad = true;
138
+ historyStorage.put(this.PAGELOADEDSTRING, true);
139
+ } else {
140
+ /*This isn't our first page load, so indicate that we want to pay attention to this location change*/
141
+ this.ignoreLocationChange = false;
142
+ this.firstLoad = false;
143
+ /*For browsers other than IE, fire a history change event; on IE, the event will be thrown automatically when its
144
+ hidden iframe reloads on page load. Unfortunately, we don't have any listeners yet; indicate that we want to fire
145
+ an event when a listener is added.*/
146
+ this.fireOnNewListener = true;
147
+ }
148
+ }
149
+
150
+ /*Other browsers can use a location handler that checks at regular intervals as their primary mechanism; we use it for IE as
151
+ well to handle an important edge case; see checkLocation() for details*/
152
+ var locationHandler = function() {
153
+ that.checkLocation();
154
+ };
155
+ setInterval(locationHandler, 100);
156
+ }
157
+ },
158
+
159
+ /*Public: Initialize our DHTML history. You must call this after the page is finished loading. Optionally, you can pass your listener in
160
+ here so you don't need to make a separate call to addListener*/
161
+ initialize: function(listener) {
162
+
163
+ /*save original document title to plug in when we hit a null-key history point*/
164
+ this.originalTitle = document.title;
165
+
166
+ /*IE needs to be explicitly initialized. IE doesn't autofill form data until the page is finished loading, so we have to wait*/
167
+ if (this.isIE) {
168
+ /*If this is the first time this page has loaded*/
169
+ if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
170
+ /*For IE, we do this in initialize(); for other browsers, we do it in create()*/
171
+ this.fireOnNewListener = false;
172
+ this.firstLoad = true;
173
+ historyStorage.put(this.PAGELOADEDSTRING, true);
174
+ }
175
+ /*Else if this is a fake onload event*/
176
+ else {
177
+ this.fireOnNewListener = true;
178
+ this.firstLoad = false;
179
+ }
180
+ }
181
+ /*optional convenience to save a separate call to addListener*/
182
+ if (listener) {
183
+ this.addListener(listener);
184
+ }
185
+ },
186
+
187
+ /*Public: Adds a history change listener. Only one listener is supported at this time.*/
188
+ addListener: function(listener) {
189
+ this.listener = listener;
190
+ /*If the page was just loaded and we should not ignore it, fire an event to our new listener now*/
191
+ if (this.fireOnNewListener) {
192
+ this.fireHistoryEvent(this.currentLocation);
193
+ this.fireOnNewListener = false;
194
+ }
195
+ },
196
+
197
+ /*Public: Change the current HTML title*/
198
+ changeTitle: function(historyData) {
199
+ var winTitle = (historyData && historyData.newTitle
200
+ /*Plug the new title into the pattern*/
201
+ ? this.baseTitle.replace('@@@', historyData.newTitle)
202
+ /*Otherwise, if there is no new title, use the original document title. This is useful when some
203
+ history changes have title changes and some don't; we can automatically return to the original
204
+ title rather than leaving a misleading title in the title bar. The same goes for our "virgin"
205
+ (hashless) page state.*/
206
+ : this.originalTitle
207
+ );
208
+ /*No need to do anything if the title isn't changing*/
209
+ if (document.title == winTitle) {
210
+ return;
211
+ }
212
+
213
+
214
+ /*Now change the DOM*/
215
+ document.title = winTitle;
216
+ /*Change it in the iframe, too, for IE*/
217
+ if (this.isIE) {
218
+ this.iframe.contentWindow.document.title = winTitle;
219
+ }
220
+
221
+ /*If non-IE, reload the hash so the new title "sticks" in the browser history object*/
222
+ if (!this.isIE && !this.isOpera) {
223
+ var hash = decodeURI(document.location.hash);
224
+ if (hash != "") {
225
+ var encodedHash = encodeURI(this.removeHash(hash));
226
+ document.location.hash = encodedHash;
227
+ } else {
228
+ //document.location.hash = "#";
229
+ }
230
+ }
231
+ },
232
+
233
+ /*Public: Add a history point. Parameters available:
234
+ * newLocation (required):
235
+ This will be the #hash value in the URL. Users can bookmark it. It will persist across sessions, so
236
+ your application should be able to restore itself to a specific state based on just this value. It
237
+ should be either a simple keyword for a viewstate or else a pseudo-querystring.
238
+ * historyData (optional):
239
+ This is for complex data that is relevant only to the current browsing session. It will be available
240
+ to your application until the browser is closed. If the user comes back to a bookmarked history point
241
+ during a later session, this data will no longer be available. Don't rely on it for application
242
+ re-initialization from a bookmark.
243
+ * historyData.newTitle (optional):
244
+ This will swap out the html <title> attribute with a new value. If you have set a baseTitle using the
245
+ options bundle, the value will be plugged into the baseTitle by swapping out the @@@ replacement param.
246
+ */
247
+ add: function(newLocation, historyData) {
248
+
249
+ var that = this;
250
+
251
+ /*Escape the location and remove any leading hash symbols*/
252
+ var encodedLocation = encodeURI(this.removeHash(newLocation));
253
+
254
+ if (this.isSafari) {
255
+
256
+ /*Store the history data into history storage - pass in unencoded newLocation since
257
+ historyStorage does its own encoding*/
258
+ historyStorage.put(newLocation, historyData);
259
+
260
+ /*Save this as our current location*/
261
+ this.currentLocation = encodedLocation;
262
+
263
+ /*Change the browser location*/
264
+ window.location.hash = encodedLocation;
265
+
266
+ /*Save this to the Safari form field*/
267
+ this.putSafariState(encodedLocation);
268
+
269
+ this.changeTitle(historyData);
270
+
271
+ } else {
272
+
273
+ /*Most browsers require that we wait a certain amount of time before changing the location, such
274
+ as 200 MS; rather than forcing external callers to use window.setTimeout to account for this,
275
+ we internally handle it by putting requests in a queue.*/
276
+ var addImpl = function() {
277
+
278
+ /*Indicate that the current wait time is now less*/
279
+ if (that.currentWaitTime > 0) {
280
+ that.currentWaitTime = that.currentWaitTime - that.waitTime;
281
+ }
282
+
283
+ /*IE has a strange bug; if the encodedLocation is the same as _any_ preexisting id in the
284
+ document, then the history action gets recorded twice; throw a programmer exception if
285
+ there is an element with this ID*/
286
+ if (document.getElementById(encodedLocation) && that.debugMode) {
287
+ var e = "Exception: History locations can not have the same value as _any_ IDs that might be in the document,"
288
+ + " due to a bug in IE; please ask the developer to choose a history location that does not match any HTML"
289
+ + " IDs in this document. The following ID is already taken and cannot be a location: " + newLocation;
290
+ throw new Error(e);
291
+ }
292
+
293
+ /*Store the history data into history storage - pass in unencoded newLocation since
294
+ historyStorage does its own encoding*/
295
+ historyStorage.put(newLocation, historyData);
296
+
297
+ /*Indicate to the browser to ignore this upcomming location change since we're making it programmatically*/
298
+ that.ignoreLocationChange = true;
299
+
300
+ /*Indicate to IE that this is an atomic location change block*/
301
+ that.ieAtomicLocationChange = true;
302
+
303
+ /*Save this as our current location*/
304
+ that.currentLocation = encodedLocation;
305
+
306
+ /*Change the browser location*/
307
+ window.location.hash = encodedLocation;
308
+
309
+ /*Change the hidden iframe's location if on IE*/
310
+ if (that.isIE) {
311
+ that.iframe.src = that.blankURL + encodedLocation;
312
+ }
313
+
314
+ /*End of atomic location change block for IE*/
315
+ that.ieAtomicLocationChange = false;
316
+
317
+ that.changeTitle(historyData);
318
+
319
+ };
320
+
321
+ /*Now queue up this add request*/
322
+ window.setTimeout(addImpl, this.currentWaitTime);
323
+
324
+ /*Indicate that the next request will have to wait for awhile*/
325
+ this.currentWaitTime = this.currentWaitTime + this.waitTime;
326
+ }
327
+ },
328
+
329
+ /*Public*/
330
+ isFirstLoad: function() {
331
+ return this.firstLoad;
332
+ },
333
+
334
+ /*Public*/
335
+ getVersion: function() {
336
+ return this.VERSIONNUMBER;
337
+ },
338
+
339
+ /*- - - - - - - - - - - -*/
340
+
341
+ /*Private: Constant for our own internal history event called when the page is loaded*/
342
+ PAGELOADEDSTRING: "DhtmlHistory_pageLoaded",
343
+
344
+ VERSIONNUMBER: "0.8",
345
+
346
+ /*
347
+ Private: Pattern for title changes. Example: "Armchair DJ [@@@]" where @@@ will be relaced by values passed to add();
348
+ Default is just the title itself, hence "@@@"
349
+ */
350
+ baseTitle: "@@@",
351
+
352
+ /*Private: Placeholder variable for the original document title; will be set in ititialize()*/
353
+ originalTitle: null,
354
+
355
+ /*Private: URL for the blank html file we use for IE; can be overridden via the options bundle. Otherwise it must be served
356
+ in same directory as this library*/
357
+ blankURL: "/blank.html?",
358
+
359
+ /*Private: Our history change listener.*/
360
+ listener: null,
361
+
362
+ /*Private: MS to wait between add requests - will be reset for certain browsers*/
363
+ waitTime: 200,
364
+
365
+ /*Private: MS before an add request can execute*/
366
+ currentWaitTime: 0,
367
+
368
+ /*Private: Our current hash location, without the "#" symbol.*/
369
+ currentLocation: null,
370
+
371
+ /*Private: Hidden iframe used to IE to detect history changes*/
372
+ iframe: null,
373
+
374
+ /*Private: Flags and DOM references used only by Safari*/
375
+ safariHistoryStartPoint: null,
376
+ safariStack: null,
377
+ safariLength: null,
378
+
379
+ /*Private: Flag used to keep checkLocation() from doing anything when it discovers location changes we've made ourselves
380
+ programmatically with the add() method. Basically, add() sets this to true. When checkLocation() discovers it's true,
381
+ it refrains from firing our listener, then resets the flag to false for next cycle. That way, our listener only gets fired on
382
+ history change events triggered by the user via back/forward buttons and manual hash changes. This flag also helps us set up
383
+ IE's special iframe-based method of handling history changes.*/
384
+ ignoreLocationChange: null,
385
+
386
+ /*Private: A flag that indicates that we should fire a history change event when we are ready, i.e. after we are initialized and
387
+ we have a history change listener. This is needed due to an edge case in browsers other than IE; if you leave a page entirely
388
+ then return, we must fire this as a history change event. Unfortunately, we have lost all references to listeners from earlier,
389
+ because JavaScript clears out.*/
390
+ fireOnNewListener: null,
391
+
392
+ /*Private: A variable that indicates whether this is the first time this page has been loaded. If you go to a web page, leave it
393
+ for another one, and then return, the page's onload listener fires again. We need a way to differentiate between the first page
394
+ load and subsequent ones. This variable works hand in hand with the pageLoaded variable we store into historyStorage.*/
395
+ firstLoad: null,
396
+
397
+ /*Private: A variable to handle an important edge case in IE. In IE, if a user manually types an address into their browser's
398
+ location bar, we must intercept this by calling checkLocation() at regular intervals. However, if we are programmatically
399
+ changing the location bar ourselves using the add() method, we need to ignore these changes in checkLocation(). Unfortunately,
400
+ these changes take several lines of code to complete, so for the duration of those lines of code, we set this variable to true.
401
+ That signals to checkLocation() to ignore the change-in-progress. Once we're done with our chunk of location-change code in
402
+ add(), we set this back to false. We'll do the same thing when capturing user-entered address changes in checkLocation itself.*/
403
+ ieAtomicLocationChange: null,
404
+
405
+ /*Private: Generic utility function for attaching events*/
406
+ addEventListener: function(o,e,l) {
407
+ if (o.addEventListener) {
408
+ o.addEventListener(e,l,false);
409
+ } else if (o.attachEvent) {
410
+ o.attachEvent('on'+e,function() {
411
+ l(window.event);
412
+ });
413
+ }
414
+ },
415
+
416
+
417
+ /*Private: Create IE-specific DOM nodes and overrides*/
418
+ createIE: function(initialHash) {
419
+ /*write out a hidden iframe for IE and set the amount of time to wait between add() requests*/
420
+ this.waitTime = 400;/*IE needs longer between history updates*/
421
+ var styles = (historyStorage.debugMode
422
+ ? 'width: 800px;height:80px;border:1px solid black;'
423
+ : historyStorage.hideStyles
424
+ );
425
+ var iframeID = "rshHistoryFrame";
426
+ var iframeHTML = '<iframe frameborder="0" id="' + iframeID + '" style="' + styles + '" src="' + this.blankURL + initialHash + '"></iframe>';
427
+ document.write(iframeHTML);
428
+ this.iframe = document.getElementById(iframeID);
429
+ },
430
+
431
+ /*Private: Create Opera-specific DOM nodes and overrides*/
432
+ createOpera: function() {
433
+ this.waitTime = 400;/*Opera needs longer between history updates*/
434
+ var imgHTML = '<img src="javascript:location.href=\'javascript:dhtmlHistory.checkLocation();\';" style="' + historyStorage.hideStyles + '" />';
435
+ document.write(imgHTML);
436
+ },
437
+
438
+ /*Private: Create Safari-specific DOM nodes and overrides*/
439
+ createSafari: function() {
440
+ var formID = "rshSafariForm";
441
+ var stackID = "rshSafariStack";
442
+ var lengthID = "rshSafariLength";
443
+ var formStyles = historyStorage.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
444
+ var stackStyles = (historyStorage.debugMode
445
+ ? 'width: 800px;height:80px;border:1px solid black;'
446
+ : historyStorage.hideStyles
447
+ );
448
+ var lengthStyles = (historyStorage.debugMode
449
+ ? 'width:800px;height:20px;border:1px solid black;margin:0;padding:0;'
450
+ : historyStorage.hideStyles
451
+ );
452
+ var safariHTML = '<form id="' + formID + '" style="' + formStyles + '">'
453
+ + '<textarea style="' + stackStyles + '" id="' + stackID + '">[]</textarea>'
454
+ + '<input type="text" style="' + lengthStyles + '" id="' + lengthID + '" value=""/>'
455
+ + '</form>';
456
+ document.write(safariHTML);
457
+ this.safariStack = document.getElementById(stackID);
458
+ this.safariLength = document.getElementById(lengthID);
459
+ if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
460
+ this.safariHistoryStartPoint = history.length;
461
+ this.safariLength.value = this.safariHistoryStartPoint;
462
+ } else {
463
+ this.safariHistoryStartPoint = this.safariLength.value;
464
+ }
465
+ },
466
+
467
+ /*TODO: make this public again?*/
468
+ /*Private: Get browser's current hash location; for Safari, read value from a hidden form field*/
469
+ getCurrentLocation: function() {
470
+ var r = (this.isSafari
471
+ ? this.getSafariState()
472
+ : this.getCurrentHash()
473
+ );
474
+ return r;
475
+ },
476
+
477
+ /*TODO: make this public again?*/
478
+ /*Private: Manually parse the current url for a hash; tip of the hat to YUI*/
479
+ getCurrentHash: function() {
480
+ var r = window.location.href;
481
+ var i = r.indexOf("#");
482
+ return (i >= 0
483
+ ? r.substr(i+1)
484
+ : ""
485
+ );
486
+ },
487
+
488
+ /*Private: Safari method to read the history stack from a hidden form field*/
489
+ getSafariStack: function() {
490
+ var r = this.safariStack.value;
491
+ return historyStorage.fromJSON(r);
492
+ },
493
+ /*Private: Safari method to read from the history stack*/
494
+ getSafariState: function() {
495
+ var stack = this.getSafariStack();
496
+ var state = stack[history.length - this.safariHistoryStartPoint - 1];
497
+ return state;
498
+ },
499
+ /*Private: Safari method to write the history stack to a hidden form field*/
500
+ putSafariState: function(newLocation) {
501
+ var stack = this.getSafariStack();
502
+ stack[history.length - this.safariHistoryStartPoint] = newLocation;
503
+ this.safariStack.value = historyStorage.toJSON(stack);
504
+ },
505
+
506
+ /*Private: Notify the listener of new history changes.*/
507
+ fireHistoryEvent: function(newHash) {
508
+ var decodedHash = decodeURI(newHash)
509
+ /*extract the value from our history storage for this hash*/
510
+ var historyData = historyStorage.get(decodedHash);
511
+ this.changeTitle(historyData);
512
+ /*call our listener*/
513
+ this.listener.call(null, decodedHash, historyData);
514
+ },
515
+
516
+ /*Private: See if the browser has changed location. This is the primary history mechanism for Firefox. For IE, we use this to
517
+ handle an important edge case: if a user manually types in a new hash value into their IE location bar and press enter, we want to
518
+ to intercept this and notify any history listener.*/
519
+ checkLocation: function() {
520
+
521
+ /*Ignore any location changes that we made ourselves for browsers other than IE*/
522
+ if (!this.isIE && this.ignoreLocationChange) {
523
+ this.ignoreLocationChange = false;
524
+ return;
525
+ }
526
+
527
+ /*If we are dealing with IE and we are in the middle of making a location change from an iframe, ignore it*/
528
+ if (!this.isIE && this.ieAtomicLocationChange) {
529
+ return;
530
+ }
531
+
532
+ /*Get hash location*/
533
+ var hash = this.getCurrentLocation();
534
+
535
+ /*Do nothing if there's been no change*/
536
+ if (hash == this.currentLocation) {
537
+ return;
538
+ }
539
+
540
+ /*In IE, users manually entering locations into the browser; we do this by comparing the browser's location against the
541
+ iframe's location; if they differ, we are dealing with a manual event and need to place it inside our history, otherwise
542
+ we can return*/
543
+ this.ieAtomicLocationChange = true;
544
+
545
+ if (this.isIE && this.getIframeHash() != hash) {
546
+ this.iframe.src = this.blankURL + hash;
547
+ }
548
+ else if (this.isIE) {
549
+ /*the iframe is unchanged*/
550
+ return;
551
+ }
552
+
553
+ /*Save this new location*/
554
+ this.currentLocation = hash;
555
+
556
+ this.ieAtomicLocationChange = false;
557
+
558
+ /*Notify listeners of the change*/
559
+ this.fireHistoryEvent(hash);
560
+ },
561
+
562
+ /*Private: Get the current location of IE's hidden iframe.*/
563
+ getIframeHash: function() {
564
+ var doc = this.iframe.contentWindow.document;
565
+ var hash = String(doc.location.search);
566
+ if (hash.length == 1 && hash.charAt(0) == "?") {
567
+ hash = "";
568
+ }
569
+ else if (hash.length >= 2 && hash.charAt(0) == "?") {
570
+ hash = hash.substring(1);
571
+ }
572
+ return hash;
573
+ },
574
+
575
+ /*Private: Remove any leading hash that might be on a location.*/
576
+ removeHash: function(hashValue) {
577
+ var r;
578
+ if (hashValue === null || hashValue === undefined) {
579
+ r = null;
580
+ }
581
+ else if (hashValue === "") {
582
+ r = "";
583
+ }
584
+ else if (hashValue.length == 1 && hashValue.charAt(0) == "#") {
585
+ r = "";
586
+ }
587
+ else if (hashValue.length > 1 && hashValue.charAt(0) == "#") {
588
+ r = hashValue.substring(1);
589
+ }
590
+ else {
591
+ r = hashValue;
592
+ }
593
+ return r;
594
+ },
595
+
596
+ /*Private: For IE, tell when the hidden iframe has finished loading.*/
597
+ iframeLoaded: function(newLocation) {
598
+ /*ignore any location changes that we made ourselves*/
599
+ if (this.ignoreLocationChange) {
600
+ this.ignoreLocationChange = false;
601
+ return;
602
+ }
603
+
604
+ /*Get the new location*/
605
+ var hash = String(newLocation.search);
606
+ if (hash.length == 1 && hash.charAt(0) == "?") {
607
+ hash = "";
608
+ }
609
+ else if (hash.length >= 2 && hash.charAt(0) == "?") {
610
+ hash = hash.substring(1);
611
+ }
612
+ /*Keep the browser location bar in sync with the iframe hash*/
613
+ window.location.hash = hash;
614
+
615
+ /*Notify listeners of the change*/
616
+ this.fireHistoryEvent(hash);
617
+ }
618
+
619
+
620
+ };
621
+
622
+ /*
623
+ historyStorage: An object that uses a hidden form to store history state across page loads. The mechanism for doing so relies on
624
+ the fact that browsers save the text in form data for the life of the browser session, which means the text is still there when
625
+ the user navigates back to the page. This object can be used independently of the dhtmlHistory object for caching of Ajax
626
+ session information.
627
+
628
+ dependencies:
629
+ * json2007.js (included in a separate file) or alternate JSON methods passed in through an options bundle.
630
+ */
631
+ window.historyStorage = {
632
+
633
+ /*Public: Set up our historyStorage object for use by dhtmlHistory or other objects*/
634
+ setup: function(options) {
635
+
636
+ /*
637
+ options - object to store initialization parameters - passed in from dhtmlHistory or directly into historyStorage
638
+ options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
639
+ options.toJSON - function to override default JSON stringifier
640
+ options.fromJSON - function to override default JSON parser
641
+ */
642
+
643
+ /*process init parameters*/
644
+ if (typeof options !== "undefined") {
645
+ if (options.debugMode) {
646
+ this.debugMode = options.debugMode;
647
+ }
648
+ if (options.toJSON) {
649
+ this.toJSON = options.toJSON;
650
+ }
651
+ if (options.fromJSON) {
652
+ this.fromJSON = options.fromJSON;
653
+ }
654
+ }
655
+
656
+ /*write a hidden form and textarea into the page; we'll stow our history stack here*/
657
+ var formID = "rshStorageForm";
658
+ var textareaID = "rshStorageField";
659
+ var formStyles = this.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
660
+ var textareaStyles = (historyStorage.debugMode
661
+ ? 'width: 800px;height:80px;border:1px solid black;'
662
+ : historyStorage.hideStyles
663
+ );
664
+ var textareaHTML = '<form id="' + formID + '" style="' + formStyles + '">'
665
+ + '<textarea id="' + textareaID + '" style="' + textareaStyles + '"></textarea>'
666
+ + '</form>';
667
+ document.write(textareaHTML);
668
+ this.storageField = document.getElementById(textareaID);
669
+ if (typeof window.opera !== "undefined") {
670
+ this.storageField.focus();/*Opera needs to focus this element before persisting values in it*/
671
+ }
672
+ },
673
+
674
+ /*Public*/
675
+ put: function(key, value) {
676
+
677
+ var encodedKey = encodeURI(key);
678
+
679
+ this.assertValidKey(encodedKey);
680
+ /*if we already have a value for this, remove the value before adding the new one*/
681
+ if (this.hasKey(key)) {
682
+ this.remove(key);
683
+ }
684
+ /*store this new key*/
685
+ this.storageHash[encodedKey] = value;
686
+ /*save and serialize the hashtable into the form*/
687
+ this.saveHashTable();
688
+ },
689
+
690
+ /*Public*/
691
+ get: function(key) {
692
+
693
+ var encodedKey = encodeURI(key);
694
+
695
+ this.assertValidKey(encodedKey);
696
+ /*make sure the hash table has been loaded from the form*/
697
+ this.loadHashTable();
698
+ var value = this.storageHash[encodedKey];
699
+ if (value === undefined) {
700
+ value = null;
701
+ }
702
+ return value;
703
+ },
704
+
705
+ /*Public*/
706
+ remove: function(key) {
707
+
708
+ var encodedKey = encodeURI(key);
709
+
710
+ this.assertValidKey(encodedKey);
711
+ /*make sure the hash table has been loaded from the form*/
712
+ this.loadHashTable();
713
+ /*delete the value*/
714
+ delete this.storageHash[encodedKey];
715
+ /*serialize and save the hash table into the form*/
716
+ this.saveHashTable();
717
+ },
718
+
719
+ /*Public: Clears out all saved data.*/
720
+ reset: function() {
721
+ this.storageField.value = "";
722
+ this.storageHash = {};
723
+ },
724
+
725
+ /*Public*/
726
+ hasKey: function(key) {
727
+
728
+ var encodedKey = encodeURI(key);
729
+
730
+ this.assertValidKey(encodedKey);
731
+ /*make sure the hash table has been loaded from the form*/
732
+ this.loadHashTable();
733
+ return (typeof this.storageHash[encodedKey] !== "undefined");
734
+ },
735
+
736
+ /*Public*/
737
+ isValidKey: function(key) {
738
+ return (typeof key === "string");
739
+ //TODO - should we ban hash signs and other special characters?
740
+ },
741
+
742
+ /*- - - - - - - - - - - -*/
743
+
744
+ /*Private - CSS strings utilized by both objects to hide or show behind-the-scenes DOM elements*/
745
+ showStyles: 'border:0;margin:0;padding:0;',
746
+ hideStyles: 'left:-1000px;top:-1000px;width:1px;height:1px;border:0;position:absolute;',
747
+
748
+ /*Private - debug mode flag*/
749
+ debugMode: false,
750
+
751
+ /*Private: Our hash of key name/values.*/
752
+ storageHash: {},
753
+
754
+ /*Private: If true, we have loaded our hash table out of the storage form.*/
755
+ hashLoaded: false,
756
+
757
+ /*Private: DOM reference to our history field*/
758
+ storageField: null,
759
+
760
+ /*Private: Assert that a key is valid; throw an exception if it not.*/
761
+ assertValidKey: function(key) {
762
+ var isValid = this.isValidKey(key);
763
+ if (!isValid && this.debugMode) {
764
+ throw new Error("Please provide a valid key for window.historyStorage. Invalid key = " + key + ".");
765
+ }
766
+ },
767
+
768
+ /*Private: Load the hash table up from the form.*/
769
+ loadHashTable: function() {
770
+ if (!this.hashLoaded) {
771
+ var serializedHashTable = this.storageField.value;
772
+ if (serializedHashTable !== "" && serializedHashTable !== null) {
773
+ this.storageHash = this.fromJSON(serializedHashTable);
774
+ this.hashLoaded = true;
775
+ }
776
+ }
777
+ },
778
+ /*Private: Save the hash table into the form.*/
779
+ saveHashTable: function() {
780
+ this.loadHashTable();
781
+ var serializedHashTable = this.toJSON(this.storageHash);
782
+ this.storageField.value = serializedHashTable;
783
+ },
784
+ /*Private: Bridges for our JSON implementations - both rely on 2007 JSON.org library - can be overridden by options bundle*/
785
+ toJSON: function(o) {
786
+ return o.toJSONString();
787
+ },
788
+ fromJSON: function(s) {
789
+ return s.parseJSON();
790
+ }
791
+ };
792
+
793
+
794
+ /*******************************************************************/
795
+ /** QueryString Object from http://adamv.com/dev/javascript/querystring */
796
+ /* Client-side access to querystring name=value pairs
797
+ Version 1.3
798
+ 28 May 2008
799
+
800
+ License (Simplified BSD):
801
+ http://adamv.com/dev/javascript/qslicense.txt
802
+ */
803
+ function Querystring(qs) { // optionally pass a querystring to parse
804
+ this.params = {};
805
+
806
+ if (qs == null) qs = location.search.substring(1, location.search.length);
807
+ if (qs.length == 0) return;
808
+
809
+ // Turn <plus> back to <space>
810
+ // See: http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4.1
811
+ qs = qs.replace(/\+/g, ' ');
812
+ var args = qs.split('&'); // parse out name/value pairs separated via &
813
+
814
+ // split out each name=value pair
815
+ for (var i = 0; i < args.length; i++) {
816
+ var pair = args[i].split('=');
817
+ var name = decodeURI(pair[0]);
818
+
819
+ var value = (pair.length==2)
820
+ ? decodeURI(pair[1])
821
+ : name;
822
+
823
+ this.params[name] = value;
824
+ }
825
+ }
826
+
827
+ Querystring.prototype.get = function(key, default_) {
828
+ var value = this.params[key];
829
+ return (value != null) ? value : default_;
830
+ }
831
+
832
+ Querystring.prototype.contains = function(key) {
833
+ var value = this.params[key];
834
+ return (value != null);
835
+ }
836
+
837
+ /*******************************************************************/
838
+ /* Added by Ed Wildgoose - MailASail */
839
+ /* Initialise the library and add our history callback */
840
+ /*******************************************************************/
841
+ window.dhtmlHistory.create({
842
+ toJSON: function(o) {
843
+ return Object.toJSON(o);
844
+ }
845
+ , fromJSON: function(s) {
846
+ return s.evalJSON();
847
+ }
848
+
849
+ // Enable this to assist with debugging
850
+ // , debugMode: true
851
+
852
+ // dhtmlHistory has been modified not to need the next line
853
+ // But left in for robustness when updating dhtmlHistory
854
+ , blankURL: '/blank.html?'
855
+ });
856
+
857
+ /** Our callback to receive history
858
+ change events. */
859
+ var handleHistoryChange = function(pageId, pageData) {
860
+ if (!pageData) return;
861
+ var info = pageId.split(':');
862
+ var id = info[0];
863
+ pageData += '&_method=get';
864
+ new Ajax.Request(pageData, {asynchronous:true, evalScripts:true, method: 'get', onLoading:function(request){Element.show(id+'-pagination-loading-indicator');}});
865
+ }
866
+
867
+ window.onload = function() {
868
+ dhtmlHistory.initialize(handleHistoryChange);
869
+ };
870
+