motorefi-lit 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (347) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +170 -0
  4. data/Rakefile +38 -0
  5. data/app/assets/images/lit/famfamfam_flags/ad.png +0 -0
  6. data/app/assets/images/lit/famfamfam_flags/ae.png +0 -0
  7. data/app/assets/images/lit/famfamfam_flags/af.png +0 -0
  8. data/app/assets/images/lit/famfamfam_flags/ag.png +0 -0
  9. data/app/assets/images/lit/famfamfam_flags/ai.png +0 -0
  10. data/app/assets/images/lit/famfamfam_flags/al.png +0 -0
  11. data/app/assets/images/lit/famfamfam_flags/am.png +0 -0
  12. data/app/assets/images/lit/famfamfam_flags/an.png +0 -0
  13. data/app/assets/images/lit/famfamfam_flags/ao.png +0 -0
  14. data/app/assets/images/lit/famfamfam_flags/ar.png +0 -0
  15. data/app/assets/images/lit/famfamfam_flags/as.png +0 -0
  16. data/app/assets/images/lit/famfamfam_flags/at.png +0 -0
  17. data/app/assets/images/lit/famfamfam_flags/au.png +0 -0
  18. data/app/assets/images/lit/famfamfam_flags/aw.png +0 -0
  19. data/app/assets/images/lit/famfamfam_flags/ax.png +0 -0
  20. data/app/assets/images/lit/famfamfam_flags/az.png +0 -0
  21. data/app/assets/images/lit/famfamfam_flags/ba.png +0 -0
  22. data/app/assets/images/lit/famfamfam_flags/bb.png +0 -0
  23. data/app/assets/images/lit/famfamfam_flags/bd.png +0 -0
  24. data/app/assets/images/lit/famfamfam_flags/be.png +0 -0
  25. data/app/assets/images/lit/famfamfam_flags/bf.png +0 -0
  26. data/app/assets/images/lit/famfamfam_flags/bg.png +0 -0
  27. data/app/assets/images/lit/famfamfam_flags/bh.png +0 -0
  28. data/app/assets/images/lit/famfamfam_flags/bi.png +0 -0
  29. data/app/assets/images/lit/famfamfam_flags/bj.png +0 -0
  30. data/app/assets/images/lit/famfamfam_flags/bm.png +0 -0
  31. data/app/assets/images/lit/famfamfam_flags/bn.png +0 -0
  32. data/app/assets/images/lit/famfamfam_flags/bo.png +0 -0
  33. data/app/assets/images/lit/famfamfam_flags/br.png +0 -0
  34. data/app/assets/images/lit/famfamfam_flags/bs.png +0 -0
  35. data/app/assets/images/lit/famfamfam_flags/bt.png +0 -0
  36. data/app/assets/images/lit/famfamfam_flags/bv.png +0 -0
  37. data/app/assets/images/lit/famfamfam_flags/bw.png +0 -0
  38. data/app/assets/images/lit/famfamfam_flags/by.png +0 -0
  39. data/app/assets/images/lit/famfamfam_flags/bz.png +0 -0
  40. data/app/assets/images/lit/famfamfam_flags/ca.png +0 -0
  41. data/app/assets/images/lit/famfamfam_flags/catalonia.png +0 -0
  42. data/app/assets/images/lit/famfamfam_flags/cc.png +0 -0
  43. data/app/assets/images/lit/famfamfam_flags/cd.png +0 -0
  44. data/app/assets/images/lit/famfamfam_flags/cf.png +0 -0
  45. data/app/assets/images/lit/famfamfam_flags/cg.png +0 -0
  46. data/app/assets/images/lit/famfamfam_flags/ch.png +0 -0
  47. data/app/assets/images/lit/famfamfam_flags/ci.png +0 -0
  48. data/app/assets/images/lit/famfamfam_flags/ck.png +0 -0
  49. data/app/assets/images/lit/famfamfam_flags/cl.png +0 -0
  50. data/app/assets/images/lit/famfamfam_flags/cm.png +0 -0
  51. data/app/assets/images/lit/famfamfam_flags/cn.png +0 -0
  52. data/app/assets/images/lit/famfamfam_flags/co.png +0 -0
  53. data/app/assets/images/lit/famfamfam_flags/cr.png +0 -0
  54. data/app/assets/images/lit/famfamfam_flags/cs.png +0 -0
  55. data/app/assets/images/lit/famfamfam_flags/cu.png +0 -0
  56. data/app/assets/images/lit/famfamfam_flags/cv.png +0 -0
  57. data/app/assets/images/lit/famfamfam_flags/cx.png +0 -0
  58. data/app/assets/images/lit/famfamfam_flags/cy.png +0 -0
  59. data/app/assets/images/lit/famfamfam_flags/cz.png +0 -0
  60. data/app/assets/images/lit/famfamfam_flags/da.png +0 -0
  61. data/app/assets/images/lit/famfamfam_flags/de.png +0 -0
  62. data/app/assets/images/lit/famfamfam_flags/dj.png +0 -0
  63. data/app/assets/images/lit/famfamfam_flags/dk.png +0 -0
  64. data/app/assets/images/lit/famfamfam_flags/dm.png +0 -0
  65. data/app/assets/images/lit/famfamfam_flags/do.png +0 -0
  66. data/app/assets/images/lit/famfamfam_flags/dz.png +0 -0
  67. data/app/assets/images/lit/famfamfam_flags/ec.png +0 -0
  68. data/app/assets/images/lit/famfamfam_flags/ee.png +0 -0
  69. data/app/assets/images/lit/famfamfam_flags/eg.png +0 -0
  70. data/app/assets/images/lit/famfamfam_flags/eh.png +0 -0
  71. data/app/assets/images/lit/famfamfam_flags/en.png +0 -0
  72. data/app/assets/images/lit/famfamfam_flags/england.png +0 -0
  73. data/app/assets/images/lit/famfamfam_flags/er.png +0 -0
  74. data/app/assets/images/lit/famfamfam_flags/es.png +0 -0
  75. data/app/assets/images/lit/famfamfam_flags/et.png +0 -0
  76. data/app/assets/images/lit/famfamfam_flags/europeanunion.png +0 -0
  77. data/app/assets/images/lit/famfamfam_flags/fam.png +0 -0
  78. data/app/assets/images/lit/famfamfam_flags/fi.png +0 -0
  79. data/app/assets/images/lit/famfamfam_flags/fj.png +0 -0
  80. data/app/assets/images/lit/famfamfam_flags/fk.png +0 -0
  81. data/app/assets/images/lit/famfamfam_flags/fm.png +0 -0
  82. data/app/assets/images/lit/famfamfam_flags/fo.png +0 -0
  83. data/app/assets/images/lit/famfamfam_flags/fr.png +0 -0
  84. data/app/assets/images/lit/famfamfam_flags/ga.png +0 -0
  85. data/app/assets/images/lit/famfamfam_flags/gb.png +0 -0
  86. data/app/assets/images/lit/famfamfam_flags/gd.png +0 -0
  87. data/app/assets/images/lit/famfamfam_flags/ge.png +0 -0
  88. data/app/assets/images/lit/famfamfam_flags/gf.png +0 -0
  89. data/app/assets/images/lit/famfamfam_flags/gh.png +0 -0
  90. data/app/assets/images/lit/famfamfam_flags/gi.png +0 -0
  91. data/app/assets/images/lit/famfamfam_flags/gl.png +0 -0
  92. data/app/assets/images/lit/famfamfam_flags/gm.png +0 -0
  93. data/app/assets/images/lit/famfamfam_flags/gn.png +0 -0
  94. data/app/assets/images/lit/famfamfam_flags/gp.png +0 -0
  95. data/app/assets/images/lit/famfamfam_flags/gq.png +0 -0
  96. data/app/assets/images/lit/famfamfam_flags/gr.png +0 -0
  97. data/app/assets/images/lit/famfamfam_flags/gs.png +0 -0
  98. data/app/assets/images/lit/famfamfam_flags/gt.png +0 -0
  99. data/app/assets/images/lit/famfamfam_flags/gu.png +0 -0
  100. data/app/assets/images/lit/famfamfam_flags/gw.png +0 -0
  101. data/app/assets/images/lit/famfamfam_flags/gy.png +0 -0
  102. data/app/assets/images/lit/famfamfam_flags/hk.png +0 -0
  103. data/app/assets/images/lit/famfamfam_flags/hm.png +0 -0
  104. data/app/assets/images/lit/famfamfam_flags/hn.png +0 -0
  105. data/app/assets/images/lit/famfamfam_flags/hr.png +0 -0
  106. data/app/assets/images/lit/famfamfam_flags/ht.png +0 -0
  107. data/app/assets/images/lit/famfamfam_flags/hu.png +0 -0
  108. data/app/assets/images/lit/famfamfam_flags/id.png +0 -0
  109. data/app/assets/images/lit/famfamfam_flags/ie.png +0 -0
  110. data/app/assets/images/lit/famfamfam_flags/il.png +0 -0
  111. data/app/assets/images/lit/famfamfam_flags/in.png +0 -0
  112. data/app/assets/images/lit/famfamfam_flags/io.png +0 -0
  113. data/app/assets/images/lit/famfamfam_flags/iq.png +0 -0
  114. data/app/assets/images/lit/famfamfam_flags/ir.png +0 -0
  115. data/app/assets/images/lit/famfamfam_flags/is.png +0 -0
  116. data/app/assets/images/lit/famfamfam_flags/it.png +0 -0
  117. data/app/assets/images/lit/famfamfam_flags/ja.png +0 -0
  118. data/app/assets/images/lit/famfamfam_flags/jm.png +0 -0
  119. data/app/assets/images/lit/famfamfam_flags/jo.png +0 -0
  120. data/app/assets/images/lit/famfamfam_flags/jp.png +0 -0
  121. data/app/assets/images/lit/famfamfam_flags/ke.png +0 -0
  122. data/app/assets/images/lit/famfamfam_flags/kg.png +0 -0
  123. data/app/assets/images/lit/famfamfam_flags/kh.png +0 -0
  124. data/app/assets/images/lit/famfamfam_flags/ki.png +0 -0
  125. data/app/assets/images/lit/famfamfam_flags/km.png +0 -0
  126. data/app/assets/images/lit/famfamfam_flags/kn.png +0 -0
  127. data/app/assets/images/lit/famfamfam_flags/kp.png +0 -0
  128. data/app/assets/images/lit/famfamfam_flags/kr.png +0 -0
  129. data/app/assets/images/lit/famfamfam_flags/kw.png +0 -0
  130. data/app/assets/images/lit/famfamfam_flags/ky.png +0 -0
  131. data/app/assets/images/lit/famfamfam_flags/kz.png +0 -0
  132. data/app/assets/images/lit/famfamfam_flags/la.png +0 -0
  133. data/app/assets/images/lit/famfamfam_flags/lb.png +0 -0
  134. data/app/assets/images/lit/famfamfam_flags/lc.png +0 -0
  135. data/app/assets/images/lit/famfamfam_flags/li.png +0 -0
  136. data/app/assets/images/lit/famfamfam_flags/lk.png +0 -0
  137. data/app/assets/images/lit/famfamfam_flags/lr.png +0 -0
  138. data/app/assets/images/lit/famfamfam_flags/ls.png +0 -0
  139. data/app/assets/images/lit/famfamfam_flags/lt.png +0 -0
  140. data/app/assets/images/lit/famfamfam_flags/lu.png +0 -0
  141. data/app/assets/images/lit/famfamfam_flags/lv.png +0 -0
  142. data/app/assets/images/lit/famfamfam_flags/ly.png +0 -0
  143. data/app/assets/images/lit/famfamfam_flags/ma.png +0 -0
  144. data/app/assets/images/lit/famfamfam_flags/mc.png +0 -0
  145. data/app/assets/images/lit/famfamfam_flags/md.png +0 -0
  146. data/app/assets/images/lit/famfamfam_flags/me.png +0 -0
  147. data/app/assets/images/lit/famfamfam_flags/mg.png +0 -0
  148. data/app/assets/images/lit/famfamfam_flags/mh.png +0 -0
  149. data/app/assets/images/lit/famfamfam_flags/mk.png +0 -0
  150. data/app/assets/images/lit/famfamfam_flags/ml.png +0 -0
  151. data/app/assets/images/lit/famfamfam_flags/mm.png +0 -0
  152. data/app/assets/images/lit/famfamfam_flags/mn.png +0 -0
  153. data/app/assets/images/lit/famfamfam_flags/mo.png +0 -0
  154. data/app/assets/images/lit/famfamfam_flags/mp.png +0 -0
  155. data/app/assets/images/lit/famfamfam_flags/mq.png +0 -0
  156. data/app/assets/images/lit/famfamfam_flags/mr.png +0 -0
  157. data/app/assets/images/lit/famfamfam_flags/ms.png +0 -0
  158. data/app/assets/images/lit/famfamfam_flags/mt.png +0 -0
  159. data/app/assets/images/lit/famfamfam_flags/mu.png +0 -0
  160. data/app/assets/images/lit/famfamfam_flags/mv.png +0 -0
  161. data/app/assets/images/lit/famfamfam_flags/mw.png +0 -0
  162. data/app/assets/images/lit/famfamfam_flags/mx.png +0 -0
  163. data/app/assets/images/lit/famfamfam_flags/my.png +0 -0
  164. data/app/assets/images/lit/famfamfam_flags/mz.png +0 -0
  165. data/app/assets/images/lit/famfamfam_flags/na.png +0 -0
  166. data/app/assets/images/lit/famfamfam_flags/nc.png +0 -0
  167. data/app/assets/images/lit/famfamfam_flags/ne.png +0 -0
  168. data/app/assets/images/lit/famfamfam_flags/nf.png +0 -0
  169. data/app/assets/images/lit/famfamfam_flags/ng.png +0 -0
  170. data/app/assets/images/lit/famfamfam_flags/ni.png +0 -0
  171. data/app/assets/images/lit/famfamfam_flags/nl.png +0 -0
  172. data/app/assets/images/lit/famfamfam_flags/no.png +0 -0
  173. data/app/assets/images/lit/famfamfam_flags/np.png +0 -0
  174. data/app/assets/images/lit/famfamfam_flags/nr.png +0 -0
  175. data/app/assets/images/lit/famfamfam_flags/nu.png +0 -0
  176. data/app/assets/images/lit/famfamfam_flags/nz.png +0 -0
  177. data/app/assets/images/lit/famfamfam_flags/om.png +0 -0
  178. data/app/assets/images/lit/famfamfam_flags/pa.png +0 -0
  179. data/app/assets/images/lit/famfamfam_flags/pe.png +0 -0
  180. data/app/assets/images/lit/famfamfam_flags/pf.png +0 -0
  181. data/app/assets/images/lit/famfamfam_flags/pg.png +0 -0
  182. data/app/assets/images/lit/famfamfam_flags/ph.png +0 -0
  183. data/app/assets/images/lit/famfamfam_flags/pk.png +0 -0
  184. data/app/assets/images/lit/famfamfam_flags/pl.png +0 -0
  185. data/app/assets/images/lit/famfamfam_flags/pm.png +0 -0
  186. data/app/assets/images/lit/famfamfam_flags/pn.png +0 -0
  187. data/app/assets/images/lit/famfamfam_flags/pr.png +0 -0
  188. data/app/assets/images/lit/famfamfam_flags/ps.png +0 -0
  189. data/app/assets/images/lit/famfamfam_flags/pt.png +0 -0
  190. data/app/assets/images/lit/famfamfam_flags/pw.png +0 -0
  191. data/app/assets/images/lit/famfamfam_flags/py.png +0 -0
  192. data/app/assets/images/lit/famfamfam_flags/qa.png +0 -0
  193. data/app/assets/images/lit/famfamfam_flags/re.png +0 -0
  194. data/app/assets/images/lit/famfamfam_flags/ro.png +0 -0
  195. data/app/assets/images/lit/famfamfam_flags/rs.png +0 -0
  196. data/app/assets/images/lit/famfamfam_flags/ru.png +0 -0
  197. data/app/assets/images/lit/famfamfam_flags/rw.png +0 -0
  198. data/app/assets/images/lit/famfamfam_flags/sa.png +0 -0
  199. data/app/assets/images/lit/famfamfam_flags/sb.png +0 -0
  200. data/app/assets/images/lit/famfamfam_flags/sc.png +0 -0
  201. data/app/assets/images/lit/famfamfam_flags/scotland.png +0 -0
  202. data/app/assets/images/lit/famfamfam_flags/sd.png +0 -0
  203. data/app/assets/images/lit/famfamfam_flags/se.png +0 -0
  204. data/app/assets/images/lit/famfamfam_flags/sg.png +0 -0
  205. data/app/assets/images/lit/famfamfam_flags/sh.png +0 -0
  206. data/app/assets/images/lit/famfamfam_flags/si.png +0 -0
  207. data/app/assets/images/lit/famfamfam_flags/sj.png +0 -0
  208. data/app/assets/images/lit/famfamfam_flags/sk.png +0 -0
  209. data/app/assets/images/lit/famfamfam_flags/sl.png +0 -0
  210. data/app/assets/images/lit/famfamfam_flags/sm.png +0 -0
  211. data/app/assets/images/lit/famfamfam_flags/sn.png +0 -0
  212. data/app/assets/images/lit/famfamfam_flags/so.png +0 -0
  213. data/app/assets/images/lit/famfamfam_flags/sr.png +0 -0
  214. data/app/assets/images/lit/famfamfam_flags/st.png +0 -0
  215. data/app/assets/images/lit/famfamfam_flags/sv.png +0 -0
  216. data/app/assets/images/lit/famfamfam_flags/sy.png +0 -0
  217. data/app/assets/images/lit/famfamfam_flags/sz.png +0 -0
  218. data/app/assets/images/lit/famfamfam_flags/tc.png +0 -0
  219. data/app/assets/images/lit/famfamfam_flags/td.png +0 -0
  220. data/app/assets/images/lit/famfamfam_flags/tf.png +0 -0
  221. data/app/assets/images/lit/famfamfam_flags/tg.png +0 -0
  222. data/app/assets/images/lit/famfamfam_flags/th.png +0 -0
  223. data/app/assets/images/lit/famfamfam_flags/tj.png +0 -0
  224. data/app/assets/images/lit/famfamfam_flags/tk.png +0 -0
  225. data/app/assets/images/lit/famfamfam_flags/tl.png +0 -0
  226. data/app/assets/images/lit/famfamfam_flags/tm.png +0 -0
  227. data/app/assets/images/lit/famfamfam_flags/tn.png +0 -0
  228. data/app/assets/images/lit/famfamfam_flags/to.png +0 -0
  229. data/app/assets/images/lit/famfamfam_flags/tr.png +0 -0
  230. data/app/assets/images/lit/famfamfam_flags/tt.png +0 -0
  231. data/app/assets/images/lit/famfamfam_flags/tv.png +0 -0
  232. data/app/assets/images/lit/famfamfam_flags/tw.png +0 -0
  233. data/app/assets/images/lit/famfamfam_flags/tz.png +0 -0
  234. data/app/assets/images/lit/famfamfam_flags/ua.png +0 -0
  235. data/app/assets/images/lit/famfamfam_flags/ug.png +0 -0
  236. data/app/assets/images/lit/famfamfam_flags/um.png +0 -0
  237. data/app/assets/images/lit/famfamfam_flags/us.png +0 -0
  238. data/app/assets/images/lit/famfamfam_flags/uy.png +0 -0
  239. data/app/assets/images/lit/famfamfam_flags/uz.png +0 -0
  240. data/app/assets/images/lit/famfamfam_flags/va.png +0 -0
  241. data/app/assets/images/lit/famfamfam_flags/vc.png +0 -0
  242. data/app/assets/images/lit/famfamfam_flags/ve.png +0 -0
  243. data/app/assets/images/lit/famfamfam_flags/vg.png +0 -0
  244. data/app/assets/images/lit/famfamfam_flags/vi.png +0 -0
  245. data/app/assets/images/lit/famfamfam_flags/vn.png +0 -0
  246. data/app/assets/images/lit/famfamfam_flags/vu.png +0 -0
  247. data/app/assets/images/lit/famfamfam_flags/wales.png +0 -0
  248. data/app/assets/images/lit/famfamfam_flags/wf.png +0 -0
  249. data/app/assets/images/lit/famfamfam_flags/ws.png +0 -0
  250. data/app/assets/images/lit/famfamfam_flags/ye.png +0 -0
  251. data/app/assets/images/lit/famfamfam_flags/yt.png +0 -0
  252. data/app/assets/images/lit/famfamfam_flags/za.png +0 -0
  253. data/app/assets/images/lit/famfamfam_flags/zm.png +0 -0
  254. data/app/assets/images/lit/famfamfam_flags/zw.png +0 -0
  255. data/app/assets/images/lit/jquery-te.png +0 -0
  256. data/app/assets/javascripts/lit/application.js +19 -0
  257. data/app/assets/javascripts/lit/backend/bootstrap.js.coffee +4 -0
  258. data/app/assets/javascripts/lit/backend/dashboard.js +2 -0
  259. data/app/assets/javascripts/lit/backend/jquery-te-1.4.0.min.js +9 -0
  260. data/app/assets/javascripts/lit/backend/localizations.js.coffee +26 -0
  261. data/app/assets/javascripts/lit/backend/sources.js.coffee +24 -0
  262. data/app/assets/javascripts/lit/lit_frontend.js +103 -0
  263. data/app/assets/javascripts/lit/mousetrap.js +1044 -0
  264. data/app/assets/stylesheets/lit/application.css +59 -0
  265. data/app/assets/stylesheets/lit/backend/jquery-te-1.4.0.css.scss +457 -0
  266. data/app/assets/stylesheets/lit/lit_frontend.css +86 -0
  267. data/app/controllers/lit/api/v1/base_controller.rb +19 -0
  268. data/app/controllers/lit/api/v1/locales_controller.rb +14 -0
  269. data/app/controllers/lit/api/v1/localization_keys_controller.rb +15 -0
  270. data/app/controllers/lit/api/v1/localizations_controller.rb +18 -0
  271. data/app/controllers/lit/application_controller.rb +49 -0
  272. data/app/controllers/lit/concerns/request_info_store.rb +16 -0
  273. data/app/controllers/lit/concerns/request_keys_store.rb +16 -0
  274. data/app/controllers/lit/dashboard_controller.rb +9 -0
  275. data/app/controllers/lit/incomming_localizations_controller.rb +49 -0
  276. data/app/controllers/lit/locales_controller.rb +31 -0
  277. data/app/controllers/lit/localization_keys_controller.rb +117 -0
  278. data/app/controllers/lit/localizations_controller.rb +60 -0
  279. data/app/controllers/lit/sources_controller.rb +77 -0
  280. data/app/helpers/lit/application_helper.rb +4 -0
  281. data/app/helpers/lit/dashboard_helper.rb +4 -0
  282. data/app/helpers/lit/frontend_helper.rb +71 -0
  283. data/app/helpers/lit/localization_keys_helper.rb +5 -0
  284. data/app/helpers/lit/localizations_helper.rb +15 -0
  285. data/app/helpers/lit/sources_helper.rb +7 -0
  286. data/app/jobs/lit/synchronize_source_job.rb +11 -0
  287. data/app/models/lit/incomming_localization.rb +77 -0
  288. data/app/models/lit/locale.rb +48 -0
  289. data/app/models/lit/localization.rb +103 -0
  290. data/app/models/lit/localization_key.rb +107 -0
  291. data/app/models/lit/localization_version.rb +12 -0
  292. data/app/models/lit/source.rb +111 -0
  293. data/app/views/kaminari/lit/_first_page.html.erb +5 -0
  294. data/app/views/kaminari/lit/_gap.html.erb +3 -0
  295. data/app/views/kaminari/lit/_last_page.html.erb +6 -0
  296. data/app/views/kaminari/lit/_next_page.html.erb +5 -0
  297. data/app/views/kaminari/lit/_page.html.erb +3 -0
  298. data/app/views/kaminari/lit/_paginator.html.erb +17 -0
  299. data/app/views/kaminari/lit/_prev_page.html.erb +5 -0
  300. data/app/views/layouts/lit/_messages.html.erb +6 -0
  301. data/app/views/layouts/lit/_navigation.html.erb +23 -0
  302. data/app/views/layouts/lit/application.html.erb +46 -0
  303. data/app/views/lit/dashboard/index.html.erb +5 -0
  304. data/app/views/lit/incomming_localizations/accept.js.erb +2 -0
  305. data/app/views/lit/incomming_localizations/destroy.js.erb +1 -0
  306. data/app/views/lit/incomming_localizations/index.html.erb +52 -0
  307. data/app/views/lit/locales/_hide_link.html.erb +1 -0
  308. data/app/views/lit/locales/hide.js.erb +1 -0
  309. data/app/views/lit/locales/index.html.erb +20 -0
  310. data/app/views/lit/localization_keys/_localization_row.html.erb +14 -0
  311. data/app/views/lit/localization_keys/destroy.js.erb +1 -0
  312. data/app/views/lit/localization_keys/index.html.erb +127 -0
  313. data/app/views/lit/localization_keys/star.js.erb +1 -0
  314. data/app/views/lit/localizations/_form.html.erb +23 -0
  315. data/app/views/lit/localizations/_previous_versions_rows.html.erb +15 -0
  316. data/app/views/lit/localizations/edit.js.erb +7 -0
  317. data/app/views/lit/localizations/previous_versions.js.erb +3 -0
  318. data/app/views/lit/localizations/update.js.erb +4 -0
  319. data/app/views/lit/sources/_form.html.erb +35 -0
  320. data/app/views/lit/sources/edit.html.erb +6 -0
  321. data/app/views/lit/sources/index.html.erb +42 -0
  322. data/app/views/lit/sources/new.html.erb +5 -0
  323. data/app/views/lit/sources/show.html.erb +19 -0
  324. data/config/routes.rb +49 -0
  325. data/db/migrate/20180101010101_lit_create_lit_locales.rb +16 -0
  326. data/db/migrate/20180101010102_lit_create_lit_localization_keys.rb +18 -0
  327. data/db/migrate/20180101010103_lit_create_lit_localizations.rb +24 -0
  328. data/db/migrate/20180101010104_lit_add_is_completed_and_is_starred_to_localization_keys.rb +17 -0
  329. data/db/migrate/20180101010105_lit_add_is_hidden_to_locales.rb +12 -0
  330. data/db/migrate/20180101010106_lit_create_lit_localization_versions.rb +20 -0
  331. data/db/migrate/20180101010107_lit_create_lit_sources.rb +19 -0
  332. data/db/migrate/20180101010108_lit_create_lit_incomming_localizations.rb +34 -0
  333. data/db/migrate/20180101010109_lit_add_sync_complete_to_lit_sources.rb +12 -0
  334. data/lib/generators/lit/install/templates/initializer.rb +52 -0
  335. data/lib/generators/lit/install_generator.rb +66 -0
  336. data/lib/lit.rb +65 -0
  337. data/lib/lit/adapters/hash_storage.rb +12 -0
  338. data/lib/lit/adapters/redis_storage.rb +99 -0
  339. data/lib/lit/cache.rb +356 -0
  340. data/lib/lit/engine.rb +28 -0
  341. data/lib/lit/i18n_backend.rb +192 -0
  342. data/lib/lit/loader.rb +16 -0
  343. data/lib/lit/rails.rb +17 -0
  344. data/lib/lit/railtie.rb +12 -0
  345. data/lib/lit/version.rb +3 -0
  346. data/lib/tasks/lit_tasks.rake +29 -0
  347. metadata +478 -0
