fae-railsz 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -0
  3. data/Rakefile +37 -0
  4. data/app/assets/fonts/fae/icons.eot +0 -0
  5. data/app/assets/fonts/fae/icons.svg +56 -0
  6. data/app/assets/fonts/fae/icons.ttf +0 -0
  7. data/app/assets/fonts/fae/icons.woff +0 -0
  8. data/app/assets/fonts/fae/lato-bold-webfont.eot +0 -0
  9. data/app/assets/fonts/fae/lato-bold-webfont.svg +4551 -0
  10. data/app/assets/fonts/fae/lato-bold-webfont.ttf +0 -0
  11. data/app/assets/fonts/fae/lato-bold-webfont.woff +0 -0
  12. data/app/assets/fonts/fae/lato-light-webfont.eot +0 -0
  13. data/app/assets/fonts/fae/lato-light-webfont.svg +4241 -0
  14. data/app/assets/fonts/fae/lato-light-webfont.ttf +0 -0
  15. data/app/assets/fonts/fae/lato-light-webfont.woff +0 -0
  16. data/app/assets/fonts/fae/lato-regular-webfont.eot +0 -0
  17. data/app/assets/fonts/fae/lato-regular-webfont.svg +4241 -0
  18. data/app/assets/fonts/fae/lato-regular-webfont.ttf +0 -0
  19. data/app/assets/fonts/fae/lato-regular-webfont.woff +0 -0
  20. data/app/assets/fonts/fae/selection.json +1334 -0
  21. data/app/assets/images/fae/error_bg.jpg +0 -0
  22. data/app/assets/images/fae/error_sad_face.png +0 -0
  23. data/app/assets/images/fae/icons/alert.svg +9 -0
  24. data/app/assets/images/fae/icons/arrow_down.svg +8 -0
  25. data/app/assets/images/fae/icons/arrow_left.svg +9 -0
  26. data/app/assets/images/fae/icons/arrow_right.svg +9 -0
  27. data/app/assets/images/fae/icons/arrow_stacked.svg +8 -0
  28. data/app/assets/images/fae/icons/arrow_up.svg +8 -0
  29. data/app/assets/images/fae/icons/calendar.svg +17 -0
  30. data/app/assets/images/fae/icons/check.svg +7 -0
  31. data/app/assets/images/fae/icons/checkmark.svg +8 -0
  32. data/app/assets/images/fae/icons/chevron_down.svg +7 -0
  33. data/app/assets/images/fae/icons/chevron_left.svg +10 -0
  34. data/app/assets/images/fae/icons/chevron_right.svg +10 -0
  35. data/app/assets/images/fae/icons/chevron_up.svg +7 -0
  36. data/app/assets/images/fae/icons/delete.svg +8 -0
  37. data/app/assets/images/fae/icons/delete_x.svg +8 -0
  38. data/app/assets/images/fae/icons/edit.svg +11 -0
  39. data/app/assets/images/fae/icons/handle.svg +13 -0
  40. data/app/assets/images/fae/icons/home.svg +10 -0
  41. data/app/assets/images/fae/icons/logout.svg +12 -0
  42. data/app/assets/images/fae/icons/magnifying_glass.svg +10 -0
  43. data/app/assets/images/fae/icons/menu.svg +11 -0
  44. data/app/assets/images/fae/icons/offlink.svg +25 -0
  45. data/app/assets/images/fae/icons/plus.svg +8 -0
  46. data/app/assets/images/fae/icons/pulldown.svg +11 -0
  47. data/app/assets/images/fae/icons/search.svg +11 -0
  48. data/app/assets/images/fae/icons/settings.svg +12 -0
  49. data/app/assets/images/fae/icons/sort.svg +11 -0
  50. data/app/assets/images/fae/icons/support.svg +15 -0
  51. data/app/assets/images/fae/icons/trash.svg +10 -0
  52. data/app/assets/images/fae/icons/trumbowyg.svg +1 -0
  53. data/app/assets/images/fae/icons/user.svg +13 -0
  54. data/app/assets/images/fae/icons/users.svg +19 -0
  55. data/app/assets/images/fae/icons/zoom.svg +12 -0
  56. data/app/assets/images/fae/tutorial_model_location.png +0 -0
  57. data/app/assets/images/fae/youtube_helper.jpg +0 -0
  58. data/app/assets/javascripts/fae/_modals.js +60 -0
  59. data/app/assets/javascripts/fae/_tables.js +403 -0
  60. data/app/assets/javascripts/fae/admin.js.erb +11 -0
  61. data/app/assets/javascripts/fae/application.js +63 -0
  62. data/app/assets/javascripts/fae/fae_init.js +8 -0
  63. data/app/assets/javascripts/fae/form/_ajax.js +380 -0
  64. data/app/assets/javascripts/fae/form/_cancel.js +38 -0
  65. data/app/assets/javascripts/fae/form/_filtering.js +276 -0
  66. data/app/assets/javascripts/fae/form/_form.js +60 -0
  67. data/app/assets/javascripts/fae/form/_slugger.js.erb +100 -0
  68. data/app/assets/javascripts/fae/form/_validator.js +459 -0
  69. data/app/assets/javascripts/fae/form/fae_chosen.js +44 -0
  70. data/app/assets/javascripts/fae/form/fileinputer.js +178 -0
  71. data/app/assets/javascripts/fae/form/hinter.js +111 -0
  72. data/app/assets/javascripts/fae/form/inputs/_checkbox.js +27 -0
  73. data/app/assets/javascripts/fae/form/inputs/_color.js +42 -0
  74. data/app/assets/javascripts/fae/form/inputs/_dates.js +78 -0
  75. data/app/assets/javascripts/fae/form/inputs/_select.js +162 -0
  76. data/app/assets/javascripts/fae/form/inputs/_text.js +118 -0
  77. data/app/assets/javascripts/fae/navigation/_global_search.js +184 -0
  78. data/app/assets/javascripts/fae/navigation/_language.js +89 -0
  79. data/app/assets/javascripts/fae/navigation/_navigation.js +206 -0
  80. data/app/assets/javascripts/fae/navigation/_subnav_highlighter.js +81 -0
  81. data/app/assets/javascripts/fae/navigation/sticky.js +186 -0
  82. data/app/assets/javascripts/fae/vendor/chosen.jquery.min.js +2 -0
  83. data/app/assets/javascripts/fae/vendor/frob_core_helpers.js +587 -0
  84. data/app/assets/javascripts/fae/vendor/fryr.js +437 -0
  85. data/app/assets/javascripts/fae/vendor/jqColorPicker.min.js +4 -0
  86. data/app/assets/javascripts/fae/vendor/jquery.daterangepicker.js +1292 -0
  87. data/app/assets/javascripts/fae/vendor/jquery.multi-select.js +534 -0
  88. data/app/assets/javascripts/fae/vendor/jquery.simplemodal.1.4.4.js +719 -0
  89. data/app/assets/javascripts/fae/vendor/jquery.tablesorter.js +1901 -0
  90. data/app/assets/javascripts/fae/vendor/js.cookie.js +139 -0
  91. data/app/assets/javascripts/fae/vendor/moment.min.js +7 -0
  92. data/app/assets/javascripts/fae/vendor/simplemde.min.js +13 -0
  93. data/app/assets/javascripts/fae/vendor/touch_punch.js +11 -0
  94. data/app/assets/javascripts/fae/vendor/trumbowyg.js.erb +4 -0
  95. data/app/assets/javascripts/fae/vendor/trumbowyg/plugins/upload/trumbowyg.upload.js +223 -0
  96. data/app/assets/javascripts/fae/vendor/trumbowyg/trumbowyg.js +1603 -0
  97. data/app/assets/stylesheets/fae/application.css +8 -0
  98. data/app/assets/stylesheets/fae/base.scss +82 -0
  99. data/app/assets/stylesheets/fae/globals/_fonts.scss +43 -0
  100. data/app/assets/stylesheets/fae/globals/_icons.scss +15 -0
  101. data/app/assets/stylesheets/fae/globals/_plugins.scss +51 -0
  102. data/app/assets/stylesheets/fae/globals/_tags.scss +68 -0
  103. data/app/assets/stylesheets/fae/globals/imports/_extends.scss +53 -0
  104. data/app/assets/stylesheets/fae/globals/imports/_mixins.scss +58 -0
  105. data/app/assets/stylesheets/fae/globals/imports/_variables.scss +165 -0
  106. data/app/assets/stylesheets/fae/globals/imports/finescss/_fine.scss +7 -0
  107. data/app/assets/stylesheets/fae/globals/imports/finescss/_fine_functions.scss +88 -0
  108. data/app/assets/stylesheets/fae/globals/imports/finescss/_fine_mixins.scss +280 -0
  109. data/app/assets/stylesheets/fae/globals/imports/finescss/_fine_variables.scss +30 -0
  110. data/app/assets/stylesheets/fae/globals/layout/_base.scss +48 -0
  111. data/app/assets/stylesheets/fae/globals/layout/_breadcrumbs.scss +45 -0
  112. data/app/assets/stylesheets/fae/globals/layout/_content-header.scss +99 -0
  113. data/app/assets/stylesheets/fae/globals/legacy/_pre-1.3.scss +659 -0
  114. data/app/assets/stylesheets/fae/globals/navigation/_base.scss +22 -0
  115. data/app/assets/stylesheets/fae/globals/navigation/_footer.scss +50 -0
  116. data/app/assets/stylesheets/fae/globals/navigation/_header.scss +149 -0
  117. data/app/assets/stylesheets/fae/globals/navigation/_mobilenav.scss +149 -0
  118. data/app/assets/stylesheets/fae/globals/navigation/_search.scss +76 -0
  119. data/app/assets/stylesheets/fae/globals/navigation/_sidenav.scss +142 -0
  120. data/app/assets/stylesheets/fae/globals/navigation/_utility.scss +95 -0
  121. data/app/assets/stylesheets/fae/modules/_alerts.scss +50 -0
  122. data/app/assets/stylesheets/fae/modules/_buttons.scss +62 -0
  123. data/app/assets/stylesheets/fae/modules/_modal.scss +78 -0
  124. data/app/assets/stylesheets/fae/modules/_toggles.scss +48 -0
  125. data/app/assets/stylesheets/fae/modules/forms/_asset-actions.scss +83 -0
  126. data/app/assets/stylesheets/fae/modules/forms/_base.scss +100 -0
  127. data/app/assets/stylesheets/fae/modules/forms/_checkbox.scss +87 -0
  128. data/app/assets/stylesheets/fae/modules/forms/_colorpicker.scss +57 -0
  129. data/app/assets/stylesheets/fae/modules/forms/_date.scss +335 -0
  130. data/app/assets/stylesheets/fae/modules/forms/_hints.scss +40 -0
  131. data/app/assets/stylesheets/fae/modules/forms/_label.scss +57 -0
  132. data/app/assets/stylesheets/fae/modules/forms/_radio.scss +69 -0
  133. data/app/assets/stylesheets/fae/modules/forms/_select.scss +317 -0
  134. data/app/assets/stylesheets/fae/modules/forms/_simple-mde.scss +29 -0
  135. data/app/assets/stylesheets/fae/modules/forms/_text.scss +62 -0
  136. data/app/assets/stylesheets/fae/modules/forms/_textarea.scss +16 -0
  137. data/app/assets/stylesheets/fae/modules/forms/_validation.scss +63 -0
  138. data/app/assets/stylesheets/fae/modules/tables/_actions.scss +41 -0
  139. data/app/assets/stylesheets/fae/modules/tables/_base.scss +63 -0
  140. data/app/assets/stylesheets/fae/modules/tables/_collapsible.scss +76 -0
  141. data/app/assets/stylesheets/fae/modules/tables/_filters.scss +61 -0
  142. data/app/assets/stylesheets/fae/modules/tables/_image.scss +21 -0
  143. data/app/assets/stylesheets/fae/modules/tables/_pagination.scss +34 -0
  144. data/app/assets/stylesheets/fae/modules/tables/_sorting.scss +58 -0
  145. data/app/assets/stylesheets/fae/modules/tables/_sticky.scss +9 -0
  146. data/app/assets/stylesheets/fae/modules/tables/_tooltips.scss +24 -0
  147. data/app/assets/stylesheets/fae/pages/_error.scss +35 -0
  148. data/app/assets/stylesheets/fae/pages/_help.scss +28 -0
  149. data/app/assets/stylesheets/fae/pages/_home.scss +18 -0
  150. data/app/assets/stylesheets/fae/pages/_login.scss +137 -0
  151. data/app/assets/stylesheets/fae/vendor/chosen.css +377 -0
  152. data/app/assets/stylesheets/fae/vendor/daterangepicker.css +227 -0
  153. data/app/assets/stylesheets/fae/vendor/reset.css +48 -0
  154. data/app/assets/stylesheets/fae/vendor/simplemde.min.css +7 -0
  155. data/app/assets/stylesheets/fae/vendor/trumbowyg.css +584 -0
  156. data/app/controllers/concerns/fae/application_controller_concern.rb +1 -0
  157. data/app/controllers/concerns/fae/asset_cloner.rb +46 -0
  158. data/app/controllers/concerns/fae/cloneable.rb +136 -0
  159. data/app/controllers/fae/application_controller.rb +123 -0
  160. data/app/controllers/fae/base_controller.rb +117 -0
  161. data/app/controllers/fae/files_controller.rb +26 -0
  162. data/app/controllers/fae/images_controller.rb +37 -0
  163. data/app/controllers/fae/nested_base_controller.rb +100 -0
  164. data/app/controllers/fae/options_controller.rb +29 -0
  165. data/app/controllers/fae/pages_controller.rb +44 -0
  166. data/app/controllers/fae/setup_controller.rb +54 -0
  167. data/app/controllers/fae/static_pages_controller.rb +54 -0
  168. data/app/controllers/fae/users_controller.rb +71 -0
  169. data/app/controllers/fae/utilities_controller.rb +61 -0
  170. data/app/helpers/fae/application_helper.rb +114 -0
  171. data/app/helpers/fae/fae_helper.rb +6 -0
  172. data/app/helpers/fae/form_helper.rb +240 -0
  173. data/app/helpers/fae/nested_form_helper.rb +39 -0
  174. data/app/helpers/fae/view_helper.rb +175 -0
  175. data/app/inputs/date_range_input.rb +17 -0
  176. data/app/inputs/radio_collection_input.rb +17 -0
  177. data/app/inputs/symbol_input.rb +5 -0
  178. data/app/models/concerns/fae/assets_validatable.rb +12 -0
  179. data/app/models/concerns/fae/authorization_concern.rb +12 -0
  180. data/app/models/concerns/fae/base_model_concern.rb +105 -0
  181. data/app/models/concerns/fae/change_concern.rb +1 -0
  182. data/app/models/concerns/fae/file_concern.rb +1 -0
  183. data/app/models/concerns/fae/image_concern.rb +1 -0
  184. data/app/models/concerns/fae/navigation_concern.rb +10 -0
  185. data/app/models/concerns/fae/option_concern.rb +1 -0
  186. data/app/models/concerns/fae/page_validatable.rb +26 -0
  187. data/app/models/concerns/fae/paper_trailer.rb +8 -0
  188. data/app/models/concerns/fae/role_concern.rb +1 -0
  189. data/app/models/concerns/fae/sortable.rb +51 -0
  190. data/app/models/concerns/fae/static_page_concern.rb +1 -0
  191. data/app/models/concerns/fae/text_area_concern.rb +1 -0
  192. data/app/models/concerns/fae/text_field_concern.rb +1 -0
  193. data/app/models/concerns/fae/trackable.rb +124 -0
  194. data/app/models/concerns/fae/user_concern.rb +7 -0
  195. data/app/models/fae/authorization.rb +8 -0
  196. data/app/models/fae/change.rb +53 -0
  197. data/app/models/fae/file.rb +24 -0
  198. data/app/models/fae/image.rb +33 -0
  199. data/app/models/fae/navigation.rb +132 -0
  200. data/app/models/fae/option.rb +38 -0
  201. data/app/models/fae/role.rb +18 -0
  202. data/app/models/fae/static_page.rb +109 -0
  203. data/app/models/fae/text_area.rb +14 -0
  204. data/app/models/fae/text_field.rb +14 -0
  205. data/app/models/fae/user.rb +67 -0
  206. data/app/uploaders/fae/file_uploader.rb +26 -0
  207. data/app/uploaders/fae/image_uploader.rb +28 -0
  208. data/app/validators/fae/cross_model_uniqueness_validator.rb +14 -0
  209. data/app/views/devise/confirmations/new.html.slim +13 -0
  210. data/app/views/devise/mailer/confirmation_instructions.html.slim +3 -0
  211. data/app/views/devise/mailer/reset_password_instructions.html.slim +5 -0
  212. data/app/views/devise/mailer/unlock_instructions.html.slim +4 -0
  213. data/app/views/devise/passwords/edit.html.slim +10 -0
  214. data/app/views/devise/passwords/new.html.slim +10 -0
  215. data/app/views/devise/registrations/edit.html.slim +22 -0
  216. data/app/views/devise/registrations/new.html.slim +15 -0
  217. data/app/views/devise/sessions/new.html.slim +7 -0
  218. data/app/views/devise/shared/_links.slim +25 -0
  219. data/app/views/devise/unlocks/new.html.slim +13 -0
  220. data/app/views/fae/application/_breadcrumb.html.slim +19 -0
  221. data/app/views/fae/application/_content_form.html.slim +55 -0
  222. data/app/views/fae/application/_credit.slim +6 -0
  223. data/app/views/fae/application/_file_uploader.html.slim +36 -0
  224. data/app/views/fae/application/_flash_messages.slim +2 -0
  225. data/app/views/fae/application/_global_search_results.html.slim +37 -0
  226. data/app/views/fae/application/_header.slim +50 -0
  227. data/app/views/fae/application/_markdown_helper.slim +41 -0
  228. data/app/views/fae/application/_mobilenav.slim +66 -0
  229. data/app/views/fae/application/_sidenav.slim +23 -0
  230. data/app/views/fae/application/_user_log.html.slim +36 -0
  231. data/app/views/fae/images/_image_uploader.html.slim +66 -0
  232. data/app/views/fae/options/_form.html.slim +16 -0
  233. data/app/views/fae/options/edit.html.slim +1 -0
  234. data/app/views/fae/pages/activity_log.html.slim +32 -0
  235. data/app/views/fae/pages/disabled_environment.html.slim +4 -0
  236. data/app/views/fae/pages/error404.html.slim +7 -0
  237. data/app/views/fae/pages/help.html.slim +35 -0
  238. data/app/views/fae/pages/home.html.slim +36 -0
  239. data/app/views/fae/setup/first_user.html.slim +11 -0
  240. data/app/views/fae/shared/_errors.slim +4 -0
  241. data/app/views/fae/shared/_form_header.html.slim +40 -0
  242. data/app/views/fae/shared/_index_header.html.slim +26 -0
  243. data/app/views/fae/shared/_nested_table.html.slim +49 -0
  244. data/app/views/fae/shared/_recent_changes.html.slim +22 -0
  245. data/app/views/fae/shared/_shared_nested_table.html.slim +46 -0
  246. data/app/views/fae/static_pages/index.html.slim +13 -0
  247. data/app/views/fae/users/_form.html.slim +19 -0
  248. data/app/views/fae/users/edit.html.slim +1 -0
  249. data/app/views/fae/users/index.html.slim +23 -0
  250. data/app/views/fae/users/new.html.slim +1 -0
  251. data/app/views/fae/users/settings.html.slim +1 -0
  252. data/app/views/kaminari/fae/_gap.html.slim +8 -0
  253. data/app/views/kaminari/fae/_next_page.html.slim +9 -0
  254. data/app/views/kaminari/fae/_page.html.slim +15 -0
  255. data/app/views/kaminari/fae/_paginator.html.slim +15 -0
  256. data/app/views/kaminari/fae/_prev_page.html.slim +9 -0
  257. data/app/views/layouts/devise.html.slim +29 -0
  258. data/app/views/layouts/fae/application.html.slim +23 -0
  259. data/app/views/layouts/fae/error.html.slim +11 -0
  260. data/config/deploy.rb +73 -0
  261. data/config/deploy/dev.rb +19 -0
  262. data/config/deploy/prod.rb +19 -0
  263. data/config/deploy/stage.rb +19 -0
  264. data/config/initializers/assets.rb +1 -0
  265. data/config/initializers/carrierwave.rb +3 -0
  266. data/config/initializers/devise.rb +258 -0
  267. data/config/initializers/devise_monkeypatch.rb +11 -0
  268. data/config/initializers/fae_judge.rb +3 -0
  269. data/config/initializers/inflections.rb +16 -0
  270. data/config/initializers/kaminari_config.rb +10 -0
  271. data/config/initializers/simple_form.rb +147 -0
  272. data/config/initializers/time_formats.rb +3 -0
  273. data/config/locales/devise.en.yml +59 -0
  274. data/config/locales/devise.zh-CN.yml +59 -0
  275. data/config/locales/fae.en.yml +10 -0
  276. data/config/locales/fae.zh-CN.yml +10 -0
  277. data/config/locales/simple_form.en.yml +26 -0
  278. data/config/locales/simple_form.zh-CN.yml +26 -0
  279. data/config/routes.rb +46 -0
  280. data/db/migrate/20140809222030_add_user_table.rb +49 -0
  281. data/db/migrate/20140822224029_create_fae_roles.rb +10 -0
  282. data/db/migrate/20141008180718_create_fae_images_table.rb +19 -0
  283. data/db/migrate/20141017194616_create_fae_options.rb +16 -0
  284. data/db/migrate/20141021181327_create_fae_files.rb +17 -0
  285. data/db/migrate/20141021183047_create_fae_text_areas.rb +16 -0
  286. data/db/migrate/20141021184311_create_fae_pages.rb +13 -0
  287. data/db/migrate/20141105214814_create_fae_text_fields.rb +15 -0
  288. data/db/migrate/20150930224821_create_fae_changes.rb +17 -0
  289. data/lib/fae-rails.rb +6 -0
  290. data/lib/fae/concerns/models/base.rb +8 -0
  291. data/lib/fae/engine.rb +37 -0
  292. data/lib/fae/options.rb +35 -0
  293. data/lib/fae/validation_helper_collection.rb +76 -0
  294. data/lib/fae/version.rb +3 -0
  295. data/lib/file_size_validator.rb +71 -0
  296. data/lib/generators/fae/base_generator.rb +156 -0
  297. data/lib/generators/fae/controller_generator.rb +13 -0
  298. data/lib/generators/fae/install_generator.rb +61 -0
  299. data/lib/generators/fae/model_generator.rb +11 -0
  300. data/lib/generators/fae/nested_index_scaffold_generator.rb +36 -0
  301. data/lib/generators/fae/nested_scaffold_generator.rb +51 -0
  302. data/lib/generators/fae/page_generator.rb +65 -0
  303. data/lib/generators/fae/scaffold_generator.rb +15 -0
  304. data/lib/generators/fae/templates/assets/fae.js +6 -0
  305. data/lib/generators/fae/templates/assets/fae.scss +2 -0
  306. data/lib/generators/fae/templates/controllers/nested_index_scaffold_controller.rb +81 -0
  307. data/lib/generators/fae/templates/controllers/nested_scaffold_controller.rb +13 -0
  308. data/lib/generators/fae/templates/controllers/scaffold_controller.rb +13 -0
  309. data/lib/generators/fae/templates/controllers/static_pages_controller.rb +10 -0
  310. data/lib/generators/fae/templates/initializers/fae.rb +63 -0
  311. data/lib/generators/fae/templates/initializers/fae_fine.rb +51 -0
  312. data/lib/generators/fae/templates/initializers/judge.rb +6 -0
  313. data/lib/generators/fae/templates/models/concerns/authorization_concern.rb +27 -0
  314. data/lib/generators/fae/templates/models/concerns/navigation_concern.rb +33 -0
  315. data/lib/generators/fae/templates/models/pages_model.rb +14 -0
  316. data/lib/generators/fae/templates/tasks/fae_tasks.rake +19 -0
  317. data/lib/generators/fae/templates/views/_form.html.slim +18 -0
  318. data/lib/generators/fae/templates/views/_form_index_nested.html.slim +13 -0
  319. data/lib/generators/fae/templates/views/_form_nested.html.slim +17 -0
  320. data/lib/generators/fae/templates/views/edit.html.slim +1 -0
  321. data/lib/generators/fae/templates/views/edit_nested.html.slim +3 -0
  322. data/lib/generators/fae/templates/views/index.html.slim +35 -0
  323. data/lib/generators/fae/templates/views/index_nested.html.slim +8 -0
  324. data/lib/generators/fae/templates/views/new.html.slim +1 -0
  325. data/lib/generators/fae/templates/views/new_nested.html.slim +3 -0
  326. data/lib/generators/fae/templates/views/static_page_form.html.slim +15 -0
  327. data/lib/generators/fae/templates/views/table_nested.html.slim +4 -0
  328. metadata +692 -0
@@ -0,0 +1,8 @@
1
+ /* global Fae, FCH */
2
+
3
+ // This is a separate file to keep application.js pure
4
+ ;(function() {
5
+ window.FCH = new FrobCoreHelpers(Fae, {
6
+ mobile_fps: false
7
+ });
8
+ })();
@@ -0,0 +1,380 @@
1
+ /* global Fae, fae_chosen, fileinputer, FCH, Cookies */
2
+
3
+ /**
4
+ * Fae AJAX
5
+ * @namespace form.ajax
6
+ * @memberof form
7
+ */
8
+ Fae.form.ajax = {
9
+
10
+ init: function() {
11
+ this.$addedit_form = $('.js-addedit-form, .js-index-addedit-form');
12
+ this.$filter_form = $('.js-filter-form');
13
+
14
+ this.addEditLinks();
15
+ this.addEditSubmission();
16
+
17
+ this.addCancelLinks();
18
+
19
+ this.imageDeleteLinks();
20
+ this.htmlListeners();
21
+
22
+ this.deleteNoForm();
23
+ },
24
+
25
+ /**
26
+ * Click event listener for add and edit links applied to both index and nested forms
27
+ */
28
+ addEditLinks: function() {
29
+ var _this = this;
30
+
31
+ this.$addedit_form.on('click', '.js-add-link, .js-edit-link', function(ev) {
32
+ ev.preventDefault();
33
+ var $this = $(this);
34
+ var $parent = $this.hasClass('js-index-add-link') ? $('.js-addedit-form') : $this.closest('.js-addedit-form');
35
+
36
+ // scroll to the last column of the tbody, where the form will start
37
+ FCH.smoothScroll($parent.find('tbody tr:last-child'), 500, 450, -20);
38
+
39
+ _this._addEditActions($this.attr('href'), $parent.find('.js-addedit-form-wrapper'));
40
+ });
41
+ },
42
+
43
+ /**
44
+ * Once add or edit is clicked, load remote data, add it to the DOM and initialize listeners on the new create form
45
+ * @protected
46
+ * @param {String} remote_url - Remote page to load form from
47
+ * @param {jQuery} $wrapper - Whole form container
48
+ * @see addEditLinks
49
+ */
50
+ _addEditActions: function(remote_url, $wrapper) {
51
+
52
+ $.get(remote_url, function(data){
53
+ // check to see if the content is hidden and slide it down if it is.
54
+ if ($wrapper.is(':hidden')) {
55
+ // replace the content of the form area and initiate the chosen and fileinputer
56
+ $wrapper.html(data).find('.select select').fae_chosen({ width: '300px' });
57
+ $wrapper.find('.input.file').fileinputer();
58
+ $wrapper.slideDown();
59
+
60
+ } else {
61
+ // if it is visible, replace its content by retaining height
62
+ $wrapper.height($wrapper.height());
63
+
64
+ // replace the content of the form area and then remove that height and then chosen and then fileinputer
65
+ $wrapper.html(data).css('height', '').find('.select select').fae_chosen();
66
+ $wrapper.find('.input.file').fileinputer();
67
+ }
68
+
69
+ // Bind validation to nested form fields added by AJAX
70
+ Fae.form.validator.bindValidationEvents($('.nested-form'));
71
+
72
+ // Reinitialize form elements
73
+ Fae.form.dates.initDatepicker();
74
+ Fae.form.dates.initDateRangePicker();
75
+ Fae.form.color.initColorpicker();
76
+ Fae.form.slugger.addListener();
77
+ Fae.form.validator.length_counter.init();
78
+ Fae.form.text.initMarkdown();
79
+ Fae.form.checkbox.setCheckboxAsActive();
80
+
81
+ $wrapper.find('.hint').hinter();
82
+ });
83
+ },
84
+
85
+ /**
86
+ * Click event listener for cancel links applied to both index and nested forms; clears form to prevent saving errors
87
+ */
88
+ addCancelLinks: function() {
89
+ this.$addedit_form.on('click', '.js-cancel-nested', function(ev) {
90
+ ev.preventDefault();
91
+ var $this = $(this);
92
+ var $form_wrapper = $this.closest('.js-addedit-form-wrapper');
93
+
94
+ if ($form_wrapper.length) {
95
+ $form_wrapper.slideUp('normal', function(){
96
+ $form_wrapper.empty();
97
+ });
98
+ }
99
+ });
100
+ },
101
+
102
+ /**
103
+ * Once form is submitted and receives a successful AJAX response, replace form data and initialize listeners on nested elements
104
+ * @fires {@link navigation.fadeNotices}
105
+ */
106
+ addEditSubmission: function() {
107
+ var _this = this;
108
+
109
+ this.$addedit_form.on('ajax:success', function(evt, data, status, xhr){
110
+
111
+ var $target = $(evt.target);
112
+
113
+ // ignore calls not returning html
114
+ if (data !== ' ' && $(data)[0]) {
115
+ var $this = $(this);
116
+
117
+ // if its the new or old remotipart, return the html
118
+ var $html = $(data).length === 1 ? $(data) : $(data)[2];
119
+
120
+ // if it returns data inside textarea, strip that out
121
+ if ( $($html).is('textarea') ) {
122
+ $html = $( $($html).val() );
123
+ }
124
+
125
+ if ($html) {
126
+ if($html.hasClass('js-addedit-form') || $html.hasClass( 'js-index-addedit-form' )) {
127
+ // we're returning the table, replace everything
128
+ _this._addEditReplaceAndReinit($this, $html.html(), $target);
129
+ } else if ($html.hasClass('nested-form')) {
130
+
131
+ // we're returning the form due to an error, just replace the form
132
+ $this.find( '.nested-form' ).replaceWith(data);
133
+ $this.find('.select select').fae_chosen();
134
+ $this.find('.input.file').fileinputer();
135
+
136
+ Fae.form.dates.initDatepicker();
137
+ Fae.form.dates.initDateRangePicker();
138
+ Fae.form.color.initColorpicker();
139
+ Fae.form.validator.length_counter.init();
140
+ Fae.form.checkbox.setCheckboxAsActive();
141
+ Fae.form.text.initMarkdown();
142
+
143
+ FCH.smoothScroll($this.find('.js-addedit-form-wrapper'), 500, 100, 120);
144
+ }
145
+ }
146
+
147
+ if (_this.$filter_form.length) {
148
+ _this.filterSubmission();
149
+ }
150
+
151
+ Fae.navigation.fadeNotices();
152
+
153
+ } else if ($target.hasClass('js-asset-delete')) {
154
+ // handle remove asset links on nested forms
155
+ var $parent = $target.closest('.asset-actions');
156
+
157
+ $parent.fadeOut(function(){
158
+ $parent.next('.asset-inputs').fadeIn();
159
+ });
160
+ }
161
+
162
+ Fae.navigation.lockFooter();
163
+ });
164
+ },
165
+
166
+ /**
167
+ * Replace AJAX'd form and add calls to all new HTML elements
168
+ * @protected
169
+ * @param $el {jQuery} - Object to be replaced
170
+ * @param html {String} - New HTML
171
+ * @param $target {jQuery} - Original form wrapper
172
+ * @see addEditSubmission
173
+ */
174
+ _addEditReplaceAndReinit: function($el, html, $target) {
175
+ var $form_wrapper = $el.find('.js-addedit-form-wrapper');
176
+
177
+ // Private function replaces parent element with HTML and reinits select and sorting
178
+ function regenerateHTML() {
179
+ // .html() is not replacing it properly
180
+ $el.get(0).innerHTML = html;
181
+ $el.find('.select select').fae_chosen();
182
+ Fae.tables.rowSorting();
183
+ Fae.navigation.fadeNotices();
184
+
185
+ if ($el.find('.js-content-header').length) {
186
+ Fae.navigation.stickyHeaders(true);
187
+ }
188
+ }
189
+
190
+ // if there's a form wrap, slide it up before replacing content
191
+ if ($form_wrapper.length) {
192
+ $form_wrapper.slideUp(regenerateHTML);
193
+
194
+ } else {
195
+ regenerateHTML();
196
+ }
197
+
198
+ if (!$target.hasClass('js-delete-link')) {
199
+ FCH.smoothScroll($el.parent(), 500, 100, 120);
200
+ }
201
+ },
202
+
203
+ /**
204
+ * Attach filter listeners
205
+ */
206
+ filterSubmission: function() {
207
+ var _this = this;
208
+
209
+ _this.$filter_form
210
+ .on('submit', function() {
211
+ $('.js-reset-btn').show();
212
+ })
213
+
214
+ // On filter change, update table data
215
+ .on('ajax:success', function(evt, data, status, xhr){
216
+ $(this).next('table').replaceWith( $(data).find('table').first() );
217
+ $('.pagination').replaceWith( $(data).find('.pagination').first() );
218
+
219
+ Fae.tables.columnSorting();
220
+ Fae.tables.rowSorting();
221
+ Fae.tables.sortColumnsFromCookies();
222
+ Fae.navigation.lockFooter();
223
+ })
224
+
225
+ // Reset filter button
226
+ .on('click', '.js-reset-btn', function(ev) {
227
+ ev.preventDefault();
228
+
229
+ var $form = $(this).closest('form');
230
+
231
+ $form.get(0).reset();
232
+ $form.find('select').val('').trigger('chosen:updated');
233
+ // reset hashies
234
+ window.location.hash = '';
235
+
236
+ // Spoof form submission
237
+ $form.submit();
238
+
239
+ $(this).hide();
240
+ })
241
+
242
+ .on('click', '.table-filter-keyword-wrapper i', function() {
243
+ _this.$filter_form.submit();
244
+ })
245
+
246
+ .on('change', 'select', function() {
247
+ _this.$filter_form.submit();
248
+ // Update hash when filter dropdowns changed
249
+ var key = $(this).attr('id').split('filter_')[1];
250
+ var value = $(this).val();
251
+ _this.fry.update(key, value);
252
+ });
253
+ },
254
+
255
+ /**
256
+ * Delete or replace file for non-AJAX'd fields
257
+ */
258
+ deleteNoForm: function() {
259
+ $('.js-asset-delete').on('ajax:success', function(){
260
+ var $this = $(this);
261
+ if (!$this.closest('.js-addedit-form-wrapper').length) {
262
+ // handle remove asset links
263
+ var $parent = $this.closest('.asset-actions');
264
+
265
+ $parent.fadeOut(function(){
266
+ $parent.next('.asset-inputs').fadeIn();
267
+ });
268
+ }
269
+ });
270
+ },
271
+
272
+ /**
273
+ * Attach delete listener to images uploaded
274
+ */
275
+ imageDeleteLinks: function() {
276
+ $('.imageDeleteLink').click(function(e) {
277
+ e.preventDefault();
278
+ var $this = $(this);
279
+
280
+ if (confirm('Are you sure you want to delete this image?')) {
281
+ $.post($this.attr('href'), 'html');
282
+ $this.parent().next().show();
283
+ $this.parent().hide();
284
+ }
285
+ });
286
+ },
287
+
288
+ /**
289
+ * Attaching click handlers to #js-main-content to allow ajax replacement
290
+ * @todo Clean this up, moving listeners into their respective component classes (select, checkbox, etc.)
291
+ */
292
+ htmlListeners: function() {
293
+ $('#js-main-content, .login-form > form')
294
+
295
+ /**
296
+ * For the yes/no slider
297
+ */
298
+ .on('click', '.slider-wrapper', function(e){
299
+ e.preventDefault();
300
+ $(this).toggleClass('slider-yes-selected');
301
+ })
302
+
303
+ /**
304
+ * The settings menu for tables and checkboxes
305
+ */
306
+ .on('click', '.boolean label, .js-checkbox-wrapper label', function(e){
307
+ var $this = $(this);
308
+
309
+ if(!$this.hasClass('disabled') && !$this.hasClass('readonly')) {
310
+ $this.toggleClass('active');
311
+ }
312
+ })
313
+
314
+ /**
315
+ * Stop event bubbling and running the above toggleClass twice
316
+ */
317
+ .on('click', '.boolean :checkbox, .js-checkbox-wrapper :checkbox', function(e){
318
+ e.stopPropagation();
319
+ })
320
+
321
+ /**
322
+ * Support for a focus state on radio collections
323
+ */
324
+ .on('focus blur', '.radio_collection :radio', function(e) {
325
+ $(this)
326
+ .closest('.input.radio_collection')
327
+ .toggleClass('focused', $(this).has(':focus'));
328
+ })
329
+
330
+ /**
331
+ * Support for a focus state on checkboxes
332
+ */
333
+ .on('focus blur', '.boolean :checkbox, .js-checkbox-wrapper :checkbox', function(e) {
334
+ $(this)
335
+ .closest('label.boolean, span.checkbox')
336
+ .toggleClass('focused', $(this).has(':focus'));
337
+ })
338
+
339
+ /**
340
+ * Support spacebar toggling for checkboxes
341
+ */
342
+ .on('keydown', '.boolean, .js-checkbox-wrapper :checkbox', function(e) {
343
+ if (e.which === 32) {
344
+ e.preventDefault();
345
+ $(':checkbox:focus')
346
+ .closest('label')
347
+ .trigger('click');
348
+ }
349
+ })
350
+
351
+ /**
352
+ * Support for shift+tab off of ms-list element.
353
+ * By default, $.multiSelect() plugin captures shift+tab and disgards it
354
+ * @todo This entire method feels very brittle. Possible alternative:
355
+ * - Create an index of all focusable form elements on page load / DOM mutation
356
+ * - Use this index to navigate upwards from .ms-list element
357
+ */
358
+ .on('keydown', '.ms-list', function(e) {
359
+ if (e.which === 9 && e.shiftKey) {
360
+ e.preventDefault();
361
+
362
+ // Sniff out the previous focusable element
363
+ var $prevFocusable = $(this)
364
+ .closest('.input')
365
+ .prevAll('.input:not(.hidden):first') // Ugh X(
366
+ .find('input[type!=hidden][type!=file], select, button, textarea') // Yuck :(
367
+ .first();
368
+
369
+ // Trigger focus
370
+ $prevFocusable.focus();
371
+
372
+ // Check for select instance and further trigger focus via Chosen API
373
+ if ($prevFocusable.is('select')) {
374
+ $prevFocusable.trigger('chosen:activate');
375
+ }
376
+ }
377
+ });
378
+ }
379
+
380
+ };
@@ -0,0 +1,38 @@
1
+ /* global Fae */
2
+
3
+ /**
4
+ * Fae form cancel
5
+ * @namespace form.cancel
6
+ * @memberof form
7
+ */
8
+ Fae.form.cancel = {
9
+
10
+ init: function() {
11
+ this.detectCancelledUrls();
12
+ this.addCancelParam();
13
+ },
14
+
15
+ /**
16
+ * If URL has cancelled param, update the history
17
+ */
18
+ detectCancelledUrls: function() {
19
+ var params = window.location.search;
20
+ if (params.length && params.toLowerCase().indexOf("cancelled") >= 0 && params.indexOf("&") !== 0) {
21
+ window.history.replaceState(null, null, window.location.pathname);
22
+ };
23
+ },
24
+
25
+ /**
26
+ * Once any field changes, add cancelled param to button to ensure user knows data will be lost
27
+ */
28
+ addCancelParam: function() {
29
+ function updateCancel() {
30
+ var $cancel_btn = $('#js-header-cancel');
31
+ var new_href = $cancel_btn.attr('href') + '?cancelled=true';
32
+ $cancel_btn.attr('href', new_href);
33
+ $('form').off('change', 'input, textarea, select', updateCancel);
34
+ }
35
+
36
+ $('form').on('change', 'input, textarea, select', updateCancel);
37
+ }
38
+ };
@@ -0,0 +1,276 @@
1
+ /* global Fae, fae_chosen, fileinputer, FCH, Cookies */
2
+
3
+ /**
4
+ * Fae Filtering, Sorting and Paging
5
+ * @namespace form.filtering
6
+ * @memberof form
7
+ */
8
+ Fae.form.filtering = {
9
+
10
+ init: function() {
11
+ this.$filter_form = $('.js-filter-form');
12
+ this.$pagination = $('.pagination');
13
+ this.has_hash_on_load_and_cookie = false;
14
+
15
+ // init only on pages with filering, sorting or paging
16
+ if (this.$filter_form.length || this.$pagination.length || $('.js-sort-column').length) {
17
+ this.applyCookies();
18
+ var window_hash = window.location.hash;
19
+ var has_hash_on_load = window_hash.length > 2;
20
+ this.fry = new Fryr(this._refreshTable, {}, has_hash_on_load);
21
+
22
+ // this.fry.params needs to be set to the "deep link" passed params
23
+ if(has_hash_on_load) {
24
+ this.fry.parse(window_hash);
25
+ }
26
+
27
+ this.setFilterDropDowns();
28
+ this.filterFormListeners();
29
+ this.paginationListeners();
30
+ this.sortingSetup();
31
+ }
32
+ },
33
+
34
+ /**
35
+ * Fryr callback, POSTs to Fae's endpoint and updates the view
36
+ */
37
+ _refreshTable: function() {
38
+ // hardcode _this because this === Fryr object
39
+ var _this = Fae.form.filtering;
40
+
41
+ if (_this.$filter_form.length) {
42
+ post_url = _this.$filter_form.attr('action');
43
+ $current_table = _this.$filter_form.next('table');
44
+ } else {
45
+ post_url = _this.$pagination.data('filter-path');
46
+ $current_table = _this.$pagination.prevAll(":visible:first");
47
+ }
48
+
49
+ var $results_table = ($(".js-results-table").length) ? $(".js-results-table").first() : $current_table;
50
+
51
+ $results_table.addClass('loading-fade');
52
+
53
+ $.post(post_url, this.params, function(data){
54
+ var $data = $(data);
55
+ var $table_from_data = $data.find('table').first();
56
+
57
+ // replace table
58
+ $results_table.replaceWith($table_from_data);
59
+
60
+ // replace sticky header
61
+ $('.sticky-table-header thead').html($table_from_data.find('thead').html());
62
+ // replace pagination
63
+ _this.$pagination.html($data.find('.pagination').first().html());
64
+
65
+ // reinit a few things on the fresh table
66
+ Fae.tables.columnSorting();
67
+ Fae.tables.rowSorting();
68
+ Fae.navigation.lockFooter();
69
+
70
+ _this.sortingSetup();
71
+
72
+ // dont set the cookie if it's a "deep link" and they have an existing cookie - leave that intact with their old saved filter
73
+ if (!_this.has_hash_on_load_and_cookie) {
74
+ _this.setCookie();
75
+ }
76
+ });
77
+ },
78
+
79
+
80
+ /**
81
+ * Adds listeners to the filter forms
82
+ */
83
+ filterFormListeners: function() {
84
+ var _this = this;
85
+
86
+ this.$filter_form
87
+ // update search param when form submits
88
+ .on('submit', function(ev) {
89
+ $('.js-reset-btn').show();
90
+ _this.updateFryrAndResetPaging('search', $('#filter_search').val());
91
+ return false;
92
+ })
93
+
94
+ // update search param when search btn is clicked
95
+ .on('click', '.table-filter-keyword-wrapper i', function() {
96
+ _this.updateFryrAndResetPaging('search', $('#filter_search').val());
97
+ })
98
+
99
+ // reset filter button
100
+ .on('click', '.js-reset-btn', function(ev) {
101
+ var $this = $(this);
102
+ ev.preventDefault();
103
+
104
+ // reset params and form selects
105
+ _this.fry.merge({ page: '' }, true);
106
+ $('#filter_search').val('');
107
+ $this.closest('form').find('select').val('').trigger('chosen:updated');
108
+ $this.hide();
109
+ })
110
+
111
+ // update hash when filter dropdowns changed
112
+ .on('change', 'select', function() {
113
+ // get key and value from select
114
+ var key = $(this).attr('id').split('filter_')[1];
115
+ var value = $(this).val();
116
+
117
+ _this.updateFryrAndResetPaging(key, value);
118
+
119
+ $('.js-reset-btn').show();
120
+ });
121
+ },
122
+
123
+ /**
124
+ * Sets filter dropdowns on page load based on Fryr params
125
+ */
126
+ setFilterDropDowns: function() {
127
+ // Exit early if this.fry.params is blank
128
+ if ($.isEmptyObject(this.fry.params)) {
129
+ return;
130
+ }
131
+
132
+ // Loop through all available this.fry.params to find the select menu and the proper option
133
+ $.each(this.fry.params, function(key, value) {
134
+ var $select = $('.js-filter-form .table-filter-group #filter_' + key);
135
+
136
+ if($select.length) {
137
+ var $option = $select.find('option[value="' + value + '"]');
138
+
139
+ // Ensure we find the option and that it isn't already chosen
140
+ if($option.length && $option.prop('selected') !== 'selected') {
141
+ $option.prop('selected', 'selected');
142
+ $select.trigger('chosen:updated');
143
+ }
144
+ }
145
+ });
146
+
147
+ $('.js-reset-btn').show();
148
+ },
149
+
150
+ /**
151
+ * Updates a Fryr param while reseting paging
152
+ * Use for most cases of pushing a single params to Fryr, as paging will always reset in those cases
153
+ * @param {string} key - The Fryr param key to be changed
154
+ * @param {string} value - The value to be changed to
155
+ */
156
+ updateFryrAndResetPaging: function(key, value) {
157
+ var values_obj = { page: '' };
158
+ values_obj[key] = value;
159
+ this.fry.merge(values_obj);
160
+ },
161
+
162
+ /**
163
+ * Adds listeners to pagination
164
+ */
165
+ paginationListeners: function() {
166
+ var _this = this;
167
+ // update params when pagination link clicked
168
+ $('.pagination').on('click', 'a', function(ev) {
169
+ ev.preventDefault();
170
+ _this.fry.merge({ page: $(this).data('page') });
171
+ })
172
+ },
173
+
174
+ /**
175
+ * Wrapper method to setup HTML and listeners to support sorting
176
+ * Called on load and this._refreshTable()
177
+ */
178
+ sortingSetup: function() {
179
+ this._addSortingClasses();
180
+ this._addSortingListeners();
181
+ },
182
+
183
+ /**
184
+ * Updates sortable table headers
185
+ */
186
+ _addSortingClasses: function() {
187
+ // get sort_by and sort_direction from Fryr
188
+ var sort_by = this.fry.params.sort_by;
189
+ var sort_direction = this.fry.params.sort_direction;
190
+ if (!sort_direction) {
191
+ sort_direction = 'asc';
192
+ }
193
+
194
+ var $sortable_th = $('th[data-sort]');
195
+ // add sort_direction class to current sorted column
196
+ $sortable_th.filter('[data-sort="'+sort_by+'"]').addClass('-'+sort_direction);
197
+ // add .th-sortable-title to support styling
198
+ $sortable_th.not(':has(.th-sortable-title)').wrapInner('<div class="th-sortable-title"></div>');
199
+ },
200
+
201
+ /**
202
+ * Adds listeners to the sortable table headers
203
+ */
204
+ _addSortingListeners: function() {
205
+ var _this = this;
206
+
207
+ // Fizzle if listeneres have already been attached
208
+ if (this._are_sorting_listeners_setup === true) { return; }
209
+
210
+ $('body').on('click', 'th[data-sort]', function() {
211
+ var $this = $(this);
212
+ var new_direction = 'asc';
213
+
214
+ // update sort_direction class
215
+ if ($this.hasClass('-asc')) {
216
+ new_direction = 'desc';
217
+ $this.addClass('-desc').removeClass('-asc');
218
+ } else {
219
+ $this.addClass('-asc').removeClass('-desc');
220
+ }
221
+
222
+ // update Fryr
223
+ _this.fry.merge({
224
+ sort_by: $this.data('sort'),
225
+ sort_direction: new_direction,
226
+ page: ''
227
+ });
228
+ });
229
+
230
+ // To ensure listeners are only attached once
231
+ this._are_sorting_listeners_setup = true;
232
+ },
233
+
234
+ /**
235
+ * Sets the filter form's cookie to the Fryr params
236
+ */
237
+ setCookie: function() {
238
+ var cookie_name = this.$filter_form.attr('data-cookie-key');
239
+ Cookies.set(cookie_name, this.fry.params);
240
+ },
241
+
242
+ /**
243
+ * If cookies are available, load them into the hash
244
+ */
245
+ applyCookies: function() {
246
+ var cookie_key = $('.js-filter-form').attr('data-cookie-key');
247
+
248
+ if (cookie_key) {
249
+ var cookie = Cookies.getJSON(cookie_key);
250
+
251
+ if (!$.isEmptyObject(cookie)) {
252
+
253
+ // exit now if it's a "deep link" so the passed params get used instead of saved cookie params
254
+ if (window.location.hash.length > 2) {
255
+ this.has_hash_on_load_and_cookie = true;
256
+ return false;
257
+ }
258
+
259
+ var keys = Object.keys(cookie);
260
+ var hash = '?';
261
+
262
+ for(var i = 0; i < keys.length; i++) {
263
+ if (hash !== '?') {
264
+ hash += '&';
265
+ }
266
+ hash += keys[i] + '=' + cookie[keys[i]];
267
+ }
268
+
269
+ if (hash !== '?') {
270
+ window.location.hash = hash;
271
+ }
272
+ }
273
+ }
274
+ }
275
+
276
+ };