fae-railsz 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ };