Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial version

  • Loading branch information...
commit 591019f59543854fd5d80fd207f39af396d44285 0 parents
@jamuhl jamuhl authored
Showing with 29,706 additions and 0 deletions.
  1. +15 −0 .gitignore
  2. +290 −0 Jakefile.js
  3. +19 −0 license
  4. +25 −0 package.json
  5. +40 −0 sample/basic.html
  6. +42 −0 sample/node/app/assets/css/bootstrap_override.styl
  7. +43 −0 sample/node/app/assets/css/main.styl
  8. +44 −0 sample/node/app/assets/js/backbone/base/baseCollection.js
  9. +68 −0 sample/node/app/assets/js/backbone/base/baseModel.js
  10. +31 −0 sample/node/app/assets/js/backbone/base/baseRouter.js
  11. +71 −0 sample/node/app/assets/js/backbone/base/baseView.js
  12. +30 −0 sample/node/app/assets/js/backbone/collections/persons.js
  13. +49 −0 sample/node/app/assets/js/backbone/models/person.js
  14. +25 −0 sample/node/app/assets/js/backbone/routers/addRouter.js
  15. +57 −0 sample/node/app/assets/js/backbone/routers/controller/appRouter.js
  16. +35 −0 sample/node/app/assets/js/backbone/routers/indexRouter.js
  17. +52 −0 sample/node/app/assets/js/backbone/state.js
  18. +61 −0 sample/node/app/assets/js/backbone/views/addView.js
  19. +91 −0 sample/node/app/assets/js/backbone/views/controller/appView.js
  20. +160 −0 sample/node/app/assets/js/backbone/views/indexView.js
  21. +40 −0 sample/node/app/assets/js/backbone/views/shared/formHelpers.js
  22. +126 −0 sample/node/app/assets/js/backbone/views/sidebars/personDetailView.js
  23. +65 −0 sample/node/app/assets/js/bootstrap.js
  24. +22 −0 sample/node/app/assets/js/globals.js
  25. +401 −0 sample/node/app/assets/js/lib/ICanHaz.js
  26. +9 −0 sample/node/app/assets/js/lib/ICanHaz.min.js
  27. +1,158 −0 sample/node/app/assets/js/lib/backbone-0.5.3.js
  28. +33 −0 sample/node/app/assets/js/lib/backbone-0.5.3.min.js
  29. +53 −0 sample/node/app/assets/js/lib/bootstrap-dropdown-1.3.1.js
  30. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.af-ZA.js
  31. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.af.js
  32. +74 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.am-ET.js
  33. +74 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.am.js
  34. +457 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-AE.js
  35. +462 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-BH.js
  36. +458 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-DZ.js
  37. +484 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-EG.js
  38. +457 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-IQ.js
  39. +462 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-JO.js
  40. +462 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-KW.js
  41. +457 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-LB.js
  42. +462 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-LY.js
  43. +458 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-MA.js
  44. +458 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-OM.js
  45. +457 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-QA.js
  46. +457 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-SA.js
  47. +457 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-SY.js
  48. +463 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-TN.js
  49. +457 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar-YE.js
  50. +457 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ar.js
  51. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.arn-CL.js
  52. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.arn.js
  53. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.as-IN.js
  54. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.as.js
  55. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.az-Cyrl-AZ.js
  56. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.az-Cyrl.js
  57. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.az-Latn-AZ.js
  58. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.az-Latn.js
  59. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.az.js
  60. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ba-RU.js
  61. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ba.js
  62. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.be-BY.js
  63. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.be.js
  64. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bg-BG.js
  65. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bg.js
  66. +74 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bn-BD.js
  67. +74 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bn-IN.js
  68. +74 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bn.js
  69. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bo-CN.js
  70. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bo.js
  71. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.br-FR.js
  72. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.br.js
  73. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bs-Cyrl-BA.js
  74. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bs-Cyrl.js
  75. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bs-Latn-BA.js
  76. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bs-Latn.js
  77. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.bs.js
  78. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ca-ES.js
  79. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ca.js
  80. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.co-FR.js
  81. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.co.js
  82. +85 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.cs-CZ.js
  83. +85 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.cs.js
  84. +70 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.cy-GB.js
  85. +70 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.cy.js
  86. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.da-DK.js
  87. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.da.js
  88. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.de-AT.js
  89. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.de-CH.js
  90. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.de-DE.js
  91. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.de-LI.js
  92. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.de-LU.js
  93. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.de.js
  94. +84 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.dsb-DE.js
  95. +84 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.dsb.js
  96. +164 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.dv-MV.js
  97. +164 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.dv.js
  98. +82 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.el-GR.js
  99. +82 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.el.js
  100. +47 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-029.js
  101. +52 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-AU.js
  102. +54 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-BZ.js
  103. +49 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-CA.js
  104. +55 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-GB.js
  105. +57 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-IE.js
  106. +60 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-IN.js
  107. +51 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-JM.js
  108. +56 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-MY.js
  109. +54 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-NZ.js
  110. +39 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-PH.js
  111. +53 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-SG.js
  112. +54 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-TT.js
  113. +33 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-US.js
  114. +61 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-ZA.js
  115. +39 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.en-ZW.js
  116. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-AR.js
  117. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-BO.js
  118. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-CL.js
  119. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-CO.js
  120. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-CR.js
  121. +69 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-DO.js
  122. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-EC.js
  123. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-ES.js
  124. +69 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-GT.js
  125. +71 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-HN.js
  126. +69 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-MX.js
  127. +71 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-NI.js
  128. +70 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-PA.js
  129. +70 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-PE.js
  130. +70 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-PR.js
  131. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-PY.js
  132. +69 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-SV.js
  133. +62 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-US.js
  134. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-UY.js
  135. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es-VE.js
  136. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.es.js
  137. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.et-EE.js
  138. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.et.js
  139. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.eu-ES.js
  140. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.eu.js
  141. +213 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fa-IR.js
  142. +213 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fa.js
  143. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fi-FI.js
  144. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fi.js
  145. +54 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fil-PH.js
  146. +54 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fil.js
  147. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fo-FO.js
  148. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fo.js
  149. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fr-BE.js
  150. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fr-CA.js
  151. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fr-CH.js
  152. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fr-FR.js
  153. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fr-LU.js
  154. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fr-MC.js
  155. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fr.js
  156. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fy-NL.js
  157. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.fy.js
  158. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ga-IE.js
  159. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ga.js
  160. +69 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.gd-GB.js
  161. +69 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.gd.js
  162. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.gl-ES.js
  163. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.gl.js
  164. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.gsw-FR.js
  165. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.gsw.js
  166. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.gu-IN.js
  167. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.gu.js
  168. +60 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ha-Latn-NG.js
  169. +60 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ha-Latn.js
  170. +60 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ha.js
  171. +97 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.he-IL.js
  172. +97 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.he.js
  173. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hi-IN.js
  174. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hi.js
  175. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hr-BA.js
  176. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hr-HR.js
  177. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hr.js
  178. +84 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hsb-DE.js
  179. +84 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hsb.js
  180. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hu-HU.js
  181. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hu.js
  182. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hy-AM.js
  183. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.hy.js
  184. +75 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.id-ID.js
  185. +75 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.id.js
  186. +60 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ig-NG.js
  187. +60 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ig.js
  188. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ii-CN.js
  189. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ii.js
  190. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.is-IS.js
  191. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.is.js
  192. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.it-CH.js
  193. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.it-IT.js
  194. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.it.js
  195. +65 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.iu-Cans-CA.js
  196. +65 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.iu-Cans.js
  197. +60 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.iu-Latn-CA.js
  198. +60 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.iu-Latn.js
  199. +60 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.iu.js
  200. +100 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ja-JP.js
  201. +100 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ja.js
  202. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ka-GE.js
  203. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ka.js
  204. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.kk-KZ.js
  205. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.kk.js
  206. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.kl-GL.js
  207. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.kl.js
  208. +99 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.km-KH.js
  209. +99 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.km.js
  210. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.kn-IN.js
  211. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.kn.js
  212. +96 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ko-KR.js
  213. +96 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ko.js
  214. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.kok-IN.js
  215. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.kok.js
  216. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ky-KG.js
  217. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ky.js
  218. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.lb-LU.js
  219. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.lb.js
  220. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.lo-LA.js
  221. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.lo.js
  222. +83 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.lt-LT.js
  223. +83 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.lt.js
  224. +83 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.lv-LV.js
  225. +83 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.lv.js
  226. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mi-NZ.js
  227. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mi.js
  228. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mk-MK.js
  229. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mk.js
  230. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ml-IN.js
  231. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ml.js
  232. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mn-Cyrl.js
  233. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mn-MN.js
  234. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mn-Mong-CN.js
  235. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mn-Mong.js
  236. +80 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mn.js
  237. +52 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.moh-CA.js
  238. +52 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.moh.js
  239. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mr-IN.js
  240. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mr.js
  241. +74 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ms-BN.js
  242. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ms-MY.js
  243. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ms.js
  244. +68 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mt-MT.js
  245. +68 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.mt.js
  246. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.nb-NO.js
  247. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.nb.js
  248. +68 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ne-NP.js
  249. +68 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ne.js
  250. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.nl-BE.js
  251. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.nl-NL.js
  252. +76 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.nl.js
  253. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.nn-NO.js
  254. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.nn.js
  255. +78 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.no.js
  256. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.nso-ZA.js
  257. +67 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.nso.js
  258. +83 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.oc-FR.js
  259. +83 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.oc.js
  260. +70 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.or-IN.js
  261. +70 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.or.js
  262. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.pa-IN.js
  263. +72 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.pa.js
  264. +84 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.pl-PL.js
  265. +84 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.pl.js
  266. +174 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.prs-AF.js
  267. +174 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.prs.js
  268. +176 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ps-AF.js
  269. +176 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ps.js
  270. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.pt-BR.js
  271. +81 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.pt-PT.js
  272. +79 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.pt.js
  273. +69 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.qut-GT.js
  274. +69 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.qut.js
  275. +74 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.quz-BO.js
  276. +73 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.quz-EC.js
  277. +68 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.quz-PE.js
  278. +74 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.quz.js
  279. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.rm-CH.js
  280. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.rm.js
  281. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ro-RO.js
  282. +77 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ro.js
  283. +82 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ru-RU.js
  284. +82 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.ru.js
  285. +65 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.rw-RW.js
  286. +65 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.rw.js
  287. +71 −0 sample/node/app/assets/js/lib/cultures/globalize.culture.sa-IN.js
