effective_bootstrap 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (525) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +644 -0
  4. data/app/assets/images/icons/activity.svg +13 -0
  5. data/app/assets/images/icons/airplay.svg +14 -0
  6. data/app/assets/images/icons/alert-circle.svg +15 -0
  7. data/app/assets/images/icons/alert-octagon.svg +15 -0
  8. data/app/assets/images/icons/alert-triangle.svg +15 -0
  9. data/app/assets/images/icons/align-center.svg +16 -0
  10. data/app/assets/images/icons/align-justify.svg +16 -0
  11. data/app/assets/images/icons/align-left.svg +16 -0
  12. data/app/assets/images/icons/align-right.svg +16 -0
  13. data/app/assets/images/icons/anchor.svg +15 -0
  14. data/app/assets/images/icons/aperture.svg +19 -0
  15. data/app/assets/images/icons/arrow-down-circle.svg +15 -0
  16. data/app/assets/images/icons/arrow-down-left.svg +14 -0
  17. data/app/assets/images/icons/arrow-down-right.svg +14 -0
  18. data/app/assets/images/icons/arrow-down.svg +14 -0
  19. data/app/assets/images/icons/arrow-left-circle.svg +15 -0
  20. data/app/assets/images/icons/arrow-left.svg +14 -0
  21. data/app/assets/images/icons/arrow-right-circle.svg +15 -0
  22. data/app/assets/images/icons/arrow-right.svg +14 -0
  23. data/app/assets/images/icons/arrow-up-circle.svg +15 -0
  24. data/app/assets/images/icons/arrow-up-left.svg +14 -0
  25. data/app/assets/images/icons/arrow-up-right.svg +14 -0
  26. data/app/assets/images/icons/arrow-up.svg +14 -0
  27. data/app/assets/images/icons/at-sign.svg +14 -0
  28. data/app/assets/images/icons/award.svg +14 -0
  29. data/app/assets/images/icons/bar-chart-2.svg +15 -0
  30. data/app/assets/images/icons/bar-chart.svg +15 -0
  31. data/app/assets/images/icons/battery-charging.svg +15 -0
  32. data/app/assets/images/icons/battery.svg +14 -0
  33. data/app/assets/images/icons/bell-off.svg +14 -0
  34. data/app/assets/images/icons/bell.svg +13 -0
  35. data/app/assets/images/icons/bluetooth.svg +13 -0
  36. data/app/assets/images/icons/bold.svg +14 -0
  37. data/app/assets/images/icons/book-open.svg +14 -0
  38. data/app/assets/images/icons/book.svg +14 -0
  39. data/app/assets/images/icons/bookmark.svg +13 -0
  40. data/app/assets/images/icons/box.svg +15 -0
  41. data/app/assets/images/icons/briefcase.svg +14 -0
  42. data/app/assets/images/icons/calendar.svg +16 -0
  43. data/app/assets/images/icons/camera-off.svg +14 -0
  44. data/app/assets/images/icons/camera.svg +14 -0
  45. data/app/assets/images/icons/cast.svg +14 -0
  46. data/app/assets/images/icons/check-circle.svg +14 -0
  47. data/app/assets/images/icons/check-square.svg +14 -0
  48. data/app/assets/images/icons/check.svg +13 -0
  49. data/app/assets/images/icons/chevron-down.svg +13 -0
  50. data/app/assets/images/icons/chevron-left.svg +13 -0
  51. data/app/assets/images/icons/chevron-right.svg +13 -0
  52. data/app/assets/images/icons/chevron-up.svg +13 -0
  53. data/app/assets/images/icons/chevrons-down.svg +14 -0
  54. data/app/assets/images/icons/chevrons-left.svg +14 -0
  55. data/app/assets/images/icons/chevrons-right.svg +14 -0
  56. data/app/assets/images/icons/chevrons-up.svg +14 -0
  57. data/app/assets/images/icons/chrome.svg +17 -0
  58. data/app/assets/images/icons/circle.svg +13 -0
  59. data/app/assets/images/icons/clipboard.svg +14 -0
  60. data/app/assets/images/icons/clock.svg +14 -0
  61. data/app/assets/images/icons/cloud-drizzle.svg +19 -0
  62. data/app/assets/images/icons/cloud-lightning.svg +14 -0
  63. data/app/assets/images/icons/cloud-off.svg +14 -0
  64. data/app/assets/images/icons/cloud-rain.svg +16 -0
  65. data/app/assets/images/icons/cloud-snow.svg +19 -0
  66. data/app/assets/images/icons/cloud.svg +13 -0
  67. data/app/assets/images/icons/code.svg +14 -0
  68. data/app/assets/images/icons/codepen.svg +17 -0
  69. data/app/assets/images/icons/command.svg +13 -0
  70. data/app/assets/images/icons/compass.svg +14 -0
  71. data/app/assets/images/icons/copy.svg +14 -0
  72. data/app/assets/images/icons/corner-down-left.svg +14 -0
  73. data/app/assets/images/icons/corner-down-right.svg +14 -0
  74. data/app/assets/images/icons/corner-left-down.svg +14 -0
  75. data/app/assets/images/icons/corner-left-up.svg +14 -0
  76. data/app/assets/images/icons/corner-right-down.svg +14 -0
  77. data/app/assets/images/icons/corner-right-up.svg +14 -0
  78. data/app/assets/images/icons/corner-up-left.svg +14 -0
  79. data/app/assets/images/icons/corner-up-right.svg +14 -0
  80. data/app/assets/images/icons/cpu.svg +22 -0
  81. data/app/assets/images/icons/credit-card.svg +14 -0
  82. data/app/assets/images/icons/crop.svg +14 -0
  83. data/app/assets/images/icons/crosshair.svg +17 -0
  84. data/app/assets/images/icons/database.svg +15 -0
  85. data/app/assets/images/icons/delete.svg +15 -0
  86. data/app/assets/images/icons/disc.svg +14 -0
  87. data/app/assets/images/icons/dollar-sign.svg +14 -0
  88. data/app/assets/images/icons/download-cloud.svg +15 -0
  89. data/app/assets/images/icons/download.svg +15 -0
  90. data/app/assets/images/icons/droplet.svg +13 -0
  91. data/app/assets/images/icons/edit-2.svg +13 -0
  92. data/app/assets/images/icons/edit-3.svg +14 -0
  93. data/app/assets/images/icons/edit.svg +14 -0
  94. data/app/assets/images/icons/external-link.svg +15 -0
  95. data/app/assets/images/icons/eye-off.svg +14 -0
  96. data/app/assets/images/icons/eye.svg +14 -0
  97. data/app/assets/images/icons/facebook-white.svg +7 -0
  98. data/app/assets/images/icons/facebook.svg +13 -0
  99. data/app/assets/images/icons/fast-forward.svg +14 -0
  100. data/app/assets/images/icons/feather.svg +15 -0
  101. data/app/assets/images/icons/file-minus.svg +15 -0
  102. data/app/assets/images/icons/file-plus.svg +16 -0
  103. data/app/assets/images/icons/file-text.svg +17 -0
  104. data/app/assets/images/icons/file.svg +14 -0
  105. data/app/assets/images/icons/film.svg +20 -0
  106. data/app/assets/images/icons/filter.svg +13 -0
  107. data/app/assets/images/icons/flag.svg +14 -0
  108. data/app/assets/images/icons/folder-minus.svg +14 -0
  109. data/app/assets/images/icons/folder-plus.svg +15 -0
  110. data/app/assets/images/icons/folder.svg +13 -0
  111. data/app/assets/images/icons/git-branch.svg +16 -0
  112. data/app/assets/images/icons/git-commit.svg +15 -0
  113. data/app/assets/images/icons/git-merge.svg +15 -0
  114. data/app/assets/images/icons/git-pull-request.svg +16 -0
  115. data/app/assets/images/icons/github.svg +13 -0
  116. data/app/assets/images/icons/gitlab.svg +13 -0
  117. data/app/assets/images/icons/globe.svg +15 -0
  118. data/app/assets/images/icons/google.svg +2 -0
  119. data/app/assets/images/icons/grid.svg +16 -0
  120. data/app/assets/images/icons/hard-drive.svg +16 -0
  121. data/app/assets/images/icons/hash.svg +16 -0
  122. data/app/assets/images/icons/headphones.svg +14 -0
  123. data/app/assets/images/icons/heart.svg +13 -0
  124. data/app/assets/images/icons/help-circle.svg +15 -0
  125. data/app/assets/images/icons/home.svg +14 -0
  126. data/app/assets/images/icons/image.svg +15 -0
  127. data/app/assets/images/icons/inbox.svg +14 -0
  128. data/app/assets/images/icons/info.svg +15 -0
  129. data/app/assets/images/icons/instagram.svg +15 -0
  130. data/app/assets/images/icons/italic.svg +15 -0
  131. data/app/assets/images/icons/layers.svg +15 -0
  132. data/app/assets/images/icons/layout.svg +15 -0
  133. data/app/assets/images/icons/life-buoy.svg +19 -0
  134. data/app/assets/images/icons/link-2.svg +14 -0
  135. data/app/assets/images/icons/link.svg +14 -0
  136. data/app/assets/images/icons/linkedin.svg +15 -0
  137. data/app/assets/images/icons/list.svg +18 -0
  138. data/app/assets/images/icons/loader.svg +20 -0
  139. data/app/assets/images/icons/lock.svg +14 -0
  140. data/app/assets/images/icons/log-in.svg +15 -0
  141. data/app/assets/images/icons/log-out.svg +15 -0
  142. data/app/assets/images/icons/mail.svg +14 -0
  143. data/app/assets/images/icons/map-pin.svg +14 -0
  144. data/app/assets/images/icons/map.svg +15 -0
  145. data/app/assets/images/icons/maximize-2.svg +16 -0
  146. data/app/assets/images/icons/maximize.svg +13 -0
  147. data/app/assets/images/icons/menu.svg +15 -0
  148. data/app/assets/images/icons/message-circle.svg +13 -0
  149. data/app/assets/images/icons/message-square.svg +13 -0
  150. data/app/assets/images/icons/mic-off.svg +17 -0
  151. data/app/assets/images/icons/mic.svg +16 -0
  152. data/app/assets/images/icons/minimize-2.svg +16 -0
  153. data/app/assets/images/icons/minimize.svg +13 -0
  154. data/app/assets/images/icons/minus-circle.svg +14 -0
  155. data/app/assets/images/icons/minus-square.svg +14 -0
  156. data/app/assets/images/icons/minus.svg +13 -0
  157. data/app/assets/images/icons/monitor.svg +15 -0
  158. data/app/assets/images/icons/moon.svg +13 -0
  159. data/app/assets/images/icons/more-horizontal.svg +15 -0
  160. data/app/assets/images/icons/more-vertical.svg +15 -0
  161. data/app/assets/images/icons/move.svg +18 -0
  162. data/app/assets/images/icons/music.svg +14 -0
  163. data/app/assets/images/icons/navigation-2.svg +13 -0
  164. data/app/assets/images/icons/navigation.svg +13 -0
  165. data/app/assets/images/icons/octagon.svg +13 -0
  166. data/app/assets/images/icons/package.svg +16 -0
  167. data/app/assets/images/icons/paperclip.svg +13 -0
  168. data/app/assets/images/icons/pause-circle.svg +15 -0
  169. data/app/assets/images/icons/pause.svg +14 -0
  170. data/app/assets/images/icons/percent.svg +15 -0
  171. data/app/assets/images/icons/phone-call.svg +13 -0
  172. data/app/assets/images/icons/phone-forwarded.svg +15 -0
  173. data/app/assets/images/icons/phone-incoming.svg +15 -0
  174. data/app/assets/images/icons/phone-missed.svg +15 -0
  175. data/app/assets/images/icons/phone-off.svg +14 -0
  176. data/app/assets/images/icons/phone-outgoing.svg +15 -0
  177. data/app/assets/images/icons/phone.svg +13 -0
  178. data/app/assets/images/icons/pie-chart.svg +14 -0
  179. data/app/assets/images/icons/play-circle.svg +14 -0
  180. data/app/assets/images/icons/play.svg +13 -0
  181. data/app/assets/images/icons/plus-circle.svg +15 -0
  182. data/app/assets/images/icons/plus-square.svg +15 -0
  183. data/app/assets/images/icons/plus.svg +14 -0
  184. data/app/assets/images/icons/pocket.svg +14 -0
  185. data/app/assets/images/icons/power.svg +14 -0
  186. data/app/assets/images/icons/printer.svg +15 -0
  187. data/app/assets/images/icons/radio.svg +14 -0
  188. data/app/assets/images/icons/refresh-ccw.svg +15 -0
  189. data/app/assets/images/icons/refresh-cw.svg +15 -0
  190. data/app/assets/images/icons/repeat.svg +16 -0
  191. data/app/assets/images/icons/rewind.svg +14 -0
  192. data/app/assets/images/icons/rotate-ccw.svg +14 -0
  193. data/app/assets/images/icons/rotate-cw.svg +14 -0
  194. data/app/assets/images/icons/rss.svg +15 -0
  195. data/app/assets/images/icons/save.svg +15 -0
  196. data/app/assets/images/icons/scissors.svg +17 -0
  197. data/app/assets/images/icons/search.svg +14 -0
  198. data/app/assets/images/icons/send.svg +14 -0
  199. data/app/assets/images/icons/server.svg +16 -0
  200. data/app/assets/images/icons/settings.svg +14 -0
  201. data/app/assets/images/icons/share-2.svg +17 -0
  202. data/app/assets/images/icons/share.svg +15 -0
  203. data/app/assets/images/icons/shield-off.svg +15 -0
  204. data/app/assets/images/icons/shield.svg +13 -0
  205. data/app/assets/images/icons/shopping-bag.svg +15 -0
  206. data/app/assets/images/icons/shopping-cart.svg +15 -0
  207. data/app/assets/images/icons/shuffle.svg +17 -0
  208. data/app/assets/images/icons/sidebar.svg +14 -0
  209. data/app/assets/images/icons/skip-back.svg +14 -0
  210. data/app/assets/images/icons/skip-forward.svg +14 -0
  211. data/app/assets/images/icons/slack.svg +17 -0
  212. data/app/assets/images/icons/slash.svg +14 -0
  213. data/app/assets/images/icons/sliders.svg +21 -0
  214. data/app/assets/images/icons/smartphone.svg +14 -0
  215. data/app/assets/images/icons/speaker.svg +15 -0
  216. data/app/assets/images/icons/spinner.svg +1 -0
  217. data/app/assets/images/icons/square.svg +13 -0
  218. data/app/assets/images/icons/star.svg +13 -0
  219. data/app/assets/images/icons/stop-circle.svg +14 -0
  220. data/app/assets/images/icons/sun.svg +21 -0
  221. data/app/assets/images/icons/sunrise.svg +20 -0
  222. data/app/assets/images/icons/sunset.svg +20 -0
  223. data/app/assets/images/icons/tablet.svg +22 -0
  224. data/app/assets/images/icons/tag.svg +14 -0
  225. data/app/assets/images/icons/target.svg +15 -0
  226. data/app/assets/images/icons/terminal.svg +14 -0
  227. data/app/assets/images/icons/thermometer.svg +13 -0
  228. data/app/assets/images/icons/thumbs-down.svg +13 -0
  229. data/app/assets/images/icons/thumbs-up.svg +13 -0
  230. data/app/assets/images/icons/toggle-left.svg +14 -0
  231. data/app/assets/images/icons/toggle-right.svg +14 -0
  232. data/app/assets/images/icons/trash-2.svg +16 -0
  233. data/app/assets/images/icons/trash.svg +14 -0
  234. data/app/assets/images/icons/trending-down.svg +14 -0
  235. data/app/assets/images/icons/trending-up.svg +14 -0
  236. data/app/assets/images/icons/triangle.svg +13 -0
  237. data/app/assets/images/icons/truck.svg +16 -0
  238. data/app/assets/images/icons/tv.svg +14 -0
  239. data/app/assets/images/icons/twitter.svg +13 -0
  240. data/app/assets/images/icons/type.svg +15 -0
  241. data/app/assets/images/icons/umbrella.svg +13 -0
  242. data/app/assets/images/icons/underline.svg +14 -0
  243. data/app/assets/images/icons/unlock.svg +14 -0
  244. data/app/assets/images/icons/upload-cloud.svg +16 -0
  245. data/app/assets/images/icons/upload.svg +15 -0
  246. data/app/assets/images/icons/user-check.svg +15 -0
  247. data/app/assets/images/icons/user-minus.svg +15 -0
  248. data/app/assets/images/icons/user-plus.svg +16 -0
  249. data/app/assets/images/icons/user-x.svg +16 -0
  250. data/app/assets/images/icons/user.svg +14 -0
  251. data/app/assets/images/icons/users.svg +16 -0
  252. data/app/assets/images/icons/video-off.svg +14 -0
  253. data/app/assets/images/icons/video.svg +14 -0
  254. data/app/assets/images/icons/voicemail.svg +15 -0
  255. data/app/assets/images/icons/volume-1.svg +14 -0
  256. data/app/assets/images/icons/volume-2.svg +14 -0
  257. data/app/assets/images/icons/volume-x.svg +15 -0
  258. data/app/assets/images/icons/volume.svg +13 -0
  259. data/app/assets/images/icons/watch.svg +15 -0
  260. data/app/assets/images/icons/wifi-off.svg +19 -0
  261. data/app/assets/images/icons/wifi.svg +16 -0
  262. data/app/assets/images/icons/wind.svg +13 -0
  263. data/app/assets/images/icons/x-circle.svg +15 -0
  264. data/app/assets/images/icons/x-square.svg +15 -0
  265. data/app/assets/images/icons/x.svg +14 -0
  266. data/app/assets/images/icons/zap-off.svg +16 -0
  267. data/app/assets/images/icons/zap.svg +13 -0
  268. data/app/assets/images/icons/zoom-in.svg +16 -0
  269. data/app/assets/images/icons/zoom-out.svg +15 -0
  270. data/app/assets/javascripts/effective_bootstrap.js +25 -0
  271. data/app/assets/javascripts/effective_bootstrap/base.js.coffee +22 -0
  272. data/app/assets/javascripts/effective_bootstrap/confirm.js.coffee.erb +23 -0
  273. data/app/assets/javascripts/effective_bootstrap/form.js.coffee +182 -0
  274. data/app/assets/javascripts/effective_bootstrap/logic.js.coffee +64 -0
  275. data/app/assets/javascripts/effective_bootstrap/other.js.coffee +4 -0
  276. data/app/assets/javascripts/effective_bootstrap/tabs.js +14 -0
  277. data/app/assets/javascripts/effective_checks/initialize.js.coffee +7 -0
  278. data/app/assets/javascripts/effective_checks/input.js +1 -0
  279. data/app/assets/javascripts/effective_ck_editor/initialize.js.coffee +72 -0
  280. data/app/assets/javascripts/effective_ck_editor/input.js +1 -0
  281. data/app/assets/javascripts/effective_clear/initialize.js.coffee +3 -0
  282. data/app/assets/javascripts/effective_clear/input.js +1 -0
  283. data/app/assets/javascripts/effective_date/initialize.js.coffee +7 -0
  284. data/app/assets/javascripts/effective_date/input.js +5 -0
  285. data/app/assets/javascripts/effective_datetime/bootstrap-datetimepicker.js +2636 -0
  286. data/app/assets/javascripts/effective_datetime/initialize.js.coffee +7 -0
  287. data/app/assets/javascripts/effective_datetime/input.js +5 -0
  288. data/app/assets/javascripts/effective_datetime/overrides.js.coffee +45 -0
  289. data/app/assets/javascripts/effective_datetime/turbolinks.js.coffee +5 -0
  290. data/app/assets/javascripts/effective_editor/image-drop-and-paste.js +3 -0
  291. data/app/assets/javascripts/effective_editor/image-resize.js +2 -0
  292. data/app/assets/javascripts/effective_editor/initialize.js.coffee +138 -0
  293. data/app/assets/javascripts/effective_editor/input.js +4 -0
  294. data/app/assets/javascripts/effective_editor/quill.js +11496 -0
  295. data/app/assets/javascripts/effective_file/initialize.js.coffee +19 -0
  296. data/app/assets/javascripts/effective_file/input.js +1 -0
  297. data/app/assets/javascripts/effective_integer/initialize.js.coffee +6 -0
  298. data/app/assets/javascripts/effective_integer/input.js +1 -0
  299. data/app/assets/javascripts/effective_number_text/initialize.js.coffee +6 -0
  300. data/app/assets/javascripts/effective_number_text/input.js +1 -0
  301. data/app/assets/javascripts/effective_percent/initialize.js.coffee +39 -0
  302. data/app/assets/javascripts/effective_percent/input.js +1 -0
  303. data/app/assets/javascripts/effective_phone/initialize.js.coffee +4 -0
  304. data/app/assets/javascripts/effective_phone/input.js +2 -0
  305. data/app/assets/javascripts/effective_phone/jquery.maskedInput.js +182 -0
  306. data/app/assets/javascripts/effective_price/initialize.js.coffee +43 -0
  307. data/app/assets/javascripts/effective_price/input.js +1 -0
  308. data/app/assets/javascripts/effective_radio/initialize.js.coffee +36 -0
  309. data/app/assets/javascripts/effective_radio/input.js +1 -0
  310. data/app/assets/javascripts/effective_select/initialize.js.coffee +56 -0
  311. data/app/assets/javascripts/effective_select/input.js +3 -0
  312. data/app/assets/javascripts/effective_select/overrides.js.coffee +60 -0
  313. data/app/assets/javascripts/effective_select/select2.js +5747 -0
  314. data/app/assets/javascripts/effective_select_or_text/initialize.js +30 -0
  315. data/app/assets/javascripts/effective_select_or_text/input.js +1 -0
  316. data/app/assets/javascripts/effective_time/initialize.js.coffee +7 -0
  317. data/app/assets/javascripts/effective_time/input.js +5 -0
  318. data/app/assets/javascripts/effective_time_zone_select/initialize.js.coffee +13 -0
  319. data/app/assets/javascripts/effective_time_zone_select/input.js +1 -0
  320. data/app/assets/javascripts/moment/locale/af.js +72 -0
  321. data/app/assets/javascripts/moment/locale/ar-dz.js +58 -0
  322. data/app/assets/javascripts/moment/locale/ar-kw.js +58 -0
  323. data/app/assets/javascripts/moment/locale/ar-ly.js +121 -0
  324. data/app/assets/javascripts/moment/locale/ar-ma.js +58 -0
  325. data/app/assets/javascripts/moment/locale/ar-sa.js +103 -0
  326. data/app/assets/javascripts/moment/locale/ar-tn.js +58 -0
  327. data/app/assets/javascripts/moment/locale/ar.js +134 -0
  328. data/app/assets/javascripts/moment/locale/az.js +104 -0
  329. data/app/assets/javascripts/moment/locale/be.js +131 -0
  330. data/app/assets/javascripts/moment/locale/bg.js +89 -0
  331. data/app/assets/javascripts/moment/locale/bm.js +57 -0
  332. data/app/assets/javascripts/moment/locale/bn.js +118 -0
  333. data/app/assets/javascripts/moment/locale/bo.js +118 -0
  334. data/app/assets/javascripts/moment/locale/br.js +107 -0
  335. data/app/assets/javascripts/moment/locale/bs.js +150 -0
  336. data/app/assets/javascripts/moment/locale/ca.js +87 -0
  337. data/app/assets/javascripts/moment/locale/cs.js +171 -0
  338. data/app/assets/javascripts/moment/locale/cv.js +62 -0
  339. data/app/assets/javascripts/moment/locale/cy.js +79 -0
  340. data/app/assets/javascripts/moment/locale/da.js +59 -0
  341. data/app/assets/javascripts/moment/locale/de-at.js +75 -0
  342. data/app/assets/javascripts/moment/locale/de-ch.js +75 -0
  343. data/app/assets/javascripts/moment/locale/de.js +75 -0
  344. data/app/assets/javascripts/moment/locale/dv.js +98 -0
  345. data/app/assets/javascripts/moment/locale/el.js +99 -0
  346. data/app/assets/javascripts/moment/locale/en-SG.js +66 -0
  347. data/app/assets/javascripts/moment/locale/en-au.js +66 -0
  348. data/app/assets/javascripts/moment/locale/en-ca.js +62 -0
  349. data/app/assets/javascripts/moment/locale/en-gb.js +66 -0
  350. data/app/assets/javascripts/moment/locale/en-ie.js +66 -0
  351. data/app/assets/javascripts/moment/locale/en-il.js +61 -0
  352. data/app/assets/javascripts/moment/locale/en-nz.js +66 -0
  353. data/app/assets/javascripts/moment/locale/eo.js +70 -0
  354. data/app/assets/javascripts/moment/locale/es-do.js +91 -0
  355. data/app/assets/javascripts/moment/locale/es-us.js +91 -0
  356. data/app/assets/javascripts/moment/locale/es.js +91 -0
  357. data/app/assets/javascripts/moment/locale/et.js +79 -0
  358. data/app/assets/javascripts/moment/locale/eu.js +65 -0
  359. data/app/assets/javascripts/moment/locale/fa.js +105 -0
  360. data/app/assets/javascripts/moment/locale/fi.js +108 -0
  361. data/app/assets/javascripts/moment/locale/fo.js +59 -0
  362. data/app/assets/javascripts/moment/locale/fr-ca.js +73 -0
  363. data/app/assets/javascripts/moment/locale/fr-ch.js +77 -0
  364. data/app/assets/javascripts/moment/locale/fr.js +82 -0
  365. data/app/assets/javascripts/moment/locale/fy.js +74 -0
  366. data/app/assets/javascripts/moment/locale/ga.js +76 -0
  367. data/app/assets/javascripts/moment/locale/gd.js +75 -0
  368. data/app/assets/javascripts/moment/locale/gl.js +76 -0
  369. data/app/assets/javascripts/moment/locale/gom-latn.js +122 -0
  370. data/app/assets/javascripts/moment/locale/gu.js +123 -0
  371. data/app/assets/javascripts/moment/locale/he.js +96 -0
  372. data/app/assets/javascripts/moment/locale/hi.js +123 -0
  373. data/app/assets/javascripts/moment/locale/hr.js +153 -0
  374. data/app/assets/javascripts/moment/locale/hu.js +109 -0
  375. data/app/assets/javascripts/moment/locale/hy-am.js +94 -0
  376. data/app/assets/javascripts/moment/locale/id.js +81 -0
  377. data/app/assets/javascripts/moment/locale/is.js +131 -0
  378. data/app/assets/javascripts/moment/locale/it-ch.js +68 -0
  379. data/app/assets/javascripts/moment/locale/it.js +68 -0
  380. data/app/assets/javascripts/moment/locale/ja.js +91 -0
  381. data/app/assets/javascripts/moment/locale/jv.js +81 -0
  382. data/app/assets/javascripts/moment/locale/ka.js +88 -0
  383. data/app/assets/javascripts/moment/locale/kk.js +86 -0
  384. data/app/assets/javascripts/moment/locale/km.js +109 -0
  385. data/app/assets/javascripts/moment/locale/kn.js +125 -0
  386. data/app/assets/javascripts/moment/locale/ko.js +80 -0
  387. data/app/assets/javascripts/moment/locale/ku.js +118 -0
  388. data/app/assets/javascripts/moment/locale/ky.js +86 -0
  389. data/app/assets/javascripts/moment/locale/lb.js +135 -0
  390. data/app/assets/javascripts/moment/locale/lo.js +69 -0
  391. data/app/assets/javascripts/moment/locale/lt.js +117 -0
  392. data/app/assets/javascripts/moment/locale/lv.js +96 -0
  393. data/app/assets/javascripts/moment/locale/me.js +111 -0
  394. data/app/assets/javascripts/moment/locale/mi.js +63 -0
  395. data/app/assets/javascripts/moment/locale/mk.js +89 -0
  396. data/app/assets/javascripts/moment/locale/ml.js +80 -0
  397. data/app/assets/javascripts/moment/locale/mn.js +103 -0
  398. data/app/assets/javascripts/moment/locale/mr.js +159 -0
  399. data/app/assets/javascripts/moment/locale/ms-my.js +81 -0
  400. data/app/assets/javascripts/moment/locale/ms.js +81 -0
  401. data/app/assets/javascripts/moment/locale/mt.js +59 -0
  402. data/app/assets/javascripts/moment/locale/my.js +92 -0
  403. data/app/assets/javascripts/moment/locale/nb.js +61 -0
  404. data/app/assets/javascripts/moment/locale/ne.js +122 -0
  405. data/app/assets/javascripts/moment/locale/nl-be.js +86 -0
  406. data/app/assets/javascripts/moment/locale/nl.js +86 -0
  407. data/app/assets/javascripts/moment/locale/nn.js +59 -0
  408. data/app/assets/javascripts/moment/locale/pa-in.js +123 -0
  409. data/app/assets/javascripts/moment/locale/pl.js +125 -0
  410. data/app/assets/javascripts/moment/locale/pt-br.js +60 -0
  411. data/app/assets/javascripts/moment/locale/pt.js +64 -0
  412. data/app/assets/javascripts/moment/locale/ro.js +74 -0
  413. data/app/assets/javascripts/moment/locale/ru.js +181 -0
  414. data/app/assets/javascripts/moment/locale/sd.js +97 -0
  415. data/app/assets/javascripts/moment/locale/se.js +59 -0
  416. data/app/assets/javascripts/moment/locale/si.js +70 -0
  417. data/app/assets/javascripts/moment/locale/sk.js +155 -0
  418. data/app/assets/javascripts/moment/locale/sl.js +172 -0
  419. data/app/assets/javascripts/moment/locale/sq.js +67 -0
  420. data/app/assets/javascripts/moment/locale/sr-cyrl.js +110 -0
  421. data/app/assets/javascripts/moment/locale/sr.js +110 -0
  422. data/app/assets/javascripts/moment/locale/ss.js +87 -0
  423. data/app/assets/javascripts/moment/locale/sv.js +68 -0
  424. data/app/assets/javascripts/moment/locale/sw.js +58 -0
  425. data/app/assets/javascripts/moment/locale/ta.js +128 -0
  426. data/app/assets/javascripts/moment/locale/te.js +88 -0
  427. data/app/assets/javascripts/moment/locale/tet.js +66 -0
  428. data/app/assets/javascripts/moment/locale/tg.js +115 -0
  429. data/app/assets/javascripts/moment/locale/th.js +66 -0
  430. data/app/assets/javascripts/moment/locale/tl-ph.js +61 -0
  431. data/app/assets/javascripts/moment/locale/tlh.js +121 -0
  432. data/app/assets/javascripts/moment/locale/tr.js +93 -0
  433. data/app/assets/javascripts/moment/locale/tzl.js +90 -0
  434. data/app/assets/javascripts/moment/locale/tzm-latn.js +57 -0
  435. data/app/assets/javascripts/moment/locale/tzm.js +57 -0
  436. data/app/assets/javascripts/moment/locale/ug-cn.js +118 -0
  437. data/app/assets/javascripts/moment/locale/uk.js +153 -0
  438. data/app/assets/javascripts/moment/locale/ur.js +97 -0
  439. data/app/assets/javascripts/moment/locale/uz-latn.js +57 -0
  440. data/app/assets/javascripts/moment/locale/uz.js +57 -0
  441. data/app/assets/javascripts/moment/locale/vi.js +78 -0
  442. data/app/assets/javascripts/moment/locale/x-pseudo.js +67 -0
  443. data/app/assets/javascripts/moment/locale/yo.js +59 -0
  444. data/app/assets/javascripts/moment/locale/zh-cn.js +109 -0
  445. data/app/assets/javascripts/moment/locale/zh-hk.js +102 -0
  446. data/app/assets/javascripts/moment/locale/zh-tw.js +102 -0
  447. data/app/assets/javascripts/moment/moment.js +4603 -0
  448. data/app/assets/stylesheets/effective_bootstrap.scss +12 -0
  449. data/app/assets/stylesheets/effective_bootstrap/base.scss +4 -0
  450. data/app/assets/stylesheets/effective_bootstrap/forms.scss +65 -0
  451. data/app/assets/stylesheets/effective_bootstrap/icons.scss +67 -0
  452. data/app/assets/stylesheets/effective_bootstrap/overrides.scss +14 -0
  453. data/app/assets/stylesheets/effective_checks/input.scss +8 -0
  454. data/app/assets/stylesheets/effective_date/input.scss +1 -0
  455. data/app/assets/stylesheets/effective_datetime/bootstrap-datetimepicker.scss +374 -0
  456. data/app/assets/stylesheets/effective_datetime/input.scss +2 -0
  457. data/app/assets/stylesheets/effective_datetime/overrides.scss +31 -0
  458. data/app/assets/stylesheets/effective_editor/input.scss +2 -0
  459. data/app/assets/stylesheets/effective_editor/overrides.scss +25 -0
  460. data/app/assets/stylesheets/effective_editor/quill.scss +948 -0
  461. data/app/assets/stylesheets/effective_file/input.scss +46 -0
  462. data/app/assets/stylesheets/effective_radio/input.scss +29 -0
  463. data/app/assets/stylesheets/effective_select/bootstrap-theme.css +564 -0
  464. data/app/assets/stylesheets/effective_select/input.scss +3 -0
  465. data/app/assets/stylesheets/effective_select/overrides.scss +115 -0
  466. data/app/assets/stylesheets/effective_select/select2.css +484 -0
  467. data/app/assets/stylesheets/effective_select_or_text/input.scss +6 -0
  468. data/app/assets/stylesheets/effective_time/input.scss +1 -0
  469. data/app/helpers/effective_bootstrap_helper.rb +371 -0
  470. data/app/helpers/effective_editor_helper.rb +29 -0
  471. data/app/helpers/effective_form_builder_helper.rb +74 -0
  472. data/app/helpers/effective_icons_helper.rb +51 -0
  473. data/app/models/effective/access_denied.rb +17 -0
  474. data/app/models/effective/form_builder.rb +199 -0
  475. data/app/models/effective/form_input.rb +434 -0
  476. data/app/models/effective/form_inputs/check_box.rb +70 -0
  477. data/app/models/effective/form_inputs/checks.rb +99 -0
  478. data/app/models/effective/form_inputs/ck_editor.rb +48 -0
  479. data/app/models/effective/form_inputs/clear.rb +22 -0
  480. data/app/models/effective/form_inputs/collection_input.rb +168 -0
  481. data/app/models/effective/form_inputs/date_field.rb +34 -0
  482. data/app/models/effective/form_inputs/datetime_field.rb +83 -0
  483. data/app/models/effective/form_inputs/delete.rb +26 -0
  484. data/app/models/effective/form_inputs/editor.rb +88 -0
  485. data/app/models/effective/form_inputs/email_cc_field.rb +19 -0
  486. data/app/models/effective/form_inputs/email_field.rb +20 -0
  487. data/app/models/effective/form_inputs/error_field.rb +47 -0
  488. data/app/models/effective/form_inputs/file_field.rb +135 -0
  489. data/app/models/effective/form_inputs/float_field.rb +20 -0
  490. data/app/models/effective/form_inputs/form_group.rb +15 -0
  491. data/app/models/effective/form_inputs/integer_field.rb +18 -0
  492. data/app/models/effective/form_inputs/number_field.rb +10 -0
  493. data/app/models/effective/form_inputs/number_text_field.rb +15 -0
  494. data/app/models/effective/form_inputs/password_field.rb +24 -0
  495. data/app/models/effective/form_inputs/percent_field.rb +38 -0
  496. data/app/models/effective/form_inputs/phone_field.rb +39 -0
  497. data/app/models/effective/form_inputs/price_field.rb +32 -0
  498. data/app/models/effective/form_inputs/radios.rb +175 -0
  499. data/app/models/effective/form_inputs/remote_link_to.rb +30 -0
  500. data/app/models/effective/form_inputs/reset.rb +22 -0
  501. data/app/models/effective/form_inputs/save.rb +22 -0
  502. data/app/models/effective/form_inputs/search_field.rb +15 -0
  503. data/app/models/effective/form_inputs/select.rb +176 -0
  504. data/app/models/effective/form_inputs/select_or_text.rb +70 -0
  505. data/app/models/effective/form_inputs/static_field.rb +41 -0
  506. data/app/models/effective/form_inputs/submit.rb +109 -0
  507. data/app/models/effective/form_inputs/text_area.rb +11 -0
  508. data/app/models/effective/form_inputs/text_field.rb +6 -0
  509. data/app/models/effective/form_inputs/time_field.rb +34 -0
  510. data/app/models/effective/form_inputs/time_zone_select.rb +15 -0
  511. data/app/models/effective/form_inputs/url_field.rb +20 -0
  512. data/app/models/effective/form_logic.rb +45 -0
  513. data/app/models/effective/form_logics/hide_if.rb +39 -0
  514. data/app/models/effective/form_logics/show_if.rb +39 -0
  515. data/app/models/effective/form_logics/show_if_any.rb +42 -0
  516. data/app/views/effective/style_guide/__fields.html.haml +43 -0
  517. data/app/views/effective/style_guide/__inline_fields.html.haml +4 -0
  518. data/app/views/effective/style_guide/_effective_form_with.html.haml +26 -0
  519. data/app/views/effective/style_guide/_feather_icons.html.haml +9 -0
  520. data/config/effective_bootstrap.rb +27 -0
  521. data/lib/effective_bootstrap.rb +33 -0
  522. data/lib/effective_bootstrap/engine.rb +15 -0
  523. data/lib/effective_bootstrap/version.rb +3 -0
  524. data/lib/generators/effective_bootstrap/install_generator.rb +14 -0
  525. metadata +650 -0
@@ -0,0 +1,51 @@
1
+ module EffectiveIconsHelper
2
+
3
+ # icon('check', class: 'big-4')
4
+ # icon('check', class: 'small-3')
5
+ def icon(svg, options = {})
6
+ svg = svg.to_s.chomp('.svg')
7
+
8
+ options.reverse_merge!(nocomment: true)
9
+ options[:class] = [options[:class], "eb-icon eb-icon-#{svg}"].compact.join(' ')
10
+
11
+ inline_svg_tag("icons/#{svg}.svg", options)
12
+ end
13
+
14
+ def icon_to(svg, url, options = {})
15
+ link_to(icon(svg), url, options)
16
+ end
17
+
18
+ def new_icon_to(path, options = {})
19
+ icon_to('plus', path, { title: 'New' }.merge(options))
20
+ end
21
+
22
+ def show_icon_to(path, options = {})
23
+ icon_to('eye', path, { title: 'Show' }.merge(options))
24
+ end
25
+
26
+ def edit_icon_to(path, options = {})
27
+ icon_to('edit', path, { title: 'Edit' }.merge(options))
28
+ end
29
+
30
+ def destroy_icon_to(path, options = {})
31
+ defaults = { title: 'Destroy', data: { method: :delete, confirm: 'Delete this item?' } }
32
+ icon_to('trash', path, defaults.merge(options))
33
+ end
34
+
35
+ def settings_icon_to(path, options = {})
36
+ icon_to('cog', path, { title: 'Settings' }.merge(options))
37
+ end
38
+
39
+ def ok_icon_to(path, options = {})
40
+ icon_to('ok', path, { title: 'OK' }.merge(options))
41
+ end
42
+
43
+ def approve_icon_to(path, options = {})
44
+ icon_to('ok', path, { title: 'Approve' }.merge(options))
45
+ end
46
+
47
+ def remove_icon_to(path, options = {})
48
+ icon_to('remove', path, { title: 'Remove' }.merge(options))
49
+ end
50
+
51
+ end
@@ -0,0 +1,17 @@
1
+ unless defined?(Effective::AccessDenied)
2
+ module Effective
3
+ class AccessDenied < StandardError
4
+ attr_reader :action, :subject
5
+
6
+ def initialize(message = nil, action = nil, subject = nil)
7
+ @message = message
8
+ @action = action
9
+ @subject = subject
10
+ end
11
+
12
+ def to_s
13
+ @message || I18n.t(:'unauthorized.default', :default => 'Access Denied')
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,199 @@
1
+ module Effective
2
+ class FormBuilder < ActionView::Helpers::FormBuilder
3
+
4
+ attr_accessor :template, :layout, :action, :readonly, :disabled, :remote
5
+
6
+ delegate :content_tag, to: :template
7
+
8
+ def initialize(object_name, object, template, options)
9
+ @template = template
10
+
11
+ @layout = (options.delete(:layout) || :vertical).to_sym
12
+ @action = options.delete(:action)
13
+ @readonly = options.delete(:readonly)
14
+ @disabled = options.delete(:disabled)
15
+ @remote = options[:remote]
16
+
17
+ super
18
+ end
19
+
20
+ alias_method :super_number_field, :number_field
21
+ alias_method :super_text_field, :text_field
22
+ alias_method :super_text_area, :text_area
23
+
24
+ def clear(name = 'Clear', options = {})
25
+ (options = name; name = 'Clear') if name.kind_of?(Hash)
26
+ Effective::FormInputs::Clear.new(name, options, builder: self).to_html
27
+ end
28
+
29
+ def check_box(name, options = {})
30
+ Effective::FormInputs::CheckBox.new(name, options, builder: self).to_html {
31
+ checked_value = options.fetch(:checked_value, '1')
32
+ unchecked_value = options.fetch(:unchecked_value, '0')
33
+ super(name, options.except(:checked_value, :unchecked_value), checked_value, unchecked_value)
34
+ }
35
+ end
36
+
37
+ def checks(name, choices = nil, *args)
38
+ options = args.extract_options!.merge!(collection: choices)
39
+ Effective::FormInputs::Checks.new(name, options, builder: self).to_html
40
+ end
41
+
42
+ def ck_editor(name, options = {}, &block)
43
+ Effective::FormInputs::CkEditor.new(name, options, builder: self).to_html(&block)
44
+ end
45
+
46
+ def date_field(name, options = {})
47
+ Effective::FormInputs::DateField.new(name, options, builder: self).to_html { super(name, options) }
48
+ end
49
+
50
+ def datetime_field(name, options = {})
51
+ Effective::FormInputs::DatetimeField.new(name, options, builder: self).to_html { super(name, options) }
52
+ end
53
+
54
+ def delete(name = 'Remove', url = nil, options = {}, &block)
55
+ options[:href] ||= url
56
+ Effective::FormInputs::Delete.new(name, options, builder: self).to_html(&block)
57
+ end
58
+
59
+ def editor(name, options = {}, &block)
60
+ Effective::FormInputs::Editor.new(name, options, builder: self).to_html(&block)
61
+ end
62
+
63
+ def email_field(name, options = {})
64
+ Effective::FormInputs::EmailField.new(name, options, builder: self).to_html { super(name, options) }
65
+ end
66
+
67
+ def email_cc_field(name, options = {})
68
+ Effective::FormInputs::EmailCcField.new(name, options, builder: self).to_html
69
+ end
70
+
71
+ def error(name = nil, options = {})
72
+ Effective::FormInputs::ErrorField.new(name, options, builder: self).to_html()
73
+ end
74
+ alias_method :errors, :error
75
+
76
+ def file_field(name, options = {})
77
+ Effective::FormInputs::FileField.new(name, options, builder: self).to_html { super(name, options) }
78
+ end
79
+
80
+ def float_field(name, options = {})
81
+ Effective::FormInputs::FloatField.new(name, options, builder: self).to_html
82
+ end
83
+
84
+ def form_group(name = nil, options = {}, &block)
85
+ Effective::FormInputs::FormGroup.new(name, options, builder: self).to_html(&block)
86
+ end
87
+
88
+ def integer_field(name, options = {})
89
+ Effective::FormInputs::IntegerField.new(name, options, builder: self).to_html
90
+ end
91
+
92
+ # This is gonna be a post?
93
+ def remote_link_to(name, url, options = {}, &block)
94
+ options[:href] ||= url
95
+ Effective::FormInputs::RemoteLinkTo.new(name, options, builder: self).to_html(&block)
96
+ end
97
+
98
+ def search_field(name, options = {})
99
+ Effective::FormInputs::SearchField.new(name, options, builder: self).to_html { super(name, options) }
100
+ end
101
+
102
+ def number_field(name, options = {})
103
+ Effective::FormInputs::NumberField.new(name, options, builder: self).to_html { super(name, options) }
104
+ end
105
+
106
+ def number_text_field(name, options = {})
107
+ Effective::FormInputs::NumberTextField.new(name, options, builder: self).to_html
108
+ end
109
+
110
+ def password_field(name, options = {})
111
+ Effective::FormInputs::PasswordField.new(name, options, builder: self).to_html { super(name, options) }
112
+ end
113
+
114
+ def percent_field(name, options = {})
115
+ Effective::FormInputs::PercentField.new(name, options, builder: self).to_html
116
+ end
117
+
118
+ def phone_field(name, options = {})
119
+ Effective::FormInputs::PhoneField.new(name, options, builder: self).to_html { super(name, options) }
120
+ end
121
+ alias_method :tel_field, :phone_field
122
+ alias_method :telephone_field, :phone_field
123
+
124
+ def price_field(name, options = {})
125
+ Effective::FormInputs::PriceField.new(name, options, builder: self).to_html { super(name, options) }
126
+ end
127
+
128
+ def save(name = 'Save', options = {})
129
+ (options = name; name = 'Save') if name.kind_of?(Hash)
130
+ Effective::FormInputs::Save.new(name, options, builder: self).to_html { super(name, options) }
131
+ end
132
+
133
+ def select(name, choices = nil, *args)
134
+ options = args.extract_options!.merge!(collection: choices)
135
+ Effective::FormInputs::Select.new(name, options, builder: self).to_html
136
+ end
137
+
138
+ def select_or_text(name, name_text, choices = nil, *args)
139
+ options = args.extract_options!.merge!(name_text: name_text, collection: choices)
140
+ Effective::FormInputs::SelectOrText.new(name, options, builder: self).to_html
141
+ end
142
+
143
+ def submit(name = 'Save', options = {}, &block)
144
+ (options = name; name = 'Save') if name.kind_of?(Hash)
145
+ Effective::FormInputs::Submit.new(name, options, builder: self).to_html(&block)
146
+ end
147
+
148
+ def static_field(name, options = {}, &block)
149
+ options = { value: options } if options.kind_of?(String)
150
+ Effective::FormInputs::StaticField.new(name, options, builder: self).to_html(&block)
151
+ end
152
+
153
+ def radios(name, choices = nil, *args)
154
+ options = args.extract_options!.merge!(collection: choices)
155
+ Effective::FormInputs::Radios.new(name, options, builder: self).to_html
156
+ end
157
+
158
+ def reset(name = 'Reset', options = {})
159
+ (options = name; name = 'Reset') if name.kind_of?(Hash)
160
+ Effective::FormInputs::Reset.new(name, options, builder: self).to_html
161
+ end
162
+
163
+ def text_area(name, options = {})
164
+ Effective::FormInputs::TextArea.new(name, options, builder: self).to_html { super(name, options) }
165
+ end
166
+
167
+ def text_field(name, options = {})
168
+ Effective::FormInputs::TextField.new(name, options, builder: self).to_html { super(name, options) }
169
+ end
170
+
171
+ def time_field(name, options = {})
172
+ Effective::FormInputs::TimeField.new(name, options, builder: self).to_html { super(name, options) }
173
+ end
174
+
175
+ def time_zone_select(name, options = {})
176
+ opts = options.merge(collection: Effective::FormInputs::TimeZoneSelect.time_zone_collection)
177
+ Effective::FormInputs::TimeZoneSelect.new(name, opts, builder: self).to_html
178
+ end
179
+
180
+ def url_field(name, options = {})
181
+ Effective::FormInputs::UrlField.new(name, options, builder: self).to_html { super(name, options) }
182
+ end
183
+
184
+ # Logics
185
+ def hide_if(*args, &block)
186
+ Effective::FormLogics::HideIf.new(*args, builder: self).to_html(&block)
187
+ end
188
+
189
+ def show_if(*args, &block)
190
+ Effective::FormLogics::ShowIf.new(*args, builder: self).to_html(&block)
191
+ end
192
+
193
+ def show_if_any(*args, &block)
194
+ Effective::FormLogics::ShowIfAny.new(*args, builder: self).to_html(&block)
195
+ end
196
+
197
+ end
198
+ end
199
+
@@ -0,0 +1,434 @@
1
+ module Effective
2
+ class FormInput
3
+ attr_accessor :name, :options
4
+
5
+ BLANK = ''.html_safe
6
+ EXCLUSIVE_CLASS_PREFIXES = [] # None
7
+ EXCLUSIVE_CLASS_SUFFIXES = ['-primary', '-secondary', '-success', '-danger', '-warning', '-info', '-light', '-dark']
8
+
9
+ delegate :object, to: :@builder
10
+ delegate :capture, :content_tag, :link_to, :icon, :asset_path, to: :@template
11
+
12
+ # So this takes in the options for an entire form group.
13
+ def initialize(name, options, builder:, html_options: nil)
14
+ @builder = builder
15
+ @template = builder.template
16
+
17
+ @name = name
18
+ @options = extract_options!(options, html_options: html_options)
19
+ apply_input_options!
20
+ end
21
+
22
+ def input_group_options
23
+ { input_group: { class: 'input-group' }, prepend: false, append: false }
24
+ end
25
+
26
+ def input_html_options
27
+ { class: 'form-control' }
28
+ end
29
+
30
+ def input_js_options
31
+ {}
32
+ end
33
+
34
+ def label_options
35
+ case layout
36
+ when :horizontal
37
+ { class: 'col-sm-2 col-form-label'}
38
+ when :inline
39
+ { class: 'sr-only' }
40
+ else
41
+ { }
42
+ end
43
+ end
44
+
45
+ def feedback_options
46
+ case layout
47
+ when :inline
48
+ false
49
+ else
50
+ { valid: { class: 'valid-feedback' }, invalid: { class: 'invalid-feedback' } }
51
+ end
52
+ end
53
+
54
+ def hint_options
55
+ case layout
56
+ when :inline
57
+ { tag: :small, class: 'text-muted', id: "#{tag_id}_hint" }
58
+ else
59
+ { tag: :small, class: 'form-text text-muted', id: "#{tag_id}_hint" }
60
+ end
61
+ end
62
+
63
+ def wrapper_options
64
+ case layout
65
+ when :horizontal
66
+ { class: 'form-group row' }
67
+ else
68
+ { class: 'form-group' }
69
+ end
70
+ end
71
+
72
+ def to_html(&block)
73
+ wrap(&block)
74
+ end
75
+
76
+ protected
77
+
78
+ def wrap(&block)
79
+ case layout
80
+ when :inline
81
+ build_content(&block)
82
+ when :horizontal
83
+ build_wrapper do
84
+ (build_label.presence || content_tag(:div, '', class: 'col-sm-2')) +
85
+ content_tag(:div, build_content(&block), class: 'col-sm-10')
86
+ end
87
+ else # Vertical
88
+ build_wrapper { build_content(&block) }
89
+ end.html_safe
90
+ end
91
+
92
+ def build_wrapper(&block)
93
+ content_tag(:div, yield, options[:wrapper])
94
+ end
95
+
96
+ def build_content(&block)
97
+ return build_input_group_content(&block) if input_group?
98
+
99
+ if layout == :horizontal
100
+ build_input(&block) + build_feedback + build_hint
101
+ else
102
+ build_label + build_input(&block) + build_feedback + build_hint
103
+ end
104
+ end
105
+
106
+ def build_input_group_content(&block)
107
+ if layout == :horizontal
108
+ build_input_group { build_input(&block) } + build_hint
109
+ else
110
+ build_label + build_input_group { build_input(&block) } + build_hint
111
+ end
112
+ end
113
+
114
+ def build_input_group(&block) # Includes input and feedback
115
+ content_tag(:div, '', options[:input_group][:input_group]) do # Twice here, kind of weird.
116
+ [
117
+ (content_tag(:div, options[:input_group][:prepend], class: 'input-group-prepend') if options[:input_group][:prepend]),
118
+ build_input(&block),
119
+ (content_tag(:div, options[:input_group][:append], class: 'input-group-append') if options[:input_group][:append]),
120
+ build_feedback
121
+ ].compact.join.html_safe
122
+ end
123
+ end
124
+
125
+ def build_label
126
+ return BLANK if options[:label] == false
127
+ return BLANK if name.kind_of?(NilClass)
128
+
129
+ text = options[:label].delete(:text)
130
+ name_to_s = name.to_s
131
+
132
+ text ||= (
133
+ if object && name_to_s.ends_with?('_id')
134
+ object.class.human_attribute_name(name_to_s.chomp('_id'))
135
+ elsif object && name_to_s.ends_with?('_ids')
136
+ object.class.human_attribute_name(name_to_s.chomp('_ids').pluralize)
137
+ elsif object
138
+ object.class.human_attribute_name(name)
139
+ else
140
+ BLANK
141
+ end
142
+ )
143
+
144
+ if options[:input][:id]
145
+ options[:label][:for] = options[:input][:id]
146
+ end
147
+
148
+ @builder.label(name, text.html_safe, options[:label])
149
+ end
150
+
151
+ def build_input(&block)
152
+ capture(&block)
153
+ end
154
+
155
+ def build_hint
156
+ return BLANK unless options[:hint] && options[:hint][:text]
157
+
158
+ tag = options[:hint].delete(:tag)
159
+ text = options[:hint].delete(:text)
160
+
161
+ content_tag(tag, text.html_safe, options[:hint])
162
+ end
163
+
164
+ def build_feedback
165
+ return BLANK if options[:feedback] == false
166
+
167
+ invalid = object.errors[name].to_sentence.presence if object.respond_to?(:errors)
168
+ invalid ||= options[:feedback][:invalid].delete(:text).presence
169
+ invalid ||= [("can't be blank" if options[:input][:required]), ('must be valid' if validated?(name))].compact.join(' and ').presence
170
+ invalid ||= "can't be blank or is invalid"
171
+
172
+ valid = options[:feedback][:valid].delete(:text) || 'Looks good!'
173
+
174
+ content_tag(:div, invalid.html_safe, options[:feedback][:invalid]) +
175
+ content_tag(:div, valid.html_safe, options[:feedback][:valid])
176
+ end
177
+
178
+ def has_error?(name = nil)
179
+ return false unless object.respond_to?(:errors)
180
+
181
+ if name
182
+ object.errors[name].present? || (parent_object && parent_object.errors[name].present?)
183
+ else
184
+ object.errors.present? || parent_object&.errors&.present?
185
+ end
186
+ end
187
+
188
+ def required?(name)
189
+ return false unless object && name
190
+
191
+ obj = (object.class == Class) ? object : object.class
192
+ return false unless obj.respond_to?(:validators_on)
193
+
194
+ if name.to_s.ends_with?('_id')
195
+ return required_presence?(obj, name) || required_presence?(obj, name[0...-3])
196
+ end
197
+
198
+ required_presence?(obj, name)
199
+ end
200
+
201
+ def required_presence?(obj, name)
202
+ obj.validators_on(name).any? do |v|
203
+ (
204
+ v.kind_of?(ActiveRecord::Validations::PresenceValidator) ||
205
+ v.kind_of?(ActiveModel::Validations::AcceptanceValidator)
206
+ ) && required_options?(v.options)
207
+ end
208
+ end
209
+
210
+ def required_options?(opts)
211
+ return true unless (opts.key?(:if) || opts.key?(:unless))
212
+
213
+ if opts[:if].respond_to?(:call)
214
+ return object.instance_exec(&opts[:if])
215
+ end
216
+
217
+ if opts[:if].kind_of?(Symbol)
218
+ return object.send(opts[:if])
219
+ end
220
+
221
+ if opts.key?(:if)
222
+ return opts[:if]
223
+ end
224
+
225
+ if opts[:unless].respond_to?(:call)
226
+ return !object.instance_exec(&opts[:unless])
227
+ end
228
+
229
+ if opts[:unless].kind_of?(Symbol)
230
+ return !object.send(opts[:unless])
231
+ end
232
+
233
+ if opts.key?(:unless)
234
+ return !opts[:unless]
235
+ end
236
+
237
+ false
238
+ end
239
+
240
+ def validated?(name)
241
+ return false unless object && name
242
+
243
+ obj = (object.class == Class) ? object : object.class
244
+ return false unless obj.respond_to?(:validators_on)
245
+
246
+ obj.validators_on(name).any? do |v|
247
+ !(v.kind_of?(ActiveRecord::Validations::PresenceValidator) || v.kind_of?(ActiveModel::Validations::AcceptanceValidator))
248
+ end
249
+ end
250
+
251
+ def input_group?
252
+ (options[:input_group][:append] || options[:input_group][:prepend]).present?
253
+ end
254
+
255
+ # Used for passwords and to not apply server side feedback
256
+ def reset_feedback?
257
+ return @reset_feedback unless @reset_feedback.nil?
258
+ @reset_feedback = options[:feedback].present? && (options[:feedback].delete(:reset) == true)
259
+ end
260
+
261
+ def value
262
+ @_value ||= (options.delete(:value) || options[:input]&.delete(:value))
263
+ @_value ||= (object.public_send(name) if object.respond_to?(name))
264
+ end
265
+
266
+ def unique_id(item = nil)
267
+ if item && item.respond_to?(value_method)
268
+ item_value = (item.send(value_method).to_s.parameterize.presence rescue nil)
269
+ end
270
+
271
+ [tag_id, item_value, object_id].compact.join('_')
272
+ end
273
+
274
+ private
275
+
276
+ # Here we split them into { wrapper: {}, label: {}, hint: {}, input: {} }
277
+ # And make sure to keep any additional options on the input: {}
278
+ def extract_options!(options, html_options: nil)
279
+ options.symbolize_keys!
280
+ html_options.symbolize_keys! if html_options
281
+
282
+ # effective_bootstrap specific options
283
+ layout = options.delete(:layout) # Symbol
284
+ wrapper = options.delete(:wrapper) # Hash
285
+ input_group = { append: options.delete(:append), prepend: options.delete(:prepend), input_group: options.delete(:input_group) }.compact
286
+
287
+ feedback = options.delete(:feedback) # Hash
288
+ label = options.delete(:label) # String or Hash
289
+ hint = options.delete(:hint) # String or Hash
290
+
291
+ input_html = options.delete(:input_html) || {} # Hash
292
+ input_js = options.delete(:input_js) || {} # Hash
293
+
294
+ # Every other option goes to input
295
+ @options = input = (html_options || options)
296
+
297
+ # Merge all the default objects, and intialize everything
298
+ wrapper = merge_defaults!(wrapper, wrapper_options)
299
+ input_group = merge_defaults!(input_group, input_group_options)
300
+ feedback = merge_defaults!(feedback, feedback_options)
301
+
302
+ label = merge_defaults!(label, label_options)
303
+ hint = merge_defaults!(hint, hint_options)
304
+
305
+ merge_defaults!(input.merge!(input_html), input_html_options)
306
+ merge_defaults!(input_js, input_js_options)
307
+
308
+ if input_js.present?
309
+ merge_defaults!(input_js, input_js_options_method_name)
310
+ input['data-input-js-options'] = JSON.generate(input_js)
311
+ end
312
+
313
+ { layout: layout, wrapper: wrapper, input_group: input_group, label: label, hint: hint, input: input, feedback: feedback }
314
+ end
315
+
316
+ def apply_input_options!
317
+ # Server side validation
318
+ if has_error?
319
+ if has_error?(name)
320
+ options[:input][:class] = [options[:input][:class], 'is-invalid'].compact.join(' ')
321
+ elsif reset_feedback?
322
+ # Nothing
323
+ else
324
+ options[:input][:class] = [options[:input][:class], 'is-valid'].compact.join(' ')
325
+ end
326
+ end
327
+
328
+ if required?(name) && (options[:input].delete(:required) != false)
329
+ options[:input][:required] = 'required'
330
+ end
331
+
332
+ if readonly?
333
+ options[:input][:readonly] = 'readonly'
334
+
335
+ unless options[:input][:class].to_s.include?('form-control-plaintext')
336
+ options[:input][:class] = options[:input][:class].to_s.sub('form-control', 'form-control-plaintext')
337
+ end
338
+ end
339
+
340
+ if disabled?
341
+ options[:input][:disabled] = 'disabled'
342
+ end
343
+
344
+ if options[:hint] && options[:hint][:text] && options[:hint][:id]
345
+ options[:input].reverse_merge!('aria-describedby': options[:hint][:id])
346
+ end
347
+ end
348
+
349
+ def merge_defaults!(obj, defaults)
350
+ defaults = {} if defaults.nil?
351
+
352
+ case obj
353
+ when false
354
+ false
355
+ when nil, true
356
+ defaults
357
+ when String
358
+ defaults.merge(text: obj)
359
+ when Hash
360
+ html_classes = (obj[:class].to_s.split(' ') + defaults[:class].to_s.split(' ')).uniq
361
+
362
+ # Try to smart merge bootstrap classes
363
+ if (exclusive = html_classes.select { |c| c.include?('-') }).length > 1
364
+ EXCLUSIVE_CLASS_PREFIXES.each do |prefix|
365
+ prefixed = exclusive.select { |c| c.start_with?(prefix) }
366
+ prefixed[1..-1].each { |c| html_classes.delete(c) } if prefixed.length > 1
367
+ end
368
+
369
+ suffixed = exclusive.select { |c| EXCLUSIVE_CLASS_SUFFIXES.any? { |suffix| c.end_with?(suffix) } }
370
+ suffixed[1..-1].each { |c| html_classes.delete(c) } if suffixed.length > 1
371
+ end
372
+
373
+ obj[:class] = html_classes.join(' ') if html_classes.present?
374
+ obj.reverse_merge!(defaults)
375
+ obj
376
+ else
377
+ defaults.merge(text: obj.to_s)
378
+ end
379
+ end
380
+
381
+ def layout
382
+ options[:layout] || @builder.layout
383
+ end
384
+
385
+ def form_readonly?
386
+ @builder.readonly
387
+ end
388
+
389
+ def readonly?
390
+ options.dig(:input, :readonly).present? || form_readonly?
391
+ end
392
+
393
+ def disabled?
394
+ options.dig(:input, :disabled).present? || form_disabled?
395
+ end
396
+
397
+ def form_disabled?
398
+ @builder.disabled
399
+ end
400
+
401
+ def form_remote?
402
+ @builder.remote
403
+ end
404
+
405
+ def parent_object
406
+ @builder.options[:parent_builder]&.object
407
+ end
408
+
409
+ # https://github.com/rails/rails/blob/master/actionview/lib/action_view/helpers/tags/base.rb#L120
410
+ def tag_id(index = nil)
411
+ case
412
+ when @builder.object_name.empty?
413
+ sanitized_method_name.dup
414
+ when index
415
+ "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
416
+ else
417
+ "#{sanitized_object_name}_#{sanitized_method_name}"
418
+ end.downcase.parameterize
419
+ end
420
+
421
+ def sanitized_object_name
422
+ @builder.object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
423
+ end
424
+
425
+ def sanitized_method_name
426
+ name.to_s.sub(/\?$/, "")
427
+ end
428
+
429
+ def input_js_options_method_name
430
+ { method_name: "effective_#{self.class.name.split('::').last.underscore.chomp('_field')}" }
431
+ end
432
+
433
+ end
434
+ end