@@ -0,0 +1,1044 @@
1
+ /*global define:false */
2
+ /**
3
+ * Copyright 2016 Craig Campbell
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ * Mousetrap is a simple keyboard shortcut library for Javascript with
18
+ * no external dependencies
19
+ *
20
+ * @version 1.6.0
21
+ * @url craig.is/killing/mice
22
+ */
23
+ (function(window, document, undefined) {
24
+
25
+ // Check if mousetrap is used inside browser, if not, return
26
+ if (!window) {
27
+ return;
28
+ }
29
+
30
+ /**
31
+ * mapping of special keycodes to their corresponding keys
32
+ *
33
+ * everything in this dictionary cannot use keypress events
34
+ * so it has to be here to map to the correct keycodes for
35
+ * keyup/keydown events
36
+ *
37
+ * @type {Object}
38
+ */
39
+ var _MAP = {
40
+ 8: 'backspace',
41
+ 9: 'tab',
42
+ 13: 'enter',
43
+ 16: 'shift',
44
+ 17: 'ctrl',
45
+ 18: 'alt',
46
+ 20: 'capslock',
47
+ 27: 'esc',
48
+ 32: 'space',
49
+ 33: 'pageup',
50
+ 34: 'pagedown',
51
+ 35: 'end',
52
+ 36: 'home',
53
+ 37: 'left',
54
+ 38: 'up',
55
+ 39: 'right',
56
+ 40: 'down',
57
+ 45: 'ins',
58
+ 46: 'del',
59
+ 91: 'meta',
60
+ 93: 'meta',
61
+ 224: 'meta'
62
+ };
63
+
64
+ /**
65
+ * mapping for special characters so they can support
66
+ *
67
+ * this dictionary is only used incase you want to bind a
68
+ * keyup or keydown event to one of these keys
69
+ *
70
+ * @type {Object}
71
+ */
72
+ var _KEYCODE_MAP = {
73
+ 106: '*',
74
+ 107: '+',
75
+ 109: '-',
76
+ 110: '.',
77
+ 111 : '/',
78
+ 186: ';',
79
+ 187: '=',
80
+ 188: ',',
81
+ 189: '-',
82
+ 190: '.',
83
+ 191: '/',
84
+ 192: '`',
85
+ 219: '[',
86
+ 220: '\\',
87
+ 221: ']',
88
+ 222: '\''
89
+ };
90
+
91
+ /**
92
+ * this is a mapping of keys that require shift on a US keypad
93
+ * back to the non shift equivelents
94
+ *
95
+ * this is so you can use keyup events with these keys
96
+ *
97
+ * note that this will only work reliably on US keyboards
98
+ *
99
+ * @type {Object}
100
+ */
101
+ var _SHIFT_MAP = {
102
+ '~': '`',
103
+ '!': '1',
104
+ '@': '2',
105
+ '#': '3',
106
+ '$': '4',
107
+ '%': '5',
108
+ '^': '6',
109
+ '&': '7',
110
+ '*': '8',
111
+ '(': '9',
112
+ ')': '0',
113
+ '_': '-',
114
+ '+': '=',
115
+ ':': ';',
116
+ '\"': '\'',
117
+ '<': ',',
118
+ '>': '.',
119
+ '?': '/',
120
+ '|': '\\'
121
+ };
122
+
123
+ /**
124
+ * this is a list of special strings you can use to map
125
+ * to modifier keys when you specify your keyboard shortcuts
126
+ *
127
+ * @type {Object}
128
+ */
129
+ var _SPECIAL_ALIASES = {
130
+ 'option': 'alt',
131
+ 'command': 'meta',
132
+ 'return': 'enter',
133
+ 'escape': 'esc',
134
+ 'plus': '+',
135
+ 'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
136
+ };
137
+
138
+ /**
139
+ * variable to store the flipped version of _MAP from above
140
+ * needed to check if we should use keypress or not when no action
141
+ * is specified
142
+ *
143
+ * @type {Object|undefined}
144
+ */
145
+ var _REVERSE_MAP;
146
+
147
+ /**
148
+ * loop through the f keys, f1 to f19 and add them to the map
149
+ * programatically
150
+ */
151
+ for (var i = 1; i < 20; ++i) {
152
+ _MAP[111 + i] = 'f' + i;
153
+ }
154
+
155
+ /**
156
+ * loop through to map numbers on the numeric keypad
157
+ */
158
+ for (i = 0; i <= 9; ++i) {
159
+
160
+ // This needs to use a string cause otherwise since 0 is falsey
161
+ // mousetrap will never fire for numpad 0 pressed as part of a keydown
162
+ // event.
163
+ //
164
+ // @see https://github.com/ccampbell/mousetrap/pull/258
165
+ _MAP[i + 96] = i.toString();
166
+ }
167
+
168
+ /**
169
+ * cross browser add event method
170
+ *
171
+ * @param {Element|HTMLDocument} object
172
+ * @param {string} type
173
+ * @param {Function} callback
174
+ * @returns void
175
+ */
176
+ function _addEvent(object, type, callback) {
177
+ if (object.addEventListener) {
178
+ object.addEventListener(type, callback, false);
179
+ return;
180
+ }
181
+
182
+ object.attachEvent('on' + type, callback);
183
+ }
184
+
185
+ /**
186
+ * takes the event and returns the key character
187
+ *
188
+ * @param {Event} e
189
+ * @return {string}
190
+ */
191
+ function _characterFromEvent(e) {
192
+
193
+ // for keypress events we should return the character as is
194
+ if (e.type == 'keypress') {
195
+ var character = String.fromCharCode(e.which);
196
+
197
+ // if the shift key is not pressed then it is safe to assume
198
+ // that we want the character to be lowercase. this means if
199
+ // you accidentally have caps lock on then your key bindings
200
+ // will continue to work
201
+ //
202
+ // the only side effect that might not be desired is if you
203
+ // bind something like 'A' cause you want to trigger an
204
+ // event when capital A is pressed caps lock will no longer
205
+ // trigger the event. shift+a will though.
206
+ if (!e.shiftKey) {
207
+ character = character.toLowerCase();
208
+ }
209
+
210
+ return character;
211
+ }
212
+
213
+ // for non keypress events the special maps are needed
214
+ if (_MAP[e.which]) {
215
+ return _MAP[e.which];
216
+ }
217
+
218
+ if (_KEYCODE_MAP[e.which]) {
219
+ return _KEYCODE_MAP[e.which];
220
+ }
221
+
222
+ // if it is not in the special map
223
+
224
+ // with keydown and keyup events the character seems to always
225
+ // come in as an uppercase character whether you are pressing shift
226
+ // or not. we should make sure it is always lowercase for comparisons
227
+ return String.fromCharCode(e.which).toLowerCase();
228
+ }
229
+
230
+ /**
231
+ * checks if two arrays are equal
232
+ *
233
+ * @param {Array} modifiers1
234
+ * @param {Array} modifiers2
235
+ * @returns {boolean}
236
+ */
237
+ function _modifiersMatch(modifiers1, modifiers2) {
238
+ return modifiers1.sort().join(',') === modifiers2.sort().join(',');
239
+ }
240
+
241
+ /**
242
+ * takes a key event and figures out what the modifiers are
243
+ *
244
+ * @param {Event} e
245
+ * @returns {Array}
246
+ */
247
+ function _eventModifiers(e) {
248
+ var modifiers = [];
249
+
250
+ if (e.shiftKey) {
251
+ modifiers.push('shift');
252
+ }
253
+
254
+ if (e.altKey) {
255
+ modifiers.push('alt');
256
+ }
257
+
258
+ if (e.ctrlKey) {
259
+ modifiers.push('ctrl');
260
+ }
261
+
262
+ if (e.metaKey) {
263
+ modifiers.push('meta');
264
+ }
265
+
266
+ return modifiers;
267
+ }
268
+
269
+ /**
270
+ * prevents default for this event
271
+ *
272
+ * @param {Event} e
273
+ * @returns void
274
+ */
275
+ function _preventDefault(e) {
276
+ if (e.preventDefault) {
277
+ e.preventDefault();
278
+ return;
279
+ }
280
+
281
+ e.returnValue = false;
282
+ }
283
+
284
+ /**
285
+ * stops propogation for this event
286
+ *
287
+ * @param {Event} e
288
+ * @returns void
289
+ */
290
+ function _stopPropagation(e) {
291
+ if (e.stopPropagation) {
292
+ e.stopPropagation();
293
+ return;
294
+ }
295
+
296
+ e.cancelBubble = true;
297
+ }
298
+
299
+ /**
300
+ * determines if the keycode specified is a modifier key or not
301
+ *
302
+ * @param {string} key
303
+ * @returns {boolean}
304
+ */
305
+ function _isModifier(key) {
306
+ return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
307
+ }
308
+
309
+ /**
310
+ * reverses the map lookup so that we can look for specific keys
311
+ * to see what can and can't use keypress
312
+ *
313
+ * @return {Object}
314
+ */
315
+ function _getReverseMap() {
316
+ if (!_REVERSE_MAP) {
317
+ _REVERSE_MAP = {};
318
+ for (var key in _MAP) {
319
+
320
+ // pull out the numeric keypad from here cause keypress should
321
+ // be able to detect the keys from the character
322
+ if (key > 95 && key < 112) {
323
+ continue;
324
+ }
325
+
326
+ if (_MAP.hasOwnProperty(key)) {
327
+ _REVERSE_MAP[_MAP[key]] = key;
328
+ }
329
+ }
330
+ }
331
+ return _REVERSE_MAP;
332
+ }
333
+
334
+ /**
335
+ * picks the best action based on the key combination
336
+ *
337
+ * @param {string} key - character for key
338
+ * @param {Array} modifiers
339
+ * @param {string=} action passed in
340
+ */
341
+ function _pickBestAction(key, modifiers, action) {
342
+
343
+ // if no action was picked in we should try to pick the one
344
+ // that we think would work best for this key
345
+ if (!action) {
346
+ action = _getReverseMap()[key] ? 'keydown' : 'keypress';
347
+ }
348
+
349
+ // modifier keys don't work as expected with keypress,
350
+ // switch to keydown
351
+ if (action == 'keypress' && modifiers.length) {
352
+ action = 'keydown';
353
+ }
354
+
355
+ return action;
356
+ }
357
+
358
+ /**
359
+ * Converts from a string key combination to an array
360
+ *
361
+ * @param {string} combination like "command+shift+l"
362
+ * @return {Array}
363
+ */
364
+ function _keysFromString(combination) {
365
+ if (combination === '+') {
366
+ return ['+'];
367
+ }
368
+
369
+ combination = combination.replace(/\+{2}/g, '+plus');
370
+ return combination.split('+');
371
+ }
372
+
373
+ /**
374
+ * Gets info for a specific key combination
375
+ *
376
+ * @param {string} combination key combination ("command+s" or "a" or "*")
377
+ * @param {string=} action
378
+ * @returns {Object}
379
+ */
380
+ function _getKeyInfo(combination, action) {
381
+ var keys;
382
+ var key;
383
+ var i;
384
+ var modifiers = [];
385
+
386
+ // take the keys from this pattern and figure out what the actual
387
+ // pattern is all about
388
+ keys = _keysFromString(combination);
389
+
390
+ for (i = 0; i < keys.length; ++i) {
391
+ key = keys[i];
392
+
393
+ // normalize key names
394
+ if (_SPECIAL_ALIASES[key]) {
395
+ key = _SPECIAL_ALIASES[key];
396
+ }
397
+
398
+ // if this is not a keypress event then we should
399
+ // be smart about using shift keys
400
+ // this will only work for US keyboards however
401
+ if (action && action != 'keypress' && _SHIFT_MAP[key]) {
402
+ key = _SHIFT_MAP[key];
403
+ modifiers.push('shift');
404
+ }
405
+
406
+ // if this key is a modifier then add it to the list of modifiers
407
+ if (_isModifier(key)) {
408
+ modifiers.push(key);
409
+ }
410
+ }
411
+
412
+ // depending on what the key combination is
413
+ // we will try to pick the best event for it
414
+ action = _pickBestAction(key, modifiers, action);
415
+
416
+ return {
417
+ key: key,
418
+ modifiers: modifiers,
419
+ action: action
420
+ };
421
+ }
422
+
423
+ function _belongsTo(element, ancestor) {
424
+ if (element === null || element === document) {
425
+ return false;
426
+ }
427
+
428
+ if (element === ancestor) {
429
+ return true;
430
+ }
431
+
432
+ return _belongsTo(element.parentNode, ancestor);
433
+ }
434
+
435
+ function Mousetrap(targetElement) {
436
+ var self = this;
437
+
438
+ targetElement = targetElement || document;
439
+
440
+ if (!(self instanceof Mousetrap)) {
441
+ return new Mousetrap(targetElement);
442
+ }
443
+
444
+ /**
445
+ * element to attach key events to
446
+ *
447
+ * @type {Element}
448
+ */
449
+ self.target = targetElement;
450
+
451
+ /**
452
+ * a list of all the callbacks setup via Mousetrap.bind()
453
+ *
454
+ * @type {Object}
455
+ */
456
+ self._callbacks = {};
457
+
458
+ /**
459
+ * direct map of string combinations to callbacks used for trigger()
460
+ *
461
+ * @type {Object}
462
+ */
463
+ self._directMap = {};
464
+
465
+ /**
466
+ * keeps track of what level each sequence is at since multiple
467
+ * sequences can start out with the same sequence
468
+ *
469
+ * @type {Object}
470
+ */
471
+ var _sequenceLevels = {};
472
+
473
+ /**
474
+ * variable to store the setTimeout call
475
+ *
476
+ * @type {null|number}
477
+ */
478
+ var _resetTimer;
479
+
480
+ /**
481
+ * temporary state where we will ignore the next keyup
482
+ *
483
+ * @type {boolean|string}
484
+ */
485
+ var _ignoreNextKeyup = false;
486
+
487
+ /**
488
+ * temporary state where we will ignore the next keypress
489
+ *
490
+ * @type {boolean}
491
+ */
492
+ var _ignoreNextKeypress = false;
493
+
494
+ /**
495
+ * are we currently inside of a sequence?
496
+ * type of action ("keyup" or "keydown" or "keypress") or false
497
+ *
498
+ * @type {boolean|string}
499
+ */
500
+ var _nextExpectedAction = false;
501
+
502
+ /**
503
+ * resets all sequence counters except for the ones passed in
504
+ *
505
+ * @param {Object} doNotReset
506
+ * @returns void
507
+ */
508
+ function _resetSequences(doNotReset) {
509
+ doNotReset = doNotReset || {};
510
+
511
+ var activeSequences = false,
512
+ key;
513
+
514
+ for (key in _sequenceLevels) {
515
+ if (doNotReset[key]) {
516
+ activeSequences = true;
517
+ continue;
518
+ }
519
+ _sequenceLevels[key] = 0;
520
+ }
521
+
522
+ if (!activeSequences) {
523
+ _nextExpectedAction = false;
524
+ }
525
+ }
526
+
527
+ /**
528
+ * finds all callbacks that match based on the keycode, modifiers,
529
+ * and action
530
+ *
531
+ * @param {string} character
532
+ * @param {Array} modifiers
533
+ * @param {Event|Object} e
534
+ * @param {string=} sequenceName - name of the sequence we are looking for
535
+ * @param {string=} combination
536
+ * @param {number=} level
537
+ * @returns {Array}
538
+ */
539
+ function _getMatches(character, modifiers, e, sequenceName, combination, level) {
540
+ var i;
541
+ var callback;
542
+ var matches = [];
543
+ var action = e.type;
544
+
545
+ // if there are no events related to this keycode
546
+ if (!self._callbacks[character]) {
547
+ return [];
548
+ }
549
+
550
+ // if a modifier key is coming up on its own we should allow it
551
+ if (action == 'keyup' && _isModifier(character)) {
552
+ modifiers = [character];
553
+ }
554
+
555
+ // loop through all callbacks for the key that was pressed
556
+ // and see if any of them match
557
+ for (i = 0; i < self._callbacks[character].length; ++i) {
558
+ callback = self._callbacks[character][i];
559
+
560
+ // if a sequence name is not specified, but this is a sequence at
561
+ // the wrong level then move onto the next match
562
+ if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
563
+ continue;
564
+ }
565
+
566
+ // if the action we are looking for doesn't match the action we got
567
+ // then we should keep going
568
+ if (action != callback.action) {
569
+ continue;
570
+ }
571
+
572
+ // if this is a keypress event and the meta key and control key
573
+ // are not pressed that means that we need to only look at the
574
+ // character, otherwise check the modifiers as well
575
+ //
576
+ // chrome will not fire a keypress if meta or control is down
577
+ // safari will fire a keypress if meta or meta+shift is down
578
+ // firefox will fire a keypress if meta or control is down
579
+ if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {
580
+
581
+ // when you bind a combination or sequence a second time it
582
+ // should overwrite the first one. if a sequenceName or
583
+ // combination is specified in this call it does just that
584
+ //
585
+ // @todo make deleting its own method?
586
+ var deleteCombo = !sequenceName && callback.combo == combination;
587
+ var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
588
+ if (deleteCombo || deleteSequence) {
589
+ self._callbacks[character].splice(i, 1);
590
+ }
591
+
592
+ matches.push(callback);
593
+ }
594
+ }
595
+
596
+ return matches;
597
+ }
598
+
599
+ /**
600
+ * actually calls the callback function
601
+ *
602
+ * if your callback function returns false this will use the jquery
603
+ * convention - prevent default and stop propogation on the event
604
+ *
605
+ * @param {Function} callback
606
+ * @param {Event} e
607
+ * @returns void
608
+ */
609
+ function _fireCallback(callback, e, combo, sequence) {
610
+
611
+ // if this event should not happen stop here
612
+ if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
613
+ return;
614
+ }
615
+
616
+ if (callback(e, combo) === false) {
617
+ _preventDefault(e);
618
+ _stopPropagation(e);
619
+ }
620
+ }
621
+
622
+ /**
623
+ * handles a character key event
624
+ *
625
+ * @param {string} character
626
+ * @param {Array} modifiers
627
+ * @param {Event} e
628
+ * @returns void
629
+ */
630
+ self._handleKey = function(character, modifiers, e) {
631
+ var callbacks = _getMatches(character, modifiers, e);
632
+ var i;
633
+ var doNotReset = {};
634
+ var maxLevel = 0;
635
+ var processedSequenceCallback = false;
636
+
637
+ // Calculate the maxLevel for sequences so we can only execute the longest callback sequence
638
+ for (i = 0; i < callbacks.length; ++i) {
639
+ if (callbacks[i].seq) {
640
+ maxLevel = Math.max(maxLevel, callbacks[i].level);
641
+ }
642
+ }
643
+
644
+ // loop through matching callbacks for this key event
645
+ for (i = 0; i < callbacks.length; ++i) {
646
+
647
+ // fire for all sequence callbacks
648
+ // this is because if for example you have multiple sequences
649
+ // bound such as "g i" and "g t" they both need to fire the
650
+ // callback for matching g cause otherwise you can only ever
651
+ // match the first one
652
+ if (callbacks[i].seq) {
653
+
654
+ // only fire callbacks for the maxLevel to prevent
655
+ // subsequences from also firing
656
+ //
657
+ // for example 'a option b' should not cause 'option b' to fire
658
+ // even though 'option b' is part of the other sequence
659
+ //
660
+ // any sequences that do not match here will be discarded
661
+ // below by the _resetSequences call
662
+ if (callbacks[i].level != maxLevel) {
663
+ continue;
664
+ }
665
+
666
+ processedSequenceCallback = true;
667
+
668
+ // keep a list of which sequences were matches for later
669
+ doNotReset[callbacks[i].seq] = 1;
670
+ _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
671
+ continue;
672
+ }
673
+
674
+ // if there were no sequence matches but we are still here
675
+ // that means this is a regular match so we should fire that
676
+ if (!processedSequenceCallback) {
677
+ _fireCallback(callbacks[i].callback, e, callbacks[i].combo);
678
+ }
679
+ }
680
+
681
+ // if the key you pressed matches the type of sequence without
682
+ // being a modifier (ie "keyup" or "keypress") then we should
683
+ // reset all sequences that were not matched by this event
684
+ //
685
+ // this is so, for example, if you have the sequence "h a t" and you
686
+ // type "h e a r t" it does not match. in this case the "e" will
687
+ // cause the sequence to reset
688
+ //
689
+ // modifier keys are ignored because you can have a sequence
690
+ // that contains modifiers such as "enter ctrl+space" and in most
691
+ // cases the modifier key will be pressed before the next key
692
+ //
693
+ // also if you have a sequence such as "ctrl+b a" then pressing the
694
+ // "b" key will trigger a "keypress" and a "keydown"
695
+ //
696
+ // the "keydown" is expected when there is a modifier, but the
697
+ // "keypress" ends up matching the _nextExpectedAction since it occurs
698
+ // after and that causes the sequence to reset
699
+ //
700
+ // we ignore keypresses in a sequence that directly follow a keydown
701
+ // for the same character
702
+ var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
703
+ if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {
704
+ _resetSequences(doNotReset);
705
+ }
706
+
707
+ _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
708
+ };
709
+
710
+ /**
711
+ * handles a keydown event
712
+ *
713
+ * @param {Event} e
714
+ * @returns void
715
+ */
716
+ function _handleKeyEvent(e) {
717
+
718
+ // normalize e.which for key events
719
+ // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
720
+ if (typeof e.which !== 'number') {
721
+ e.which = e.keyCode;
722
+ }
723
+
724
+ var character = _characterFromEvent(e);
725
+
726
+ // no character found then stop
727
+ if (!character) {
728
+ return;
729
+ }
730
+
731
+ // need to use === for the character check because the character can be 0
732
+ if (e.type == 'keyup' && _ignoreNextKeyup === character) {
733
+ _ignoreNextKeyup = false;
734
+ return;
735
+ }
736
+
737
+ self.handleKey(character, _eventModifiers(e), e);
738
+ }
739
+
740
+ /**
741
+ * called to set a 1 second timeout on the specified sequence
742
+ *
743
+ * this is so after each key press in the sequence you have 1 second
744
+ * to press the next key before you have to start over
745
+ *
746
+ * @returns void
747
+ */
748
+ function _resetSequenceTimer() {
749
+ clearTimeout(_resetTimer);
750
+ _resetTimer = setTimeout(_resetSequences, 1000);
751
+ }
752
+
753
+ /**
754
+ * binds a key sequence to an event
755
+ *
756
+ * @param {string} combo - combo specified in bind call
757
+ * @param {Array} keys
758
+ * @param {Function} callback
759
+ * @param {string=} action
760
+ * @returns void
761
+ */
762
+ function _bindSequence(combo, keys, callback, action) {
763
+
764
+ // start off by adding a sequence level record for this combination
765
+ // and setting the level to 0
766
+ _sequenceLevels[combo] = 0;
767
+
768
+ /**
769
+ * callback to increase the sequence level for this sequence and reset
770
+ * all other sequences that were active
771
+ *
772
+ * @param {string} nextAction
773
+ * @returns {Function}
774
+ */
775
+ function _increaseSequence(nextAction) {
776
+ return function() {
777
+ _nextExpectedAction = nextAction;
778
+ ++_sequenceLevels[combo];
779
+ _resetSequenceTimer();
780
+ };
781
+ }
782
+
783
+ /**
784
+ * wraps the specified callback inside of another function in order
785
+ * to reset all sequence counters as soon as this sequence is done
786
+ *
787
+ * @param {Event} e
788
+ * @returns void
789
+ */
790
+ function _callbackAndReset(e) {
791
+ _fireCallback(callback, e, combo);
792
+
793
+ // we should ignore the next key up if the action is key down
794
+ // or keypress. this is so if you finish a sequence and
795
+ // release the key the final key will not trigger a keyup
796
+ if (action !== 'keyup') {
797
+ _ignoreNextKeyup = _characterFromEvent(e);
798
+ }
799
+
800
+ // weird race condition if a sequence ends with the key
801
+ // another sequence begins with
802
+ setTimeout(_resetSequences, 10);
803
+ }
804
+
805
+ // loop through keys one at a time and bind the appropriate callback
806
+ // function. for any key leading up to the final one it should
807
+ // increase the sequence. after the final, it should reset all sequences
808
+ //
809
+ // if an action is specified in the original bind call then that will
810
+ // be used throughout. otherwise we will pass the action that the
811
+ // next key in the sequence should match. this allows a sequence
812
+ // to mix and match keypress and keydown events depending on which
813
+ // ones are better suited to the key provided
814
+ for (var i = 0; i < keys.length; ++i) {
815
+ var isFinal = i + 1 === keys.length;
816
+ var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
817
+ _bindSingle(keys[i], wrappedCallback, action, combo, i);
818
+ }
819
+ }
820
+
821
+ /**
822
+ * binds a single keyboard combination
823
+ *
824
+ * @param {string} combination
825
+ * @param {Function} callback
826
+ * @param {string=} action
827
+ * @param {string=} sequenceName - name of sequence if part of sequence
828
+ * @param {number=} level - what part of the sequence the command is
829
+ * @returns void
830
+ */
831
+ function _bindSingle(combination, callback, action, sequenceName, level) {
832
+
833
+ // store a direct mapped reference for use with Mousetrap.trigger
834
+ self._directMap[combination + ':' + action] = callback;
835
+
836
+ // make sure multiple spaces in a row become a single space
837
+ combination = combination.replace(/\s+/g, ' ');
838
+
839
+ var sequence = combination.split(' ');
840
+ var info;
841
+
842
+ // if this pattern is a sequence of keys then run through this method
843
+ // to reprocess each pattern one key at a time
844
+ if (sequence.length > 1) {
845
+ _bindSequence(combination, sequence, callback, action);
846
+ return;
847
+ }
848
+
849
+ info = _getKeyInfo(combination, action);
850
+
851
+ // make sure to initialize array if this is the first time
852
+ // a callback is added for this key
853
+ self._callbacks[info.key] = self._callbacks[info.key] || [];
854
+
855
+ // remove an existing match if there is one
856
+ _getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level);
857
+
858
+ // add this call back to the array
859
+ // if it is a sequence put it at the beginning
860
+ // if not put it at the end
861
+ //
862
+ // this is important because the way these are processed expects
863
+ // the sequence ones to come first
864
+ self._callbacks[info.key][sequenceName ? 'unshift' : 'push']({
865
+ callback: callback,
866
+ modifiers: info.modifiers,
867
+ action: info.action,
868
+ seq: sequenceName,
869
+ level: level,
870
+ combo: combination
871
+ });
872
+ }
873
+
874
+ /**
875
+ * binds multiple combinations to the same callback
876
+ *
877
+ * @param {Array} combinations
878
+ * @param {Function} callback
879
+ * @param {string|undefined} action
880
+ * @returns void
881
+ */
882
+ self._bindMultiple = function(combinations, callback, action) {
883
+ for (var i = 0; i < combinations.length; ++i) {
884
+ _bindSingle(combinations[i], callback, action);
885
+ }
886
+ };
887
+
888
+ // start!
889
+ _addEvent(targetElement, 'keypress', _handleKeyEvent);
890
+ _addEvent(targetElement, 'keydown', _handleKeyEvent);
891
+ _addEvent(targetElement, 'keyup', _handleKeyEvent);
892
+ }
893
+
894
+ /**
895
+ * binds an event to mousetrap
896
+ *
897
+ * can be a single key, a combination of keys separated with +,
898
+ * an array of keys, or a sequence of keys separated by spaces
899
+ *
900
+ * be sure to list the modifier keys first to make sure that the
901
+ * correct key ends up getting bound (the last key in the pattern)
902
+ *
903
+ * @param {string|Array} keys
904
+ * @param {Function} callback
905
+ * @param {string=} action - 'keypress', 'keydown', or 'keyup'
906
+ * @returns void
907
+ */
908
+ Mousetrap.prototype.bind = function(keys, callback, action) {
909
+ var self = this;
910
+ keys = keys instanceof Array ? keys : [keys];
911
+ self._bindMultiple.call(self, keys, callback, action);
912
+ return self;
913
+ };
914
+
915
+ /**
916
+ * unbinds an event to mousetrap
917
+ *
918
+ * the unbinding sets the callback function of the specified key combo
919
+ * to an empty function and deletes the corresponding key in the
920
+ * _directMap dict.
921
+ *
922
+ * TODO: actually remove this from the _callbacks dictionary instead
923
+ * of binding an empty function
924
+ *
925
+ * the keycombo+action has to be exactly the same as
926
+ * it was defined in the bind method
927
+ *
928
+ * @param {string|Array} keys
929
+ * @param {string} action
930
+ * @returns void
931
+ */
932
+ Mousetrap.prototype.unbind = function(keys, action) {
933
+ var self = this;
934
+ return self.bind.call(self, keys, function() {}, action);
935
+ };
936
+
937
+ /**
938
+ * triggers an event that has already been bound
939
+ *
940
+ * @param {string} keys
941
+ * @param {string=} action
942
+ * @returns void
943
+ */
944
+ Mousetrap.prototype.trigger = function(keys, action) {
945
+ var self = this;
946
+ if (self._directMap[keys + ':' + action]) {
947
+ self._directMap[keys + ':' + action]({}, keys);
948
+ }
949
+ return self;
950
+ };
951
+
952
+ /**
953
+ * resets the library back to its initial state. this is useful
954
+ * if you want to clear out the current keyboard shortcuts and bind
955
+ * new ones - for example if you switch to another page
956
+ *
957
+ * @returns void
958
+ */
959
+ Mousetrap.prototype.reset = function() {
960
+ var self = this;
961
+ self._callbacks = {};
962
+ self._directMap = {};
963
+ return self;
964
+ };
965
+
966
+ /**
967
+ * should we stop this event before firing off callbacks
968
+ *
969
+ * @param {Event} e
970
+ * @param {Element} element
971
+ * @return {boolean}
972
+ */
973
+ Mousetrap.prototype.stopCallback = function(e, element) {
974
+ var self = this;
975
+
976
+ // if the element has the class "mousetrap" then no need to stop
977
+ if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
978
+ return false;
979
+ }
980
+
981
+ if (_belongsTo(element, self.target)) {
982
+ return false;
983
+ }
984
+
985
+ // stop for input, select, and textarea
986
+ return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
987
+ };
988
+
989
+ /**
990
+ * exposes _handleKey publicly so it can be overwritten by extensions
991
+ */
992
+ Mousetrap.prototype.handleKey = function() {
993
+ var self = this;
994
+ return self._handleKey.apply(self, arguments);
995
+ };
996
+
997
+ /**
998
+ * allow custom key mappings
999
+ */
1000
+ Mousetrap.addKeycodes = function(object) {
1001
+ for (var key in object) {
1002
+ if (object.hasOwnProperty(key)) {
1003
+ _MAP[key] = object[key];
1004
+ }
1005
+ }
1006
+ _REVERSE_MAP = null;
1007
+ };
1008
+
1009
+ /**
1010
+ * Init the global mousetrap functions
1011
+ *
1012
+ * This method is needed to allow the global mousetrap functions to work
1013
+ * now that mousetrap is a constructor function.
1014
+ */
1015
+ Mousetrap.init = function() {
1016
+ var documentMousetrap = Mousetrap(document);
1017
+ for (var method in documentMousetrap) {
1018
+ if (method.charAt(0) !== '_') {
1019
+ Mousetrap[method] = (function(method) {
1020
+ return function() {
1021
+ return documentMousetrap[method].apply(documentMousetrap, arguments);
1022
+ };
1023
+ } (method));
1024
+ }
1025
+ }
1026
+ };
1027
+
1028
+ Mousetrap.init();
1029
+
1030
+ // expose mousetrap to the global object
1031
+ window.Mousetrap = Mousetrap;
1032
+
1033
+ // expose as a common js module
1034
+ if (typeof module !== 'undefined' && module.exports) {
1035
+ module.exports = Mousetrap;
1036
+ }
1037
+
1038
+ // expose mousetrap as an AMD module
1039
+ if (typeof define === 'function' && define.amd) {
1040
+ define(function() {
1041
+ return Mousetrap;
1042
+ });
1043
+ }
1044
+ }) (typeof window !== 'undefined' ? window : null, typeof window !== 'undefined' ? document : null);