Sorry, we could not display the entire diff because too many files (425) changed.
15 .gitignore
@@ -0,0 +1,15 @@
+$ cat .gitignore
+
+# Can ignore specific files
+.settings.xml
+.monitor
+
+# Use wildcards as well
+*~
+#*.swp
+
+# Can also ignore all directories and files in a directory.
+node_modules
+node_modules/**/*
+build
+build/**/*
290 Jakefile.js
@@ -0,0 +1,290 @@
+// Jakefile.js v0.0.1
+// (c) 2011 Kaba AG, CC EAC
+// (by) Jan Muehlemann (jamuhl)
+
+// Here we will document the common [jake](https://github.com/mde/jake) tasks generic to all other jake
+// files in other projects.
+
+// ## used modules
+// - __fs__ -> is all about filesystem [documentation](http://nodejs.org/docs/v0.3.1/api/fs.html)
+// - __spawn__ -> a longer running app call in console normally exit with some exitcode [documentation](http://nodejs.org/docs/v0.3.1/api/child_processes.html#child_process.spawn)
+// - __exec__ -> a simple version of console execution [documentation](http://nodejs.org/docs/v0.3.1/api/child_processes.html#child_process.exec)
+// - __smoosh__ -> is a javascript compressing tool [documentation](https://github.com/fat/smoosh)
+var fs = require('fs')
+ , spawn = require('child_process').spawn
+ , exec = require('child_process').exec
+ , smoosh = require('smoosh');
+
+// ## variables
+// - __port__ -> the port on which express and socket.io is running
+var port = 3000;
+
+// # root tasks
+// This are the main tasks in our project.
+// __hint:__ `jake -T` will display all avaiable tasks
+
+// ## DOC
+// run `jake doc`
+// This task will document provided modules by using [docco](http://jashkenas.github.com/docco/)
+desc('Creates the documentation');
+task('doc', [], function() {
+
+ // add your docs to this array
+ //
+ // - __name__ will be displayed in output
+ // - __files__ add your files (wildcards supported)
+ // - __target__ folder where to output the docs
+ var docs = [
+ { name: 'backbone',
+ files: [
+ 'app/assets/js/backbone/*.js'
+ , 'app/assets/js/backbone/base/*.js'
+ , 'app/assets/js/backbone/routers/controller/*.js'
+ , 'app/assets/js/backbone/routers/indexRouter.js'
+ , 'app/assets/js/backbone/views/controller/*.js'
+ , 'app/assets/js/bootstrap.js'
+ ],
+ target: 'build/docs/client/backbone' },
+ { name: 'jakefile', files: ['Jakefile.js'], target: 'build/docs/jake' },
+ ];
+
+ // __documents all items in docs array serial:__
+ // the selfcalling function next will shift the first item from the array
+ // and call the function process with itself as callback. The process
+ // function than calls the function document passing next as callback.
+ // When the callback _next_ is called it will take the next item from the array.
+ (function next(e) {
+ var process = function(doc, next) {
+ document(doc.name, doc.files.join(' '), doc.target, next);
+ };
+
+ (!e && docs.length) ? process(docs.shift(), next) : console.log((e) ? e : '');
+ }
+ )();
+});
+
+// # client tasks
+// This are tasks used to generate the clientside assets of the application
+namespace('client', function () {
+
+ // ## BUILD
+ // run `jake client:build`
+ // or `jake client:build[true]` for debugging output
+ // will build the projects clientsources.
+ desc('Builds the clientside scripts');
+ task('build', [], function(debug) {
+
+ // first grab all javascript files for client app
+ // __hint:__ the option `{debug:true}` will return the full list -
+ // in production we would only get the _client.js_.
+ var files = require('./app/assets/jsFileList').init({debug: true}).client;
+
+ var readFiles = [],
+ remaining = files.length;
+
+ // this will read all files from filesystem in parallel
+ for (i = 0, len = files.length; i < len; i++) {
+
+ // as we need the index (current i) later to put the file into
+ // the right readFiles array space we put the index into a closure function.
+ var read = function(index) {
+ fs.readFile(files[index], 'utf8', function(err, file) {
+ if (err) console.log(('- failed to read ' + files[index]).red);
+
+ readFiles[index] = file;
+ if (debug) console.log(('read ' + files[index]).grey)
+ remaining--;
+
+ // go on if all files are read in
+ if (remaining === 0) {
+ process();
+ }
+ });
+ };
+
+ read(i);
+ }
+
+ // process the _readFiles_ array
+ var process = function() {
+
+ // fist create target directory and log on error and call fail
+ mkdirs(dirs('public/js'), 0755, function(err) {
+ if (err) {
+ console.log('- failed to make folder public/js'.red);
+ fail('client:build - failed to make folder public/js')
+ }
+
+ // concate the files array
+ var concate = readFiles.join('\n\n');
+
+ // replace placeholders in the concated file.
+ concate = concate.replace('.#socketIoPort#.', port);
+
+ // write the file to the _public/js_ folder
+ fs.writeFile('./public/js/client.js', concate, 'utf8', function(err) {
+ if (err) {
+ console.log('- failed to write public/js/client.js'.red);
+ fail('client:build - failed to write public/js/client.js')
+ } else {
+
+ // a basic smoosh configuration object
+ smoosh.config({
+ /*"VERSION": "0.1",*/
+ "JAVASCRIPT": {
+ "DIST_DIR": "/public/js",
+ "client": [
+ "public/js/client.js",
+ ]
+ }
+ })
+
+ // run smoosh to get minified version of the js file
+ smoosh.build().analyze();
+
+ console.log('+ written public/js/client.js successfully'.green);
+ }
+ });
+ });
+ }
+ });
+});
+
+// # functions
+
+// ### function pathDepth
+// will return a string seperator with length depending on '/' count
+var pathDepth = function(str) {
+ var deep = (str.split("/").length - 1) * 2;
+ var sep = '';
+ for (i = 0; i < deep; i++) {
+ sep += ' ';
+ }
+ return sep;
+}
+
+// ### function dirs
+// will generate an array of folders out of a string
+// __Example:__
+// passing `root/sub1/sub2` will result in
+// `{'root','root/sub1','root/sub1/sub2'}`
+var dirs = function(path) {
+ var parts = path.split('/')
+ , arr = [];
+ for (i = 0, y = 0, len = parts.length; i < len; i++) {
+ var dir = parts[0];
+ for (z = 1; z <= y; z++) {
+ dir += '/' + parts[z];
+ }
+ arr.push(dir);
+ y++;
+ }
+ return arr;
+};
+
+// ### function mkdirs
+// will create the folders provided in the _dirs_ array
+// __hint:__ mode is permission set on folder as digit (like in chmod)
+var mkdirs = function(dirs, mode, cb){
+
+ var createIfNotExists = function(dir, mode, cb) {
+ fs.stat(dir, function(err, stat) {
+ if (stat && stat.isDirectory()) {
+ cb()
+ } else {
+ fs.mkdir(dir, mode, cb);
+ }
+ });
+ };
+
+ // creates all folder in dirs serial
+ (function next(e) {
+ (!e && dirs.length) ? createIfNotExists(dirs.shift(), mode, next) : cb((e) ? e : undefined);
+ })(null);
+};
+
+// ### function document
+// will document passed in source string and copy docs to output folder.
+var document = function(name, source, target, cb) {
+
+ // first it will create the target folder and only callback on error
+ // __hint:__ the function _dirs_ will generate an array out of the target string.
+ mkdirs(dirs(target), 0755, function(err) {
+ if (err) cb(err)
+
+ // __execute docco__ on success `docco myFile1 myFile2`
+ var docco = exec('docco ' + source, function (err, stdout, stderr) {
+
+ // on error log it and pass error to callback
+ if (err !== null) {
+ console.log(('+ failed to document ' + name + ' files').red);
+ cb(err);
+ }
+
+ // else __move the files__ from the _docs folder_ to target folder `mv source target`
+ else {
+ var move = exec('mv docs/* ' + target, function (err, stdout, stderr) {
+
+ // on error log it and pass error to callback
+ if (err !== null) {
+ console.log(('+ failed to move ' + name + ' documentation').red);
+ cb(error);
+ }
+
+ // else __remove the docs folder__ to have an empty one for next run `rm -rf folder`
+ else {
+ var remove = exec('rm -rf docs', function (err, stdout, stderr) {
+
+ // on error log it and pass error to callback
+ if (err !== null) {
+ console.log('+ failed to remove docs folder'.red);
+ cb(error);
+ }
+
+ // else __log success and callback__
+ else {
+ console.log(('+ documented ' + name + ' files successfully').green);
+ cb();
+ }
+ });
+ }
+ });
+ }
+ });
+ });
+};
+
+
+// ### function document
+// will extend the string object to append a _stylize function_ which will
+// style the console output.
+// __Example:__
+// `console.log('my string'.blue)`
+function stylize(str, style) {
+
+ // define the styles
+ var styles = {
+ //styles
+ 'bold' : [1, 22], 'italic' : [3, 23],
+ 'underline' : [4, 24], 'inverse' : [7, 27],
+ //grayscales
+ 'white' : [37, 39], 'grey' : [90, 39],
+ 'black' : [90, 39],
+ //colors
+ 'blue' : [34, 39], 'cyan' : [36, 39],
+ 'green' : [32, 39], 'magenta' : [35, 39],
+ 'red' : [31, 39], 'yellow' : [33, 39]
+ };
+ return '\033[' + styles[style][0] + 'm' + str + '\033[' + styles[style][1] + 'm';
+}
+
+['bold', 'underline', 'italic',
+ 'inverse', 'grey', 'yellow',
+ 'red', 'green', 'blue',
+ 'white', 'cyan', 'magenta'].forEach(function (style) {
+
+ String.prototype.__defineGetter__(style, function () {
+ return stylize(this, style);
+ });
+
+});
19 license
@@ -0,0 +1,19 @@
+Copyright (c) 2011 Jan Mühlemann
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
25 package.json
@@ -0,0 +1,25 @@
+{
+ "author": "jamuhl"
+ , "name": "express-backbone-bootstrap"
+ , "version": "0.0.1"
+ , "private": false
+ , "main": "server.js"
+ , "engines": {
+ "node": "~v0.4.12"
+ }
+ , "dependencies": {
+ "express": "2.4.6"
+ , "gzippo": ">= 0.0.1"
+ , "stylus": ">= 0.0.1"
+ , "nib" : ">= 0.0.1"
+ , "jade": ">= 0.0.1"
+ , "smoosh": "0.2.x"
+ , "glob": ">= 0.0.1"
+ }
+ , "devDependencies": {
+ "docco": ">=0.0.1"
+ , "jake": ">=0.0.1"
+ }
+ , "scripts": {
+ }
+}
40 sample/basic.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Basic Sample Usage</title>
+
+ <script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
+ <script type="text/javascript" src="../src/i18next.js"></script>
+
+</head>
+
+<body>
+
+ <ul>
+ <li><span id='fillMe'></span></li>
+ </ul>
+
+ <script type="text/javascript">
+
+ $.i18n.init(
+ {
+ lang: 'en',
+ dictionary: {
+ mykey: 'this is a simple value'
+ }
+ },
+ function(t) {
+ $('#fillMe').text($.t('mykey'));
+ }
+
+
+ );
+
+
+
+ </script>
+
+</body>
+
+</html>
42 sample/node/app/assets/css/bootstrap_override.styl
@@ -0,0 +1,42 @@
+/* Override some defaults */
+html, body
+ background-color: #eee
+
+body
+ padding-top: 40px /* 40px to make the container go all the way to the bottom of the topbar */
+
+.container > footer p
+ text-align: center /* center align it with the container */
+
+/* The white background content wrapper */
+.content
+ background-color: #fff
+ padding: 20px
+ margin: 0 -20px /* negative indent the amount of the padding to maintain the grid system */
+ border-radius: 0 0 6px 6px
+ box-shadow: 0 1px 2px rgba(0,0,0,.15)
+
+/* Page header tweaks */
+.page-header
+ background-color: #f5f5f5
+ padding: 20px 20px 10px
+ margin: -20px -20px 20px
+
+/* Styles you shouldn't keep as they are for displaying this base example only */
+.content .span10,
+.content .span4
+ min-height: 500px
+
+/* Give a quick and non-cross-browser friendly divider */
+.content .span4
+ margin-left: 0
+ padding-left: 19px
+ border-left: 1px solid #eee
+
+.topbar .btn
+ border: 0
+
+// action extension
+.actions
+ &.left
+ padding: 17px 20px 18px 20px
43 sample/node/app/assets/css/main.styl
@@ -0,0 +1,43 @@
+@import "bootstrap_override.styl"
+
+// colors
+$highlight = #0069D6
+
+.personItem
+ position: relative
+ border: solid 1px $highlight
+ background: #fff
+ padding: 0 60px 0 20px
+ margin: 4px 0
+ border-radius: 2px
+
+ &:hover
+ border: solid 1px $highlight
+ border-left: solid 2px $highlight
+ color: $highlight
+ padding: 0 60px 0 19px
+
+ .name
+ font-family: Arial, sans-serif
+ font-size: 12pt
+
+ &.selected
+ border: solid 1px $highlight
+ border-left: solid 2px $highlight
+ color: $highlight
+ padding: 0 60px 0 19px
+
+ .itemcontent
+ padding: 12px 0 10px 0
+
+ .commands
+ display: inline-block
+ position: absolute
+ right: 10px
+ bottom: 5px
+
+ a
+ margin: 0 4px
+
+
+// add you styles
44 sample/node/app/assets/js/backbone/base/baseCollection.js
@@ -0,0 +1,44 @@
+// This is the base collection for our app. All our collections should inherit (by extending)
+// from this baseCollection.
+// __Example:__
+//
+// var collection = myApp.Collection.extend({});
+
+(function(myApp) {
+
+ // set _calix.Collection to our baseCollection
+ myApp.Collection = Backbone.Collection.extend({
+
+ // fetch list without overwriting existing objects (copied from backbone's fetch())
+ fetchNew: function(options) {
+ options = options || {};
+ var collection = this,
+ success = options.success;
+ options.success = function(resp, status, xhr) {
+ _(collection.parse(resp, xhr)).each(function(item) {
+ if (!collection.get(item.id)) {
+ collection.add(item, {silent:true});
+ }
+ });
+ if (!options.silent) collection.trigger('reset', collection, options);
+ if (success) success(collection, resp);
+ };
+ return (this.sync || Backbone.sync).call(this, 'read', this, options);
+ },
+
+ // will get existing model or create a new model
+ // __Example:__
+ //
+ // var myModel = myCollection.getOrCreate(myModelsId);
+ getOrCreate: function(modelId) {
+ var model = this.get(modelId);
+ if (!model) {
+ model = new this.model({ id: modelId});
+ this.add(model);
+ }
+ return model;
+ }
+
+ });
+
+}(myApp));
68 sample/node/app/assets/js/backbone/base/baseModel.js
@@ -0,0 +1,68 @@
+// This is the base model for our app. All our models should inherit (by extending)
+// from this baseModel.
+// __Example:__
+//
+// var model = myApp.Model.extend({});
+
+(function(myApp) {
+
+ // set _calix.Model_ to our baseModel
+ myApp.Model = Backbone.Model.extend({
+
+ // this will set the passed values to the model and validate it by calling
+ // the underlaying model.save function (which will not be executed thanks to
+ // our backbone.sync).
+ // __Example:__
+ //
+ // changeValues: function(e) {
+ // this.model.setAndValidate({myAttr: 'newValue'}, {
+ // error: _.bind(function(model, error) {
+ // // give feedback in ui
+ // }, this),
+ // success: _.bind(function(model, res) {
+ // model.emitChange();
+ // this.render();
+ // }, this)
+ // });
+ // },
+ setAndValidate: function(changes, options) {
+ this.save(changes, {
+ error: function(model, error) {
+ if (options && options.error) options.error(model, error)
+ },
+ success: function(model) {
+ if (options && options.success) options.success(model)
+ }
+ });
+ },
+
+ // override this in subclasses if needed
+ isFullyLoaded: function() {
+ return true;
+ },
+
+ // callback for actions to take when fully loaded
+ onReady: $.noop,
+
+ // support for common pattern
+ ready: function(loadCallback, immediateCallback) {
+ var model = this,
+ immediateCallback = immediateCallback || loadCallback;
+ if (!model.isFullyLoaded()) {
+ model.fetch({
+ success: function() {
+ model.onReady();
+ loadCallback();
+ },
+ error: function() {
+ console.log('Error fetching model with id ' + model.id)
+ }
+ });
+ } else {
+ immediateCallback();
+ }
+ }
+
+ });
+
+}(myApp));
31 sample/node/app/assets/js/backbone/base/baseRouter.js
@@ -0,0 +1,31 @@
+// This is the base router for our app. All our routers should inherit (by extending)
+// from this baseRouter.
+// __Example:__
+//
+// var router = myApp.Router.extend({});
+
+(function(myApp) {
+
+ // set _calix.Router to our baseRouter
+ myApp.Router = Backbone.Router.extend({
+
+ // navigate to the current route (you must override this in subclasses)
+ getRoute: function() {
+ return '';
+ },
+
+ // update the url based on the current state
+ updateRoute: function() {
+ this.navigate(this.getRoute());
+ },
+
+ // update the url if this router's view is the top view
+ updateViewRoute: function() {
+ if (this.topview && this.topview == calix.state.get('topview')) {
+ this.updateRoute();
+ }
+ }
+
+ });
+
+}(myApp));
71 sample/node/app/assets/js/backbone/base/baseView.js
@@ -0,0 +1,71 @@
+// This is the base view for our app. All our views should inherit (by extending)
+// from this baseView.
+// __Example:__
+//
+// var view = myApp.View.extend({});
+
+(function(myApp, window) {
+ var state = myApp.state;
+
+ // set _calix.View_ to our baseView
+ myApp.View = Backbone.View.extend({
+
+ // basic open/close/remove support you can override this in specific view
+ open: function() {
+ $(this.el).show();
+ },
+
+ close: function() {
+ $(this.el).hide();
+ },
+
+ remove: function() {
+ $(this.el).slideUp(500, _.bind(function() {
+ $(this.el).remove();
+ }, this));
+ },
+
+ // bind/unbind state listeners. you can use this in your view to bind
+ // listeners to changes.
+ // __Example:__
+ //
+ // initialize: function() {
+ // this.bindState('change:myAttribute', this.myFunction, this);
+ // },
+ bindState: function(event, handler, context) {
+ if (!this._stateHandlers) {
+ this._stateHandlers = [];
+ }
+ state.bind(event, handler, context);
+ this._stateHandlers.push({ event: event, handler: handler });
+ },
+
+ unbindState: function() {
+ (this._stateHandlers || []).forEach(function(h) {
+ state.unbind(h.event, h.handler);
+ });
+ },
+
+ // unbind UI event handlers
+ unbindEvents: function() {
+ var view = this,
+ eventSplitter = /^(\S+)\s*(.*)$/,
+ events = view.events || [];
+ _(events).each(function(e, key) {
+ var match = key.match(eventSplitter),
+ eventName = match[1],
+ selector = match[2];
+ $(view.el).undelegate(selector, eventName);
+ });
+ },
+
+ // basic clear support
+ clear: function() {
+ $(this.el).empty();
+ this.unbindState();
+ this.unbindEvents();
+ }
+
+ });
+
+}(myApp, this));
30 sample/node/app/assets/js/backbone/collections/persons.js
@@ -0,0 +1,30 @@
+(function(myApp) {
+ var ns = myApp.collections || extend(myApp, 'myApp.collections')
+ , models = myApp.models
+ , Persons;
+
+ // the main collection holding our person models
+ Persons = ns.Persons = myApp.Collection.extend({
+
+ url: "/data/persons",
+
+ model: models.Person,
+
+ // will return the response part from json fetch
+ // to autofill the models
+ parse: function(res) {
+ var persons = res.response.persons;
+
+ for (i = 0, len = persons.length; i < len; i++) {
+ var person = persons[i];
+ if (person.persons) {
+ var list = new ns.Persons(person.persons);
+ person.persons = list;
+ }
+ }
+
+ return persons;
+ }
+ });
+
+}(myApp));
49 sample/node/app/assets/js/backbone/models/person.js
@@ -0,0 +1,49 @@
+(function(myApp) {
+ var ns = myApp.models || extend(myApp, 'myApp.models')
+ , collections = myApp.collections || extend(myApp, 'myApp.collections')
+ , Person;
+
+ // ### Visit
+ // the main model representing a visit
+ Person = ns.Person = myApp.Model.extend({
+
+ url: function(){
+ if (this.isNew()){
+ return "/data/addPerson";
+ } else {
+ return "/data/persons/" + this.id;
+ }
+ },
+
+ defaults: {
+ },
+
+ addPerson: function(person) {
+ var list = this.get('persons');
+
+ if (!list) {
+ list = new collections.Persons();
+ this.set({persons: list});
+ }
+
+ list.add(person);
+ this.trigger('change:persons', list);
+ },
+
+ onReady: function() {
+ },
+
+ isFullyLoaded: function() {
+ return true;
+ },
+
+ validate: function(attrs) {
+ if (attrs.firstname === '') {
+ return 'cannot have an empty firstname';
+ }
+ if (attrs.lastname === '') {
+ return 'cannot have an empty lastname';
+ }
+ }
+ });
+}(myApp));
25 sample/node/app/assets/js/backbone/routers/addRouter.js
@@ -0,0 +1,25 @@
+(function(myApp) {
+ var ns = myApp.routers || extend(myApp, 'myApp.routers')
+ , state = myApp.state
+ , AddView = myApp.views.AddView
+ , AddRouter;
+
+ AddRouter = myApp.Router.extend({
+
+ routes: {
+ "add": "add"
+ },
+
+ add: function() {
+ state.set({ topview: AddView });
+ },
+
+ getRoute: function() {
+ return 'add';
+ }
+
+ });
+
+ ns.AppRouter.register(AddRouter, AddView);
+
+}(myApp));
57 sample/node/app/assets/js/backbone/routers/controller/appRouter.js
@@ -0,0 +1,57 @@
+// All routers need to register itself to this _approuter_ which will handling all state
+// changes by calling updateRoute on the right router.
+
+(function(myApp) {
+ var ns = myApp.routers || extend(myApp, 'myApp.routers')
+ , state = myApp.state
+ , AppRouter
+ // array to hold registered routers
+ , routers = [];
+
+ AppRouter = ns.AppRouter = myApp.Router.extend({
+
+ initialize: function() {
+
+ // instantiate registered routers
+ routers.forEach(function(r) {
+ r.router = new r.cls();
+ });
+
+ // listen for state changes - if topview value changes the corresponding route
+ // will be updated
+ state.bind('change:topview', this.updateRoute, this);
+ },
+
+ // get the router for the current top view
+ getRouter: function() {
+ var topview = state.get('topview');
+ try {
+ return _(routers).detect(function(r) {
+ return r.view == topview;
+ }).router;
+ } catch (e) {
+ console.error('top view not found');
+ }
+ },
+
+ getRoute: function() {
+ // delegate
+ return this.getRouter().getRoute();
+ },
+
+ navigate: function(route, trigger) {
+ // delegate
+ return this.getRouter().navigate(route, trigger);
+ }
+
+ });
+
+ // register a router class to deal with a top-level view
+ AppRouter.register = function(router, view) {
+ routers.push({
+ view: view,
+ cls: router
+ })
+ };
+
+}(myApp));
35 sample/node/app/assets/js/backbone/routers/indexRouter.js
@@ -0,0 +1,35 @@
+// All routers work the same way - so we will only document this one.
+// A router registers itself to the _approuter_ which will handling all state
+// changes by calling updateRoute on the right router.
+
+(function(myApp) {
+ var ns = myApp.routers || extend(myApp, 'myApp.routers')
+ , state = myApp.state
+ , IndexView = myApp.views.IndexView
+ , DetailView = myApp.views.PersonDetailView
+ , IndexRouter;
+
+ ns.IndexRouter = IndexRouter = myApp.Router.extend({
+
+ routes: {
+ "": "index",
+ "index": "index"
+ },
+
+ index: function() {
+ // when the route match set the needed values in state
+ // this will invoke changes in _approuter_ and _appview_.
+ state.set({ topview: IndexView });
+ state.set({ sidebarview: DetailView });
+ },
+
+ getRoute: function() {
+ return 'index';
+ }
+
+ });
+
+ // register the router at the _approuter_ with the matching (top)view
+ ns.AppRouter.register(IndexRouter, IndexView);
+
+}(myApp));
52 sample/node/app/assets/js/backbone/state.js
@@ -0,0 +1,52 @@
+// The state backbone model holds the current state of the application.
+// It will hold:
+//
+// - shared collections and models
+// - current views (topview, sidebarview,...)
+//
+// Because this is a regular backbone model we can use all functionality
+// described under the [backbone documentation](http://documentcloud.github.com/backbone/#Model).
+// Mostly we will get / set attributes and watch if they changed.
+
+// This will self-execute the function with the _calix_ namespace as this.
+(function(myApp) {
+ var State, state;
+
+ // model to hold current state
+ State = Backbone.Model.extend({
+
+ // (de)serialization functions
+ deserialize: function(key, value) {
+ var params = this.params,
+ f = params[key] && params[key].deserialize || _.identity;
+ return f(value);
+ },
+
+ // serialization functions
+ serialize: function(key, value) {
+ var params = this.params,
+ f = params[key] && params[key].serialize || _.identity;
+ return f(value);
+ },
+
+ // convenience function to set a serialized value
+ setSerialized: function(key, value) {
+ o = {};
+ o[key] = this.deserialize(key, value);
+ this.set(o);
+ },
+
+ });
+
+ // initialize the singleton
+ state = myApp.state = new State();
+
+ // factory for de/serializable state parameters
+ function param(deserialize, serialize) {
+ return {
+ deserialize: deserialize || _.identity,
+ serialize: serialize || _.identity
+ };
+ };
+
+}(myApp));
61 sample/node/app/assets/js/backbone/views/addView.js
@@ -0,0 +1,61 @@
+(function(myApp) {
+ var ns = myApp.views || extend(myApp, 'myApp.views')
+ , models = myApp.models
+ , state = myApp.state
+ , formHelpers = myApp.views.shared.helpers.formHelpers;
+
+ // View: Add Person
+ ns.AddView = myApp.View.extend({
+
+ el: '#add-person-view',
+
+ initialize: function() {
+ var person = this.model = new models.Person();
+ this.render();
+ },
+
+ // route ui events to functions
+ events: {
+ 'click .createPerson' : 'createPerson',
+ 'click .cancel' : 'cancelAdd'
+ },
+
+ // is called from ui event to create a new visit
+ createPerson: function(e) {
+ // prevent default behavior -> form submit
+ e.preventDefault();
+
+ this.model.save({
+ firstname: this.$('#firstname').val(),
+ lastname: this.$('#lastname').val()
+ }, {
+ success: function(person) {
+ myApp.app.persons.add(person);
+ }
+ });
+
+ // go back
+ state.set({ 'topview': ns.IndexView });
+ },
+
+ cancelAdd: function(e) {
+ e.preventDefault();
+ state.set({ 'topview': ns.IndexView });
+ },
+
+ // is called to render the view
+ render: function() {
+ $(this.el).html(ich.addPerson(this.model.toJSON()));
+ return this;
+ },
+
+ open: function(fromRight) {
+ $(this.el).show('slide', {direction: (fromRight ? 'right' : 'left') }, 500);
+ },
+
+ close: function(fromRight) {
+ $(this.el).hide('slide', {direction: (fromRight ? 'left' : 'right') }, 500);
+ }
+ });
+
+}(myApp));
91 sample/node/app/assets/js/backbone/views/controller/appView.js
@@ -0,0 +1,91 @@
+// The _appView_ will listen on changes of topview, sidebarview and load the
+// matching view on change.
+
+(function(myApp) {
+ var ns = myApp.views || extend(myApp, 'myApp.views')
+ , state = myApp.state
+ , topviewOrder = [
+ ns.IndexView,
+ ns.AddVisitView,
+ ns.ConfigurationView
+ ]
+ , viewCache = [];
+
+ ns.AppView = myApp.View.extend({
+
+ initialize: function() {
+ // listen for state changes
+ state.bind('change:topview', this.updateTopView, this);
+ state.bind('change:sidebarview', this.updateSidebarView, this);
+ },
+
+ // this will cache all 'mainviews' views so they only have to be loaded once
+ cached: function(cls) {
+ var cached = _(viewCache).detect(function(c) {
+ return c.view == cls;
+ });
+ // if no key has been set, this has not been cached
+ if (!cached) {
+ // instantiate and cache
+ cached = {
+ view: cls,
+ instance: new cls({ parent: this })
+ };
+ viewCache.push(cached);
+ }
+ return cached.instance;
+ },
+
+ // update the top-level view
+ updateTopView: function() {
+ var cls = state.get('topview'),
+ view = this.cached(cls);
+ this.openTopView(view, cls);
+ },
+
+ // close the current view and open a new one
+ openTopView: function(view, cls) {
+ if (view) {
+ var oldview = this.currentTopView,
+ fromRight = true;
+ if (oldview && oldview != view) {
+ // get the old view class
+ oldCls = _(viewCache).detect(function(c) {
+ return c.instance == oldview;
+ }).view;
+ // work out left/right
+ fromRight = topviewOrder.indexOf(oldCls) < topviewOrder.indexOf(cls);
+ oldview.close(fromRight);
+ }
+ this.currentTopView = view;
+ view.open(fromRight);
+ }
+ },
+
+ // update the sidebar view
+ updateSidebarView: function() {
+ var cls = state.get('sidebarview'),
+ view = this.cached(cls);
+ this.openSidebarView(view, cls);
+ },
+
+ // close the current view and open a new one
+ openSidebarView: function(view, cls) {
+ if (view) {
+ var oldview = this.currentSidebarView;
+ if (oldview && oldview != view) {
+ // get the old view class
+ oldCls = _(viewCache).detect(function(c) {
+ return c.instance == oldview;
+ }).view;
+
+ oldview.close(true);
+ }
+ this.currentSidebarView = view;
+ view.open(true);
+ }
+ }
+
+ });
+
+}(myApp));
160 sample/node/app/assets/js/backbone/views/indexView.js
@@ -0,0 +1,160 @@
+(function(myApp) {
+ var ns = myApp.views || extend(myApp, 'myApp.views')
+ , state = myApp.state
+ , formHelpers = myApp.views.shared.helpers.formHelpers;
+
+ ns.PersonView = myApp.View.extend({
+ tagName: 'li',
+
+ className: 'personItem',
+
+ initialize: function() {
+ // bind functions to this
+ _.bindAll(this, 'editPerson', 'changePerson', 'render', 'remove');
+
+ // bind changes in the underlying model to the matching function.
+ this.model.bind('change', this.render);
+ this.model.bind('destroy', this.remove);
+
+
+ this.bindState('change:selectedPerson', this.toggleSelectedState, this);
+ },
+
+ // route ui events to functions
+ events: {
+ 'click .itemcontent' : 'selectPerson',
+ 'click .editPerson' : 'editPerson',
+ 'click .cancelEdit' : 'cancelEdit',
+ 'click .changePerson' : 'changePerson',
+ 'click .deletePerson' : 'deletePerson'
+ },
+
+ selectPerson: function() {
+ if (state.get('selectedPerson') !== this.model) {
+ state.set({selectedPerson: this.model});
+ } else {
+ state.unset('selectedPerson');
+ }
+ },
+
+ toggleSelectedState: function() {
+ if (state.get('selectedPerson') === this.model) {
+ if (this.model.selected === true) return;
+
+ this.model.selected = true;
+ this.render();
+ } else {
+ if (this.model.selected === false) return;
+
+ this.model.selected = false;
+ this.render();
+ }
+ },
+
+ // sets model to editMode and renders the view
+ editPerson: function(e) {
+ e.preventDefault();
+ this.model.editMode = true;
+ this.render();
+ },
+
+ // sets model back to non editMode and rerenders the view
+ cancelEdit: function(e) {
+ e.preventDefault();
+ this.model.editMode = false;
+ this.render();
+ },
+
+ // is called from the ui event to change the person.
+ changePerson: function(e) {
+ // prevent default behavior -> form submit
+ e.preventDefault();
+
+ this.model.save({
+ firstname: this.$('#firstname').val(),
+ lastname: this.$('#lastname').val()
+ });
+
+ this.model.editMode = false;
+ this.render();
+ },
+
+ // is called from the ui event to delete the person.
+ deletePerson: function() {
+ this.model.destroy();
+ state.set({ 'selectedPerson': null });
+ },
+
+ // is called to render the view
+ render: function() {
+ var model = this.model.toJSON();
+ model.selected = this.model.selected;
+ model.editMode = this.model.editMode;
+
+ // set html of this element using [icanhaz](http://icanhazjs.com/)
+ $(this.el).html(ich.person(model));
+
+ // select this item
+ if (this.model.selected === true) {
+ $(this.el).addClass('selected');
+ } else {
+ $(this.el).removeClass('selected');
+ }
+
+ return this;
+ },
+
+ // is called to remove the element from dom with a slideUp animation
+ remove: function() {
+ $(this.el).slideUp(500, _.bind(function() {
+ $(this.el).remove();
+ }, this));
+ }
+
+ });
+
+ // View: IndexView (index page)
+ ns.IndexView = myApp.View.extend({
+ el: '#index-view',
+
+ initialize: function() {
+ if (myApp.app.persons && !this.collection) {
+ this.collection = myApp.app.persons;
+ }
+ if (this.collection) {
+ this.collection.bind('reset', this.render, this);
+ this.collection.bind('add', this.addPerson, this);
+ this.collection.fetchNew();
+ }
+ },
+
+ events: {
+ 'click #newPerson' : 'uiAddPerson'
+ },
+
+ uiAddPerson: function() {
+ state.set({ 'topview': ns.AddView });
+ },
+
+ // is called to render all visit models in this collection
+ render: function() {
+ this.collection.each(this.addPerson);
+ },
+
+ // creates a personView with the given person model and
+ // appends it to the list element
+ addPerson: function(person) {
+ var view = new ns.PersonView({model: person});
+ this.$('#persons').append(view.render().el);
+ },
+
+ open: function(fromRight) {
+ $(this.el).show('slide', {direction: (fromRight ? 'right' : 'left') }, 500);
+ },
+
+ close: function(fromRight) {
+ $(this.el).hide('slide', {direction: (fromRight ? 'left' : 'right') }, 500);
+ }
+ });
+
+}(myApp));
40 sample/node/app/assets/js/backbone/views/shared/formHelpers.js
@@ -0,0 +1,40 @@
+// This holds shared elements used by more than one view. especially interesting
+// if you use includes in the serverside templates (eg. in add and edit forms).
+
+(function(myApp) {
+
+ var mod = extend(myApp, 'myApp.views.shared.helpers.formHelpers')
+ , formHelpers = {};
+
+ formHelpers.Select = {
+
+ // addes the elements of the array to the passed in select
+ fill: function(ele, arr) {
+ for(i=0, len=arr.length; i<len; i++) {
+ ele.append(
+ $('<option></option>').val(arr[i]).html(arr[i])
+ );
+ }
+ },
+
+ selectValue: function(ele, val) {
+ var selector = "option[value='" + val + "']";
+ ele.find(selector).attr('selected', true);
+ }
+ }
+
+ formHelpers.Input = {
+
+ validateNotEmpty: function(ele) {
+ if (ele.val() === '') {
+ ele.addClass('error');
+ } else {
+ ele.removeClass('error');
+ }
+ }
+ }
+
+ // finally extend the namespace with the helper
+ $.extend(mod, formHelpers);
+
+})(myApp);
126 sample/node/app/assets/js/backbone/views/sidebars/personDetailView.js
@@ -0,0 +1,126 @@
+(function(myApp) {
+ var ns = myApp.views || extend(myApp, 'myApp.views')
+ , models = myApp.models
+ , collections = myApp.collections
+ , formHelpers = myApp.views.shared.helpers.formHelpers
+ , state = myApp.state
+ , PersonItemView;
+
+
+ PersonItemView = myApp.View.extend({
+ tagName: 'li',
+
+ className: 'personItem',
+
+ initialize: function() {
+ // bind changes in the underlying model to the matching function.
+ this.model.bind('change', this.render, this);
+ this.model.bind('destroy', this.remove, this);
+
+ this.model.parent.bind('change:selectedPerson', this.toggleSelectedState, this);
+ },
+
+ // route ui events to functions
+ events: {
+ 'click .itemcontent' : 'uiSelectPerson',
+ 'click .removePerson' : 'uiRemovePerson'
+ },
+
+ uiSelectPerson: function() {
+ if (this.model.parent.get('selectedPerson') !== this.model) {
+ this.model.parent.set({selectedPerson: this.model});
+ } else {
+ this.model.parent.unset('selectedPerson');
+ }
+ },
+
+ uiRemovePerson: function(e) {
+ e.preventDefault();
+
+ this.model.destroy();
+ },
+
+ toggleSelectedState: function() {
+ if (this.model.parent.get('selectedPerson') === this.model) {
+ if (this.model.selected === true) return;
+
+ this.model.selected = true;
+ this.render();
+ } else {
+ if (this.model.selected === false) return;
+
+ this.model.selected = false;
+ this.render();
+ }
+ },
+
+ // is called to render the view
+ render: function() {
+ var model = this.model.toJSON();
+ model.selected = this.model.selected;
+
+ // set html of this element using [icanhaz](http://icanhazjs.com/)
+ $(this.el).html(ich.personItem(model));
+
+ if (this.model.selected === true) {
+ // select this item
+ $(this.el).addClass('selected');
+ } else {
+ $(this.el).removeClass('selected');
+ }
+
+ return this;
+ }
+ });
+
+ // View: Sidebar
+ ns.PersonDetailView = myApp.View.extend({
+ el: '#sidebar',
+
+ initialize: function() {
+ _.bindAll(this, 'render');
+
+ this.bindState('change:selectedPerson', this.setModel, this);
+ },
+
+ events: {
+ },
+
+ setModel: function() {
+ $(this.el).hide();
+ this.model = state.get('selectedPerson');
+ if (this.model && this.model.bind) {
+ this.model.bind('change:persons', this.render, this);
+ }
+ this.render();
+ this.open();
+ },
+
+ // is called to render all visit models in this collection
+ render: function() {
+ $(this.el).html(ich.personSidebar(this.model.toJSON()));
+
+ var persons = this.model.get('persons');
+ var ele = this.$('#persons');
+ if (persons) {
+ for (i = 0, len = persons.models.length; i < len; i++) {
+ var model = persons.models[i];
+ model.parent = this.model;
+ var view = new PersonItemView({model: model});
+ ele.append(view.render().el);
+ }
+ }
+
+ return this;
+ },
+
+ open: function(fromRight) {
+ $(this.el).show('slide', {direction: (fromRight ? 'right' : 'left') }, 500);
+ },
+
+ close: function(fromRight) {
+ $(this.el).hide('slide', {direction: (fromRight ? 'left' : 'right') }, 500);
+ }
+ });
+
+}(myApp));
65 sample/node/app/assets/js/bootstrap.js
@@ -0,0 +1,65 @@
+// app/assets/js/bootstrap.js v0.0.1
+// (c) 2011 Kaba AG, CC EAC
+// (by) Jan Muehlemann (jamuhl)
+
+
+// Basic architecture:
+
+// - __Models__ and __Collections__ are responsible for getting data from API
+// or emitting commands
+// - Singleton _state model_ is responsible for ui state data
+// - __Views__ are responsible for:
+// initialize:
+// - instantiating/fetching their models if necessary
+// - instantiating sub-views
+// - listening for state changes
+// - listening for model changes
+// render:
+// - adjusting the layout of their container boxes
+// - creating their content
+// events:
+// - listening for ui events, updating state
+// ui methods:
+// - updating ui on state change
+// - updating ui on model change
+// - __Routers__ are responsible for:
+// - setting state depending on route
+// - setting route depending on state
+ //
+// Process of opening a view:
+//
+// - URL router or UI event sets state.topview to the requested view class
+// - State fires topview:change
+// - AppView receives event, closes other views, calls view.open()
+// - view clears previous content if necessary
+// - view either renders, or fetches data and renders in the callback
+
+
+// setup app
+(function(myApp) {
+
+ myApp.app.init = function() {
+
+ myApp.app.persons = new myApp.collections.Persons();
+ myApp.app.router = new myApp.routers.AppRouter();
+ myApp.app.view = new myApp.views.AppView();
+
+ $.i18n.init({
+ lng: 'de-DE',
+ ns: { namespaces: ['ns.common', 'ns.special'], defaultNs: 'ns.special'}, // or just 'ns1' set default and list
+ dynamicLoad: true,
+ sendMissing: true
+ }, function() {
+ Backbone.history.start();
+
+ $('a.brand').text($.t('app.name'));
+ $('.page-header h1').text($.t('app.area'));
+ $('footer p').text($.t('ns.common:app.company.name'));
+ $('#newPerson').text($.t('ns.common:add'));
+ });
+ };
+
+})(myApp);
+
+// kick things off
+$(myApp.app.init);
22 sample/node/app/assets/js/globals.js
@@ -0,0 +1,22 @@
+// extend function
+function extend( ns, ns_string ) {
+ var parts = ns_string.split('.'),
+ parent = ns,
+ pl, i;
+ if (parts[0] == "myApp") {
+ parts = parts.slice(1);
+ }
+ pl = parts.length;
+ for (i = 0; i < pl; i++) {
+ //create a property if it doesnt exist
+ if (typeof parent[parts[i]] == 'undefined') {
+ parent[parts[i]] = {};
+ }
+ parent = parent[parts[i]];
+ }
+ return parent;
+}
+
+// root namespace
+var myApp = myApp = myApp || {};
+myApp.app = {};
401 sample/node/app/assets/js/lib/ICanHaz.js
@@ -0,0 +1,401 @@
+/*!
+ICanHaz.js version 0.9 -- by @HenrikJoreteg
+More info at: http://icanhazjs.com
+*/
+(function ($) {
+/*!
+ mustache.js -- Logic-less templates in JavaScript
+
+ by @janl (MIT Licensed, https://github.com/janl/mustache.js/blob/master/LICENSE).
+
+ See http://mustache.github.com/ for more info.
+*/
+
+var Mustache = function() {
+ var Renderer = function() {};
+
+ Renderer.prototype = {
+ otag: "{{",
+ ctag: "}}",
+ pragmas: {},
+ buffer: [],
+ pragmas_implemented: {
+ "IMPLICIT-ITERATOR": true
+ },
+ context: {},
+
+ render: function(template, context, partials, in_recursion) {
+ // reset buffer & set context
+ if(!in_recursion) {
+ this.context = context;
+ this.buffer = []; // TODO: make this non-lazy
+ }
+
+ // fail fast
+ if(!this.includes("", template)) {
+ if(in_recursion) {
+ return template;
+ } else {
+ this.send(template);
+ return;
+ }
+ }
+
+ template = this.render_pragmas(template);
+ var html = this.render_section(template, context, partials);
+ if(in_recursion) {
+ return this.render_tags(html, context, partials, in_recursion);
+ }
+
+ this.render_tags(html, context, partials, in_recursion);
+ },
+
+ /*
+ Sends parsed lines
+ */
+ send: function(line) {
+ if(line != "") {
+ this.buffer.push(line);
+ }
+ },
+
+ /*
+ Looks for %PRAGMAS
+ */
+ render_pragmas: function(template) {
+ // no pragmas
+ if(!this.includes("%", template)) {
+ return template;
+ }
+
+ var that = this;
+ var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
+ this.ctag);
+ return template.replace(regex, function(match, pragma, options) {
+ if(!that.pragmas_implemented[pragma]) {
+ throw({message:
+ "This implementation of mustache doesn't understand the '" +
+ pragma + "' pragma"});
+ }
+ that.pragmas[pragma] = {};
+ if(options) {
+ var opts = options.split("=");
+ that.pragmas[pragma][opts[0]] = opts[1];
+ }
+ return "";
+ // ignore unknown pragmas silently
+ });
+ },
+
+ /*
+ Tries to find a partial in the curent scope and render it
+ */
+ render_partial: function(name, context, partials) {
+ name = this.trim(name);
+ if(!partials || partials[name] === undefined) {
+ throw({message: "unknown_partial '" + name + "'"});
+ }
+ if(typeof(context[name]) != "object") {
+ return this.render(partials[name], context, partials, true);
+ }
+ return this.render(partials[name], context[name], partials, true);
+ },
+
+ /*
+ Renders inverted (^) and normal (#) sections
+ */
+ render_section: function(template, context, partials) {
+ if(!this.includes("#", template) && !this.includes("^", template)) {
+ return template;
+ }
+
+ var that = this;
+ // CSW - Added "+?" so it finds the tighest bound, not the widest
+ var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
+ "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
+ "\\s*", "mg");
+
+ // for each {{#foo}}{{/foo}} section do...
+ return template.replace(regex, function(match, type, name, content) {
+ var value = that.find(name, context);
+ if(type == "^") { // inverted section
+ if(!value || that.is_array(value) && value.length === 0) {
+ // false or empty list, render it
+ return that.render(content, context, partials, true);
+ } else {
+ return "";
+ }
+ } else if(type == "#") { // normal section
+ if(that.is_array(value)) { // Enumerable, Let's loop!
+ return that.map(value, function(row) {
+ return that.render(content, that.create_context(row),
+ partials, true);
+ }).join("");
+ } else if(that.is_object(value)) { // Object, Use it as subcontext!
+ return that.render(content, that.create_context(value),
+ partials, true);
+ } else if(typeof value === "function") {
+ // higher order section
+ return value.call(context, content, function(text) {
+ return that.render(text, context, partials, true);
+ });
+ } else if(value) { // boolean section
+ return that.render(content, context, partials, true);
+ } else {
+ return "";
+ }
+ }
+ });
+ },
+
+ /*
+ Replace {{foo}} and friends with values from our view
+ */
+ render_tags: function(template, context, partials, in_recursion) {
+ // tit for tat
+ var that = this;
+
+ var new_regex = function() {
+ return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
+ that.ctag + "+", "g");
+ };
+
+ var regex = new_regex();
+ var tag_replace_callback = function(match, operator, name) {
+ switch(operator) {
+ case "!": // ignore comments
+ return "";
+ case "=": // set new delimiters, rebuild the replace regexp
+ that.set_delimiters(name);
+ regex = new_regex();
+ return "";
+ case ">": // render partial
+ return that.render_partial(name, context, partials);
+ case "{": // the triple mustache is unescaped
+ return that.find(name, context);
+ default: // escape the value
+ return that.escape(that.find(name, context));
+ }
+ };
+ var lines = template.split("\n");
+ for(var i = 0; i < lines.length; i++) {
+ lines[i] = lines[i].replace(regex, tag_replace_callback, this);
+ if(!in_recursion) {
+ this.send(lines[i]);
+ }
+ }
+
+ if(in_recursion) {
+ return lines.join("\n");
+ }
+ },
+
+ set_delimiters: function(delimiters) {
+ var dels = delimiters.split(" ");
+ this.otag = this.escape_regex(dels[0]);
+ this.ctag = this.escape_regex(dels[1]);
+ },
+
+ escape_regex: function(text) {
+ // thank you Simon Willison
+ if(!arguments.callee.sRE) {
+ var specials = [
+ '/', '.', '*', '+', '?', '|',
+ '(', ')', '[', ']', '{', '}', '\\'
+ ];
+ arguments.callee.sRE = new RegExp(
+ '(\\' + specials.join('|\\') + ')', 'g'
+ );
+ }
+ return text.replace(arguments.callee.sRE, '\\$1');
+ },
+
+ /*
+ find `name` in current `context`. That is find me a value
+ from the view object
+ */
+ find: function(name, context) {
+ name = this.trim(name);
+
+ // Checks whether a value is thruthy or false or 0
+ function is_kinda_truthy(bool) {
+ return bool === false || bool === 0 || bool;
+ }
+
+ var value;
+ if(is_kinda_truthy(context[name])) {
+ value = context[name];
+ } else if(is_kinda_truthy(this.context[name])) {
+ value = this.context[name];
+ }
+
+ if(typeof value === "function") {
+ return value.apply(context);
+ }
+ if(value !== undefined) {
+ return value;
+ }
+ // silently ignore unkown variables
+ return "";
+ },
+
+ // Utility methods
+
+ /* includes tag */
+ includes: function(needle, haystack) {
+ return haystack.indexOf(this.otag + needle) != -1;
+ },
+
+ /*
+ Does away with nasty characters
+ */
+ escape: function(s) {
+ s = String(s === null ? "" : s);
+ return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
+ switch(s) {
+ case "&": return "&amp;";
+ case "\\": return "\\\\";
+ case '"': return '\"';
+ case "<": return "&lt;";
+ case ">": return "&gt;";
+ default: return s;
+ }
+ });
+ },
+
+ // by @langalex, support for arrays of strings
+ create_context: function(_context) {
+ if(this.is_object(_context)) {
+ return _context;
+ } else {
+ var iterator = ".";
+ if(this.pragmas["IMPLICIT-ITERATOR"]) {
+ iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
+ }
+ var ctx = {};
+ ctx[iterator] = _context;
+ return ctx;
+ }
+ },
+
+ is_object: function(a) {
+ return a && typeof a == "object";
+ },
+
+ is_array: function(a) {
+ return Object.prototype.toString.call(a) === '[object Array]';
+ },