camaleon_cms 0.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of camaleon_cms might be problematic. Click here for more details.

Files changed (653) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +37 -0
  3. data/app/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  4. data/app/assets/fonts/glyphicons-halflings-regular.svg +229 -0
  5. data/app/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  6. data/app/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  7. data/app/assets/images/admin/no_image.jpg +0 -0
  8. data/app/assets/images/bg-tree.jpeg +0 -0
  9. data/app/assets/images/camaleon-dark.png +0 -0
  10. data/app/assets/images/camaleon.png +0 -0
  11. data/app/assets/images/category-icon.png +0 -0
  12. data/app/assets/images/image-not-found.png +0 -0
  13. data/app/assets/images/language/de.png +0 -0
  14. data/app/assets/images/language/en.png +0 -0
  15. data/app/assets/images/language/es.png +0 -0
  16. data/app/assets/images/language/fr.png +0 -0
  17. data/app/assets/images/language/it.png +0 -0
  18. data/app/assets/images/language/pt.png +0 -0
  19. data/app/assets/images/language/zh.png +0 -0
  20. data/app/assets/images/loader.gif +0 -0
  21. data/app/assets/images/users/no-image.jpg +0 -0
  22. data/app/assets/javascripts/admin/actions.js +248 -0
  23. data/app/assets/javascripts/admin/admin-manifest.js +32 -0
  24. data/app/assets/javascripts/admin/bootstrap-colorpicker.js +540 -0
  25. data/app/assets/javascripts/admin/bootstrap-datepicker.js +1690 -0
  26. data/app/assets/javascripts/admin/bootstrap-datetimepicker.min.js +26 -0
  27. data/app/assets/javascripts/admin/bootstrap-select.js +1022 -0
  28. data/app/assets/javascripts/admin/core.js +267 -0
  29. data/app/assets/javascripts/admin/custom_fields.js +165 -0
  30. data/app/assets/javascripts/admin/data.js +78 -0
  31. data/app/assets/javascripts/admin/elfinder-manifest.js +7 -0
  32. data/app/assets/javascripts/admin/elfinder/elfinder.full.js +12646 -0
  33. data/app/assets/javascripts/admin/elfinder/i18n/elfinder.es.js +294 -0
  34. data/app/assets/javascripts/admin/elfinder/i18n/elfinder.fr.js +298 -0
  35. data/app/assets/javascripts/admin/elfinder/proxy/elFinderSupportVer1.js +338 -0
  36. data/app/assets/javascripts/admin/elfinder/upload_elfinder.js +236 -0
  37. data/app/assets/javascripts/admin/es.js +27 -0
  38. data/app/assets/javascripts/admin/form-builder/formbuilder.js +1250 -0
  39. data/app/assets/javascripts/admin/form-builder/vendor.js +3072 -0
  40. data/app/assets/javascripts/admin/form/cropper.min.js +9 -0
  41. data/app/assets/javascripts/admin/form/jquery.form.js +911 -0
  42. data/app/assets/javascripts/admin/jquery-sieve.js +6 -0
  43. data/app/assets/javascripts/admin/jquery-ui.min.js +10 -0
  44. data/app/assets/javascripts/admin/jquery.nestable.js +485 -0
  45. data/app/assets/javascripts/admin/jquery.slugify.js +65 -0
  46. data/app/assets/javascripts/admin/jquery.tagsinput.min.js +1 -0
  47. data/app/assets/javascripts/admin/jquery.validate.js +1415 -0
  48. data/app/assets/javascripts/admin/login.js.coffee +7 -0
  49. data/app/assets/javascripts/admin/login_manifest.js +18 -0
  50. data/app/assets/javascripts/admin/modal.js +94 -0
  51. data/app/assets/javascripts/admin/nav-menu.js +110 -0
  52. data/app/assets/javascripts/admin/plugins.js +637 -0
  53. data/app/assets/javascripts/admin/post.js +274 -0
  54. data/app/assets/javascripts/admin/tageditor/jquery.caret.min.js +2 -0
  55. data/app/assets/javascripts/admin/tageditor/jquery.tag-editor.js +352 -0
  56. data/app/assets/javascripts/admin/tinymce/langs/es.js +177 -0
  57. data/app/assets/javascripts/admin/tinymce/langs/readme.md +3 -0
  58. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-cool.gif +0 -0
  59. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-cry.gif +0 -0
  60. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-embarassed.gif +0 -0
  61. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif +0 -0
  62. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-frown.gif +0 -0
  63. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-innocent.gif +0 -0
  64. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-kiss.gif +0 -0
  65. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-laughing.gif +0 -0
  66. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-money-mouth.gif +0 -0
  67. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-sealed.gif +0 -0
  68. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-smile.gif +0 -0
  69. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-surprised.gif +0 -0
  70. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-tongue-out.gif +0 -0
  71. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-undecided.gif +0 -0
  72. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-wink.gif +0 -0
  73. data/app/assets/javascripts/admin/tinymce/plugins/emoticons/img/smiley-yell.gif +0 -0
  74. data/app/assets/javascripts/admin/tinymce/plugins/filemanager/plugin.min.js +31 -0
  75. data/app/assets/javascripts/admin/tinymce/plugins/visualblocks/css/visualblocks.css +135 -0
  76. data/app/assets/javascripts/admin/tinymce/skins/lightgray/content.min.css.scss +4 -0
  77. data/app/assets/javascripts/admin/tinymce/skins/lightgray/fonts/tinymce-small.eot +0 -0
  78. data/app/assets/javascripts/admin/tinymce/skins/lightgray/fonts/tinymce-small.svg +62 -0
  79. data/app/assets/javascripts/admin/tinymce/skins/lightgray/fonts/tinymce-small.ttf +0 -0
  80. data/app/assets/javascripts/admin/tinymce/skins/lightgray/fonts/tinymce-small.woff +0 -0
  81. data/app/assets/javascripts/admin/tinymce/skins/lightgray/fonts/tinymce.eot +0 -0
  82. data/app/assets/javascripts/admin/tinymce/skins/lightgray/fonts/tinymce.svg +83 -0
  83. data/app/assets/javascripts/admin/tinymce/skins/lightgray/fonts/tinymce.ttf +0 -0
  84. data/app/assets/javascripts/admin/tinymce/skins/lightgray/fonts/tinymce.woff +0 -0
  85. data/app/assets/javascripts/admin/tinymce/skins/lightgray/img/anchor.gif +0 -0
  86. data/app/assets/javascripts/admin/tinymce/skins/lightgray/img/loader.gif +0 -0
  87. data/app/assets/javascripts/admin/tinymce/skins/lightgray/img/object.gif +0 -0
  88. data/app/assets/javascripts/admin/tinymce/skins/lightgray/img/trans.gif +0 -0
  89. data/app/assets/javascripts/admin/tinymce/themes/modern/theme.min.js +1 -0
  90. data/app/assets/javascripts/admin/tinymce/tinymce.min.js +11 -0
  91. data/app/assets/javascripts/admin/translator.js +109 -0
  92. data/app/assets/javascripts/admin/underscore.js +1536 -0
  93. data/app/assets/javascripts/admin/widgets.js +53 -0
  94. data/app/assets/javascripts/audio/alert.mp3 +0 -0
  95. data/app/assets/javascripts/audio/fail.mp3 +0 -0
  96. data/app/assets/javascripts/bootstrap.min.js +7 -0
  97. data/app/assets/javascripts/elfinder_front.js +85 -0
  98. data/app/assets/javascripts/gibberish-aes.js +1017 -0
  99. data/app/assets/javascripts/jquery-1.11.3.min.js +5 -0
  100. data/app/assets/stylesheets/admin/admin-manifest.css +28 -0
  101. data/app/assets/stylesheets/admin/animate/animate.min.css +6 -0
  102. data/app/assets/stylesheets/admin/bootstrap/bootstrap-datetimepicker.css +339 -0
  103. data/app/assets/stylesheets/admin/bootstrap/bootstrap.min.css +5 -0
  104. data/app/assets/stylesheets/admin/bootstrap/images/glyphicons-halflings.png +0 -0
  105. data/app/assets/stylesheets/admin/core.scss +345 -0
  106. data/app/assets/stylesheets/admin/cropper/cropper.min.css +52 -0
  107. data/app/assets/stylesheets/admin/elfinder-manifest.css +6 -0
  108. data/app/assets/stylesheets/admin/elfinder/elfinder-theme.scss +191 -0
  109. data/app/assets/stylesheets/admin/elfinder/elfinder.css +1922 -0
  110. data/app/assets/stylesheets/admin/elfinder/images/arrows-active.png +0 -0
  111. data/app/assets/stylesheets/admin/elfinder/images/arrows-normal.png +0 -0
  112. data/app/assets/stylesheets/admin/elfinder/images/crop.gif +0 -0
  113. data/app/assets/stylesheets/admin/elfinder/images/dialogs.png +0 -0
  114. data/app/assets/stylesheets/admin/elfinder/images/icons-big.png +0 -0
  115. data/app/assets/stylesheets/admin/elfinder/images/icons-small.png +0 -0
  116. data/app/assets/stylesheets/admin/elfinder/images/logo.png +0 -0
  117. data/app/assets/stylesheets/admin/elfinder/images/progress.gif +0 -0
  118. data/app/assets/stylesheets/admin/elfinder/images/quicklook-bg.png +0 -0
  119. data/app/assets/stylesheets/admin/elfinder/images/quicklook-icons.png +0 -0
  120. data/app/assets/stylesheets/admin/elfinder/images/resize.png +0 -0
  121. data/app/assets/stylesheets/admin/elfinder/images/spinner-mini.gif +0 -0
  122. data/app/assets/stylesheets/admin/elfinder/images/toolbar.png +0 -0
  123. data/app/assets/stylesheets/admin/elfinder/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  124. data/app/assets/stylesheets/admin/elfinder/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  125. data/app/assets/stylesheets/admin/elfinder/images/ui-icons_222222_256x240.png +0 -0
  126. data/app/assets/stylesheets/admin/fonts/OpenSans-Regular-webfont.eot +0 -0
  127. data/app/assets/stylesheets/admin/fonts/OpenSans-Regular-webfont.svg +1831 -0
  128. data/app/assets/stylesheets/admin/fonts/OpenSans-Regular-webfont.ttf +0 -0
  129. data/app/assets/stylesheets/admin/fonts/OpenSans-Regular-webfont.woff +0 -0
  130. data/app/assets/stylesheets/admin/fonts/glyphicons-halflings-regular.eot +0 -0
  131. data/app/assets/stylesheets/admin/fonts/glyphicons-halflings-regular.svg +229 -0
  132. data/app/assets/stylesheets/admin/fonts/glyphicons-halflings-regular.ttf +0 -0
  133. data/app/assets/stylesheets/admin/fonts/glyphicons-halflings-regular.woff +0 -0
  134. data/app/assets/stylesheets/admin/form-builder/formbuilder.css +71 -0
  135. data/app/assets/stylesheets/admin/img/bg.png +0 -0
  136. data/app/assets/stylesheets/admin/img/colorpicker/alpha.png +0 -0
  137. data/app/assets/stylesheets/admin/img/colorpicker/hue.png +0 -0
  138. data/app/assets/stylesheets/admin/img/colorpicker/saturation.png +0 -0
  139. data/app/assets/stylesheets/admin/img/girls-1.jpg +0 -0
  140. data/app/assets/stylesheets/admin/img/jquery/resize-icon.png +0 -0
  141. data/app/assets/stylesheets/admin/img/loading.gif +1 -0
  142. data/app/assets/stylesheets/admin/img/logo-small.png +0 -0
  143. data/app/assets/stylesheets/admin/img/logo.png +0 -0
  144. data/app/assets/stylesheets/admin/img/no_image.jpg +0 -0
  145. data/app/assets/stylesheets/admin/img/rormeleon-white.png +0 -0
  146. data/app/assets/stylesheets/admin/img/thumb_no_found.jpg +0 -0
  147. data/app/assets/stylesheets/admin/jquery.tagsinput.css +72 -0
  148. data/app/assets/stylesheets/admin/jquery/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  149. data/app/assets/stylesheets/admin/jquery/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  150. data/app/assets/stylesheets/admin/jquery/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  151. data/app/assets/stylesheets/admin/jquery/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  152. data/app/assets/stylesheets/admin/jquery/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  153. data/app/assets/stylesheets/admin/jquery/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  154. data/app/assets/stylesheets/admin/jquery/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  155. data/app/assets/stylesheets/admin/jquery/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  156. data/app/assets/stylesheets/admin/jquery/images/ui-icons_222222_256x240.png +0 -0
  157. data/app/assets/stylesheets/admin/jquery/images/ui-icons_2e83ff_256x240.png +0 -0
  158. data/app/assets/stylesheets/admin/jquery/images/ui-icons_454545_256x240.png +0 -0
  159. data/app/assets/stylesheets/admin/jquery/images/ui-icons_888888_256x240.png +0 -0
  160. data/app/assets/stylesheets/admin/jquery/images/ui-icons_cd0a0a_256x240.png +0 -0
  161. data/app/assets/stylesheets/admin/jquery/jquery-ui.scss +1226 -0
  162. data/app/assets/stylesheets/admin/nav-menu.css +54 -0
  163. data/app/assets/stylesheets/admin/nestable/jquery.nestable.css +103 -0
  164. data/app/assets/stylesheets/admin/tageditor/delete.png +0 -0
  165. data/app/assets/stylesheets/admin/tageditor/delete.svg +4 -0
  166. data/app/assets/stylesheets/admin/tageditor/jquery.tag-editor.css +49 -0
  167. data/app/assets/stylesheets/admin/theme-default.scss +8462 -0
  168. data/app/assets/stylesheets/admin/widgets.css.scss +26 -0
  169. data/app/assets/stylesheets/bootstrap.min.css.scss +5 -0
  170. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/css/style.css +84 -0
  171. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/editor_plugin.js +1 -0
  172. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/editor_plugin_src.js +90 -0
  173. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/img/youtube.png +0 -0
  174. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/index.html +86 -0
  175. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/js/scripts.js +1 -0
  176. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/js/scripts_src.js +81 -0
  177. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/langs/en.js +3 -0
  178. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/langs/en_dlg.js +12 -0
  179. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/langs/es.js +3 -0
  180. data/app/assets/stylesheets/tinymce/plugins/youtubeIframe/langs/es_dlg.js +12 -0
  181. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/bootstrap.css +2228 -0
  182. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/content.css +189 -0
  183. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/dialog.css +295 -0
  184. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/butt2.png +0 -0
  185. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/button-bg.png +0 -0
  186. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/buttons.png +0 -0
  187. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/down_arrow.gif +0 -0
  188. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/fade-butt.png +0 -0
  189. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/icons.png +0 -0
  190. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/items.gif +0 -0
  191. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/menu-arrow.png +0 -0
  192. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/menu-check.png +0 -0
  193. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/progress.gif +0 -0
  194. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/tabs.gif +0 -0
  195. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/img/toolbarbg.png +0 -0
  196. data/app/assets/stylesheets/tinymce/themes/advanced/skins/bootstrap/ui.css +1163 -0
  197. data/app/controllers/admin/appearances/nav_menus_controller.rb +120 -0
  198. data/app/controllers/admin/appearances/themes_controller.rb +30 -0
  199. data/app/controllers/admin/appearances/widgets/assign_controller.rb +40 -0
  200. data/app/controllers/admin/appearances/widgets/main_controller.rb +57 -0
  201. data/app/controllers/admin/appearances/widgets/sidebar_controller.rb +59 -0
  202. data/app/controllers/admin/appearances_controller.rb +17 -0
  203. data/app/controllers/admin/categories_controller.rb +77 -0
  204. data/app/controllers/admin/comments_controller.rb +119 -0
  205. data/app/controllers/admin/installers_controller.rb +40 -0
  206. data/app/controllers/admin/media_controller.rb +55 -0
  207. data/app/controllers/admin/plugins_controller.rb +45 -0
  208. data/app/controllers/admin/post_tags_controller.rb +83 -0
  209. data/app/controllers/admin/posts/drafts_controller.rb +76 -0
  210. data/app/controllers/admin/posts_controller.rb +195 -0
  211. data/app/controllers/admin/sessions_controller.rb +144 -0
  212. data/app/controllers/admin/settings/custom_fields_controller.rb +85 -0
  213. data/app/controllers/admin/settings/post_types_controller.rb +73 -0
  214. data/app/controllers/admin/settings/sites_controller.rb +89 -0
  215. data/app/controllers/admin/settings_controller.rb +60 -0
  216. data/app/controllers/admin/user_roles_controller.rb +76 -0
  217. data/app/controllers/admin/users_controller.rb +120 -0
  218. data/app/controllers/admin_controller.rb +56 -0
  219. data/app/controllers/apps/plugins_admin_controller.rb +21 -0
  220. data/app/controllers/apps/plugins_front_controller.rb +21 -0
  221. data/app/controllers/apps/themes_admin_controller.rb +21 -0
  222. data/app/controllers/apps/themes_front_controller.rb +21 -0
  223. data/app/controllers/camaleon_controller.rb +87 -0
  224. data/app/controllers/concerns/frontend_concern.rb +57 -0
  225. data/app/controllers/frontend_controller.rb +240 -0
  226. data/app/decorators/application_decorator.rb +81 -0
  227. data/app/decorators/category_decorator.rb +61 -0
  228. data/app/decorators/custom_field_decorator.rb +31 -0
  229. data/app/decorators/custom_fields_concern.rb +33 -0
  230. data/app/decorators/metas_decorator_methods.rb +19 -0
  231. data/app/decorators/post_comment_decorator.rb +26 -0
  232. data/app/decorators/post_decorator.rb +162 -0
  233. data/app/decorators/post_tag_decorator.rb +36 -0
  234. data/app/decorators/post_type_decorator.rb +56 -0
  235. data/app/decorators/site_decorator.rb +136 -0
  236. data/app/decorators/term_taxonomy_decorator.rb +97 -0
  237. data/app/decorators/theme_decorator.rb +16 -0
  238. data/app/decorators/user_decorator.rb +67 -0
  239. data/app/decorators/widget_decorator.rb +13 -0
  240. data/app/helpers/admin/application_helper.rb +27 -0
  241. data/app/helpers/admin/breadcrumb_helper.rb +37 -0
  242. data/app/helpers/admin/category_helper.rb +22 -0
  243. data/app/helpers/admin/custom_fields_helper.rb +329 -0
  244. data/app/helpers/admin/menus_helper.rb +199 -0
  245. data/app/helpers/admin/post_type_helper.rb +50 -0
  246. data/app/helpers/camaleon_helper.rb +65 -0
  247. data/app/helpers/captcha_helper.rb +90 -0
  248. data/app/helpers/content_helper.rb +35 -0
  249. data/app/helpers/frontend/application_helper.rb +41 -0
  250. data/app/helpers/frontend/nav_menu_helper.rb +177 -0
  251. data/app/helpers/frontend/seo_helper.rb +60 -0
  252. data/app/helpers/frontend/site_helper.rb +60 -0
  253. data/app/helpers/hooks_helper.rb +54 -0
  254. data/app/helpers/html_helper.rb +114 -0
  255. data/app/helpers/plugins_helper.rb +129 -0
  256. data/app/helpers/session_helper.rb +108 -0
  257. data/app/helpers/short_code_helper.rb +82 -0
  258. data/app/helpers/site_helper.rb +124 -0
  259. data/app/helpers/theme_helper.rb +51 -0
  260. data/app/helpers/uploader_helper.rb +144 -0
  261. data/app/helpers/user_roles_helper.rb +14 -0
  262. data/app/mailers/html_mailer.rb +47 -0
  263. data/app/models/ability.rb +113 -0
  264. data/app/models/category.rb +55 -0
  265. data/app/models/concerns/categories_tags_for_posts.rb +140 -0
  266. data/app/models/concerns/custom_fields_read.rb +221 -0
  267. data/app/models/concerns/metas.rb +133 -0
  268. data/app/models/concerns/metas_saved.rb +10 -0
  269. data/app/models/concerns/site_public.rb +23 -0
  270. data/app/models/custom_field.rb +34 -0
  271. data/app/models/custom_field_group.rb +121 -0
  272. data/app/models/custom_fields_relationship.rb +26 -0
  273. data/app/models/meta.rb +16 -0
  274. data/app/models/nav_menu.rb +41 -0
  275. data/app/models/nav_menu_item.rb +36 -0
  276. data/app/models/plugin.rb +57 -0
  277. data/app/models/plugins.rb +13 -0
  278. data/app/models/post.rb +89 -0
  279. data/app/models/post_comment.rb +44 -0
  280. data/app/models/post_default.rb +78 -0
  281. data/app/models/post_relationship.rb +26 -0
  282. data/app/models/post_tag.rb +15 -0
  283. data/app/models/post_type.rb +125 -0
  284. data/app/models/site.rb +337 -0
  285. data/app/models/term_relationship.rb +26 -0
  286. data/app/models/term_taxonomy.rb +156 -0
  287. data/app/models/theme.rb +33 -0
  288. data/app/models/themes.rb +13 -0
  289. data/app/models/user.rb +172 -0
  290. data/app/models/user_relationship.rb +17 -0
  291. data/app/models/user_role.rb +139 -0
  292. data/app/models/widget.rb +13 -0
  293. data/app/models/widget/assigned.rb +34 -0
  294. data/app/models/widget/main.rb +52 -0
  295. data/app/models/widget/sidebar.rb +28 -0
  296. data/app/views/_flash_messages.html.erb +39 -0
  297. data/app/views/admin/appearances/_external_menu.html.erb +16 -0
  298. data/app/views/admin/appearances/menu.html.erb +199 -0
  299. data/app/views/admin/appearances/themes/index.html.erb +68 -0
  300. data/app/views/admin/appearances/themes/preview.html.erb +1 -0
  301. data/app/views/admin/appearances/themes/theme_load_data.html.erb +40 -0
  302. data/app/views/admin/appearances/widgets/assign/_form.html.erb +35 -0
  303. data/app/views/admin/appearances/widgets/main/form.html.erb +27 -0
  304. data/app/views/admin/appearances/widgets/main/index.html.erb +91 -0
  305. data/app/views/admin/appearances/widgets/sidebar/form.html.erb +23 -0
  306. data/app/views/admin/categories/_form.html.erb +42 -0
  307. data/app/views/admin/categories/edit.html.erb +18 -0
  308. data/app/views/admin/categories/index.html.erb +81 -0
  309. data/app/views/admin/comments/form.html.erb +55 -0
  310. data/app/views/admin/comments/index.html.erb +58 -0
  311. data/app/views/admin/comments/reply.html.erb +197 -0
  312. data/app/views/admin/dashboard/index.html.erb +25 -0
  313. data/app/views/admin/installers/form.html.erb +19 -0
  314. data/app/views/admin/installers/welcome.html.erb +8 -0
  315. data/app/views/admin/media/iframe.html.erb +33 -0
  316. data/app/views/admin/media/index.html.erb +31 -0
  317. data/app/views/admin/plugins/_plugins_list.html.erb +59 -0
  318. data/app/views/admin/plugins/index.html.erb +37 -0
  319. data/app/views/admin/post_tags/_form.html.erb +34 -0
  320. data/app/views/admin/post_tags/edit.html.erb +18 -0
  321. data/app/views/admin/post_tags/index.html.erb +79 -0
  322. data/app/views/admin/posts/_filter_posts.html.erb +6 -0
  323. data/app/views/admin/posts/form.html.erb +294 -0
  324. data/app/views/admin/posts/index.html.erb +121 -0
  325. data/app/views/admin/posts/show.html.erb +20 -0
  326. data/app/views/admin/sessions/forgot.html.erb +51 -0
  327. data/app/views/admin/sessions/login.html.erb +39 -0
  328. data/app/views/admin/sessions/register.html.erb +63 -0
  329. data/app/views/admin/settings/custom_fields/_meta_data.html.erb +102 -0
  330. data/app/views/admin/settings/custom_fields/_render.html.erb +73 -0
  331. data/app/views/admin/settings/custom_fields/fields/audio.html.erb +4 -0
  332. data/app/views/admin/settings/custom_fields/fields/checkbox.html.erb +3 -0
  333. data/app/views/admin/settings/custom_fields/fields/checkboxes.html.erb +5 -0
  334. data/app/views/admin/settings/custom_fields/fields/colorpicker.html.erb +7 -0
  335. data/app/views/admin/settings/custom_fields/fields/date.html.erb +7 -0
  336. data/app/views/admin/settings/custom_fields/fields/editor.html.erb +4 -0
  337. data/app/views/admin/settings/custom_fields/fields/email.html.erb +3 -0
  338. data/app/views/admin/settings/custom_fields/fields/field_attrs.html.erb +10 -0
  339. data/app/views/admin/settings/custom_fields/fields/file.html.erb +4 -0
  340. data/app/views/admin/settings/custom_fields/fields/image.html.erb +4 -0
  341. data/app/views/admin/settings/custom_fields/fields/numeric.html.erb +4 -0
  342. data/app/views/admin/settings/custom_fields/fields/phone.html.erb +3 -0
  343. data/app/views/admin/settings/custom_fields/fields/posts.html.erb +17 -0
  344. data/app/views/admin/settings/custom_fields/fields/radio.html.erb +5 -0
  345. data/app/views/admin/settings/custom_fields/fields/select.html.erb +7 -0
  346. data/app/views/admin/settings/custom_fields/fields/select_eval.html.erb +3 -0
  347. data/app/views/admin/settings/custom_fields/fields/text_area.html.erb +3 -0
  348. data/app/views/admin/settings/custom_fields/fields/text_box.html.erb +3 -0
  349. data/app/views/admin/settings/custom_fields/fields/url.html.erb +3 -0
  350. data/app/views/admin/settings/custom_fields/fields/users.html.erb +8 -0
  351. data/app/views/admin/settings/custom_fields/fields/video.html.erb +4 -0
  352. data/app/views/admin/settings/custom_fields/form.html.erb +249 -0
  353. data/app/views/admin/settings/custom_fields/get_items.html.erb +199 -0
  354. data/app/views/admin/settings/custom_fields/index.html.erb +69 -0
  355. data/app/views/admin/settings/languages.html.erb +68 -0
  356. data/app/views/admin/settings/post_types/_form.html.erb +93 -0
  357. data/app/views/admin/settings/post_types/edit.html.erb +18 -0
  358. data/app/views/admin/settings/post_types/index.html.erb +80 -0
  359. data/app/views/admin/settings/site.html.erb +124 -0
  360. data/app/views/admin/settings/sites/form.html.erb +50 -0
  361. data/app/views/admin/settings/sites/index.html.erb +56 -0
  362. data/app/views/admin/taxonomy/_form.html.erb +62 -0
  363. data/app/views/admin/taxonomy/edit.html.erb +18 -0
  364. data/app/views/admin/taxonomy/index.html.erb +79 -0
  365. data/app/views/admin/user_roles/form.html.erb +168 -0
  366. data/app/views/admin/user_roles/index.html.erb +54 -0
  367. data/app/views/admin/users/form.html.erb +305 -0
  368. data/app/views/admin/users/index.html.erb +58 -0
  369. data/app/views/admin/users/profile.html.erb +118 -0
  370. data/app/views/admin/users/profile/_activity.html.erb +180 -0
  371. data/app/views/admin/users/profile/_settings.html.erb +15 -0
  372. data/app/views/admin/users/profile_edit.html.erb +305 -0
  373. data/app/views/default_theme/admin/settings.html.erb +1 -0
  374. data/app/views/default_theme/category.html.erb +25 -0
  375. data/app/views/default_theme/category.rss.builder +23 -0
  376. data/app/views/default_theme/index.html.erb +70 -0
  377. data/app/views/default_theme/index.rss.builder +21 -0
  378. data/app/views/default_theme/layouts/_footer.html.erb +18 -0
  379. data/app/views/default_theme/layouts/_header.html.erb +38 -0
  380. data/app/views/default_theme/layouts/index.html.erb +34 -0
  381. data/app/views/default_theme/partials/_breadcrumb.html.erb +10 -0
  382. data/app/views/default_theme/partials/_categories_list.html.erb +23 -0
  383. data/app/views/default_theme/partials/_comments.html.erb +33 -0
  384. data/app/views/default_theme/partials/_comments_list.html.erb +21 -0
  385. data/app/views/default_theme/partials/_flash_messages.html.erb +26 -0
  386. data/app/views/default_theme/partials/_forms.html.erb +19 -0
  387. data/app/views/default_theme/partials/_post_list_item.html.erb +13 -0
  388. data/app/views/default_theme/partials/_render_custom_field.html.erb +57 -0
  389. data/app/views/default_theme/partials/_search_form.html.erb +10 -0
  390. data/app/views/default_theme/partials/_sidebar.html.erb +56 -0
  391. data/app/views/default_theme/post.html.erb +40 -0
  392. data/app/views/default_theme/post.rss.builder +12 -0
  393. data/app/views/default_theme/post_tag.html.erb +17 -0
  394. data/app/views/default_theme/post_tag.rss.builder +22 -0
  395. data/app/views/default_theme/post_type.html.erb +25 -0
  396. data/app/views/default_theme/post_type.rss.builder +22 -0
  397. data/app/views/default_theme/profile.html.erb +18 -0
  398. data/app/views/default_theme/robots.text.erb +1 -0
  399. data/app/views/default_theme/search.html.erb +13 -0
  400. data/app/views/default_theme/sitemap.html.erb +0 -0
  401. data/app/views/html_mailer/other.html.erb +3 -0
  402. data/app/views/html_mailer/sender.html.erb +25 -0
  403. data/app/views/layouts/admin.html.erb +164 -0
  404. data/app/views/layouts/admin/_ajax.html.erb +2 -0
  405. data/app/views/layouts/admin/_flash_messages.html.erb +49 -0
  406. data/app/views/layouts/admin/_form_error.html.erb +10 -0
  407. data/app/views/layouts/admin/_sidebar.html.erb +31 -0
  408. data/app/views/layouts/admin/installer.html.erb +36 -0
  409. data/app/views/layouts/application.html.erb +15 -0
  410. data/app/views/layouts/login.html.erb +40 -0
  411. data/app/views/shortcode_templates/form.html.erb +1 -0
  412. data/app/views/shortcode_templates/widget.html.erb +11 -0
  413. data/config/initializers/active_record_extension.rb +24 -0
  414. data/config/initializers/assets.rb +17 -0
  415. data/config/initializers/cache_helper.rb +9 -0
  416. data/config/initializers/elfinder.rb +79 -0
  417. data/config/initializers/fix_ssl.rb +14 -0
  418. data/config/initializers/mobu.rb +24 -0
  419. data/config/initializers/page_caching.rb +35 -0
  420. data/config/initializers/rufus_cron.rb +44 -0
  421. data/config/locales/admin/en.yml +561 -0
  422. data/config/locales/admin/es.yml +560 -0
  423. data/config/locales/ar.yml +275 -0
  424. data/config/locales/common.yml +125 -0
  425. data/config/locales/de.yml +210 -0
  426. data/config/locales/en.yml +207 -0
  427. data/config/locales/es.yml +198 -0
  428. data/config/locales/fr.yml +207 -0
  429. data/config/locales/languages.yml +19 -0
  430. data/config/locales/pl.yml +237 -0
  431. data/config/locales/pt.yml +201 -0
  432. data/config/locales/routes.yml +23 -0
  433. data/config/routes.rb +10 -0
  434. data/config/routes/admin.rb +119 -0
  435. data/config/routes/frontend.rb +35 -0
  436. data/config/sitemap.rb +52 -0
  437. data/config/system.json +16 -0
  438. data/db/migrate/20150611161134_post_table_into_utf8.rb +115 -0
  439. data/lib/ca-bundle.crt +3554 -0
  440. data/lib/camaleon_cms.rb +4 -0
  441. data/lib/camaleon_cms/engine.rb +60 -0
  442. data/lib/camaleon_cms/version.rb +3 -0
  443. data/lib/captcha/bumpyroad.ttf +0 -0
  444. data/lib/captcha/captcha_0.jpg +0 -0
  445. data/lib/captcha/captcha_1.jpg +0 -0
  446. data/lib/captcha/captcha_10.jpg +0 -0
  447. data/lib/captcha/captcha_11.jpg +0 -0
  448. data/lib/captcha/captcha_2.jpg +0 -0
  449. data/lib/captcha/captcha_3.jpg +0 -0
  450. data/lib/captcha/captcha_4.jpg +0 -0
  451. data/lib/captcha/captcha_5.jpg +0 -0
  452. data/lib/captcha/captcha_6.jpg +0 -0
  453. data/lib/captcha/captcha_7.jpg +0 -0
  454. data/lib/captcha/captcha_8.jpg +0 -0
  455. data/lib/captcha/captcha_9.jpg +0 -0
  456. data/lib/ext/aes_crypt.rb +246 -0
  457. data/lib/ext/array.rb +54 -0
  458. data/lib/ext/hash.rb +41 -0
  459. data/lib/ext/string.rb +159 -0
  460. data/lib/ext/translator.rb +83 -0
  461. data/lib/generators/camaleon_cms/install_generator.rb +20 -0
  462. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/admin_controller.rb +25 -0
  463. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/assets/docs/images/screenshot_1.jpg +0 -0
  464. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/assets/docs/images/screenshot_2.jpg +0 -0
  465. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/assets/docs/images/screenshot_3.jpg +0 -0
  466. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/assets/docs/index.html +60 -0
  467. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/attack_helper.rb +99 -0
  468. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/config/config.json +31 -0
  469. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/config/custom_models.rb +4 -0
  470. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/config/locales/readme.txt +1 -0
  471. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/config/locales/translation.yml +36 -0
  472. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/config/routes_admin.txt +2 -0
  473. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/models/attack.rb +12 -0
  474. data/lib/generators/camaleon_cms/install_template/apps/plugins/attack/views/admin/settings.html.erb +49 -0
  475. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/admin_forms_controller.rb +93 -0
  476. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/css/admin/form-builder/formbuilder.css +70 -0
  477. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/css/contact-form.css +8 -0
  478. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/css/front/railsform.scss +94 -0
  479. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/css/readme.txt +1 -0
  480. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/docs/images/screenshot_1.jpg +0 -0
  481. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/docs/images/screenshot_2.jpg +0 -0
  482. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/docs/images/screenshot_3.jpg +0 -0
  483. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/docs/images/screenshot_4.jpg +0 -0
  484. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/docs/images/screenshot_5.jpg +0 -0
  485. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/docs/index.html +89 -0
  486. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/js/contact_form.js +2 -0
  487. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/js/form-builder/formbuilder.js +1271 -0
  488. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/js/form-builder/vendor.js +3072 -0
  489. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/assets/js/readme.txt +1 -0
  490. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/config/config.json +35 -0
  491. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/config/locales/readme.txt +1 -0
  492. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/config/locales/translation.yml +210 -0
  493. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/config/routes_admin.txt +4 -0
  494. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/config/routes_front.txt +2 -0
  495. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/contact_form_helper.rb +91 -0
  496. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/contact_form_html_helper.rb +157 -0
  497. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/front_controller.rb +106 -0
  498. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/models/contact_form.rb +26 -0
  499. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/views/admin_forms/_form.html.erb +33 -0
  500. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/views/admin_forms/edit.html.erb +375 -0
  501. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/views/admin_forms/index.html.erb +65 -0
  502. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/views/admin_forms/manual.html.erb +59 -0
  503. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/views/admin_forms/responses.html.erb +67 -0
  504. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/views/forms_shorcode.html.erb +27 -0
  505. data/lib/generators/camaleon_cms/install_template/apps/plugins/contact_form/views/front/index.html.erb +3 -0
  506. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/admin_controller.rb +35 -0
  507. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/assets/docs/images/screenshot_1.jpg +0 -0
  508. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/assets/docs/images/screenshot_2.jpg +0 -0
  509. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/assets/docs/index.html +58 -0
  510. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/config/config.json +21 -0
  511. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/config/locales/readme.txt +1 -0
  512. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/config/locales/translation.yml +37 -0
  513. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/config/routes_admin.txt +3 -0
  514. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/front_cache_helper.rb +154 -0
  515. data/lib/generators/camaleon_cms/install_template/apps/plugins/front_cache/views/admin/settings.html.erb +71 -0
  516. data/lib/generators/camaleon_cms/install_template/apps/plugins/visibility_post/assets/docs/images/screenshot_1.jpg +0 -0
  517. data/lib/generators/camaleon_cms/install_template/apps/plugins/visibility_post/assets/docs/images/screenshot_2.jpg +0 -0
  518. data/lib/generators/camaleon_cms/install_template/apps/plugins/visibility_post/assets/docs/images/screenshot_3.jpg +0 -0
  519. data/lib/generators/camaleon_cms/install_template/apps/plugins/visibility_post/assets/docs/images/screenshot_4.jpg +0 -0
  520. data/lib/generators/camaleon_cms/install_template/apps/plugins/visibility_post/assets/docs/index.html +62 -0
  521. data/lib/generators/camaleon_cms/install_template/apps/plugins/visibility_post/assets/js/form.js +29 -0
  522. data/lib/generators/camaleon_cms/install_template/apps/plugins/visibility_post/config/config.json +24 -0
  523. data/lib/generators/camaleon_cms/install_template/apps/plugins/visibility_post/visibility_post_helper.rb +132 -0
  524. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/css/magnific-ver.css +394 -0
  525. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/css/main.css +15 -0
  526. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/css/style-ver.css.scss +224 -0
  527. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/css/wpbase.css +1 -0
  528. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/images/empe.jpg +0 -0
  529. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/images/image.png +0 -0
  530. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/images/loader.gif +0 -0
  531. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/images/zoom.png +0 -0
  532. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/js/hover.zoom.js +67 -0
  533. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/js/magnific.min.js +3 -0
  534. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/js/main-ver.js +46 -0
  535. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/js/main.js +18 -0
  536. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/assets/js/modernizr.custom.js +4 -0
  537. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/config/config.json +14 -0
  538. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/main_helper.rb +43 -0
  539. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/views/admin/settings.html.erb +4 -0
  540. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/views/index.html.erb +28 -0
  541. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/views/layouts/index.html.erb +85 -0
  542. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/views/partials/readme.txt +1 -0
  543. data/lib/generators/camaleon_cms/install_template/apps/themes/camaleon_first/views/template_sample_page.html.erb +6 -0
  544. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/css/main.css +13 -0
  545. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/css/style.css.scss +3395 -0
  546. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/css/styles.css.scss +175 -0
  547. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/genericons/COPYING.txt +9 -0
  548. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/genericons/Genericons-Regular.otf +0 -0
  549. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/genericons/LICENSE.txt +339 -0
  550. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/genericons/README.txt +123 -0
  551. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/genericons/font/genericons-regular-webfont.eot +0 -0
  552. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/genericons/font/genericons-regular-webfont.svg +135 -0
  553. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/genericons/font/genericons-regular-webfont.ttf +0 -0
  554. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/genericons/font/genericons-regular-webfont.woff +0 -0
  555. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/genericons/genericons.css +197 -0
  556. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/circle.png +0 -0
  557. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/dotted-line-2x.png +0 -0
  558. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/dotted-line-light-2x.png +0 -0
  559. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/dotted-line-light.png +0 -0
  560. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/dotted-line.png +0 -0
  561. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/headers/circle-thumbnail.png +0 -0
  562. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/headers/circle.png +0 -0
  563. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/headers/diamond-thumbnail.png +0 -0
  564. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/headers/diamond.png +0 -0
  565. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/headers/star-thumbnail.png +0 -0
  566. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/headers/star.png +0 -0
  567. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/image.png +0 -0
  568. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/search-icon-2x.png +0 -0
  569. data/lib/generators/camaleon_cms/install_template/apps/themes/default/assets/images/search-icon.png +0 -0
  570. data/lib/generators/camaleon_cms/install_template/apps/themes/default/config/config.json +19 -0
  571. data/lib/generators/camaleon_cms/install_template/apps/themes/default/default_helper.rb +36 -0
  572. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/admin/settings.html.erb +1 -0
  573. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/category.html.erb +16 -0
  574. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/index.html.erb +35 -0
  575. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/layouts/index.html.erb +83 -0
  576. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/page.html.erb +22 -0
  577. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/partials/_breadcrumb.html.erb +10 -0
  578. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/partials/_categories_list.html.erb +23 -0
  579. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/partials/_comments.html.erb +25 -0
  580. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/partials/_comments_list.html.erb +20 -0
  581. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/partials/_flash_messages.html.erb +26 -0
  582. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/partials/_forms.html.erb +19 -0
  583. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/partials/_post_list_item.html.erb +34 -0
  584. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/partials/_search_form.html.erb +8 -0
  585. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/partials/_sidebar.html.erb +50 -0
  586. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/post.html.erb +22 -0
  587. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/post_tag.html.erb +14 -0
  588. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/post_type.html.erb +33 -0
  589. data/lib/generators/camaleon_cms/install_template/apps/themes/default/views/search.html.erb +17 -0
  590. data/lib/generators/camaleon_cms/install_template/apps/themes/new/assets/css/bootstrap/bootstrap.min.css +6735 -0
  591. data/lib/generators/camaleon_cms/install_template/apps/themes/new/assets/css/main.css +13 -0
  592. data/lib/generators/camaleon_cms/install_template/apps/themes/new/assets/css/styles.css.scss +175 -0
  593. data/lib/generators/camaleon_cms/install_template/apps/themes/new/assets/images/image.png +0 -0
  594. data/lib/generators/camaleon_cms/install_template/apps/themes/new/assets/js/main.js +15 -0
  595. data/lib/generators/camaleon_cms/install_template/apps/themes/new/assets/js/plugins/bootstrap/bootstrap.min.js +6 -0
  596. data/lib/generators/camaleon_cms/install_template/apps/themes/new/assets/js/plugins/jquery/jquery.min.js +4 -0
  597. data/lib/generators/camaleon_cms/install_template/apps/themes/new/assets/js/plugins/modal_elfinder.js +29 -0
  598. data/lib/generators/camaleon_cms/install_template/apps/themes/new/config/config.json +26 -0
  599. data/lib/generators/camaleon_cms/install_template/apps/themes/new/custom_helper.rb +37 -0
  600. data/lib/generators/camaleon_cms/install_template/apps/themes/new/data.json +1 -0
  601. data/lib/generators/camaleon_cms/install_template/apps/themes/new/image.png +0 -0
  602. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/admin/settings.html.erb +1 -0
  603. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/category.html.erb +33 -0
  604. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/index.html.erb +70 -0
  605. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/layouts/_footer.html.erb +18 -0
  606. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/layouts/_header.html.erb +38 -0
  607. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/layouts/index.html.erb +34 -0
  608. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/page.html.erb +24 -0
  609. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/partials/_breadcrumb.html.erb +10 -0
  610. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/partials/_categories_list.html.erb +23 -0
  611. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/partials/_comments.html.erb +32 -0
  612. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/partials/_comments_list.html.erb +21 -0
  613. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/partials/_flash_messages.html.erb +26 -0
  614. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/partials/_forms.html.erb +19 -0
  615. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/partials/_post_list_item.html.erb +13 -0
  616. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/partials/_search_form.html.erb +10 -0
  617. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/partials/_sidebar.html.erb +60 -0
  618. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/post.html.erb +20 -0
  619. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/post_tag.html.erb +22 -0
  620. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/post_type.html.erb +33 -0
  621. data/lib/generators/camaleon_cms/install_template/apps/themes/new/views/search.html.erb +18 -0
  622. data/lib/generators/camaleon_cms/install_template/plugin_routes.rb +311 -0
  623. data/lib/generators/camaleon_cms/install_template/system.json +11 -0
  624. data/lib/generators/cplugin_generator.rb +80 -0
  625. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/admin_controller.rb +9 -0
  626. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/assets/css/readme.txt +1 -0
  627. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/assets/images/readme.txt +1 -0
  628. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/assets/js/readme.txt +1 -0
  629. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/config/config.json +18 -0
  630. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/config/locales/readme.txt +1 -0
  631. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/config/routes_admin.txt +1 -0
  632. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/config/routes_front.txt +1 -0
  633. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/front_controller.rb +9 -0
  634. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/main_helper.rb +21 -0
  635. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/models/my_plugin.rb +12 -0
  636. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/views/admin/index.html.erb +2 -0
  637. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/views/front/index.html.erb +2 -0
  638. data/lib/generators/cplugin_template/app/apps/plugins/my_plugin/views/layouts/readme.txt +2 -0
  639. data/lib/generators/ctheme_generator.rb +63 -0
  640. data/lib/generators/ctheme_template/app/apps/themes/my_theme/assets/css/main.css +12 -0
  641. data/lib/generators/ctheme_template/app/apps/themes/my_theme/assets/images/image.png +0 -0
  642. data/lib/generators/ctheme_template/app/apps/themes/my_theme/assets/js/main.js +14 -0
  643. data/lib/generators/ctheme_template/app/apps/themes/my_theme/config/config.json +14 -0
  644. data/lib/generators/ctheme_template/app/apps/themes/my_theme/main_helper.rb +25 -0
  645. data/lib/generators/ctheme_template/app/apps/themes/my_theme/views/admin/settings.html.erb +4 -0
  646. data/lib/generators/ctheme_template/app/apps/themes/my_theme/views/index.html.erb +25 -0
  647. data/lib/generators/ctheme_template/app/apps/themes/my_theme/views/layouts/index.html.erb +71 -0
  648. data/lib/generators/ctheme_template/app/apps/themes/my_theme/views/partials/readme.txt +1 -0
  649. data/lib/tasks/camaleon_cms_tasks.rake +4 -0
  650. data/test/camaleon_cms_test.rb +7 -0
  651. data/test/integration/navigation_test.rb +10 -0
  652. data/test/test_helper.rb +19 -0
  653. metadata +699 -0
@@ -0,0 +1,3072 @@
1
+ var Node = Node || {
2
+ ELEMENT_NODE: 1,
3
+ ATTRIBUTE_NODE: 2,
4
+ TEXT_NODE: 3
5
+ };
6
+
7
+
8
+ $.scrollWindowTo = function(pos, duration, cb) {
9
+ if (duration == null) {
10
+ duration = 0;
11
+ }
12
+ if (pos === $(window).scrollTop()) {
13
+ $(window).trigger('scroll');
14
+ if (typeof cb === "function") {
15
+ cb();
16
+ }
17
+ return;
18
+ }
19
+ return $('html, body').animate({
20
+ scrollTop: pos
21
+ }, duration, function() {
22
+ return typeof cb === "function" ? cb() : void 0;
23
+ });
24
+ };
25
+
26
+ (function() {
27
+ var arrays, basicObjects, deepClone, deepExtend, deepExtendCouple, isBasicObject,
28
+ __slice = [].slice;
29
+
30
+ deepClone = function(obj) {
31
+ var func, isArr;
32
+ if (!_.isObject(obj || _.isFunction(obj))) {
33
+ return obj;
34
+ }
35
+ if (_.isDate(obj)) {
36
+ return new Date(obj.getTime());
37
+ }
38
+ if (_.isRegExp(obj)) {
39
+ return new RegExp(obj.source, obj.toString().replace(/.*\//, ""));
40
+ }
41
+ isArr = _.isArray(obj || _.isArguments(obj));
42
+ func = function(memo, value, key) {
43
+ if (isArr) {
44
+ memo.push(deepClone(value));
45
+ } else {
46
+ memo[key] = deepClone(value);
47
+ }
48
+ return memo;
49
+ };
50
+ return _.reduce(obj, func, isArr ? [] : {});
51
+ };
52
+
53
+ isBasicObject = function(object) {
54
+ if(object === null) return false;
55
+ return (object.prototype === {}.prototype || object.prototype === Object.prototype) && _.isObject(object) && !_.isArray(object) && !_.isFunction(object) && !_.isDate(object) && !_.isRegExp(object) && !_.isArguments(object);
56
+ };
57
+
58
+ basicObjects = function(object) {
59
+ return _.filter(_.keys(object), function(key) {
60
+ return isBasicObject(object[key]);
61
+ });
62
+ };
63
+
64
+ arrays = function(object) {
65
+ return _.filter(_.keys(object), function(key) {
66
+ return _.isArray(object[key]);
67
+ });
68
+ };
69
+
70
+ deepExtendCouple = function(destination, source, maxDepth) {
71
+ var combine, recurse, sharedArrayKey, sharedArrayKeys, sharedObjectKey, sharedObjectKeys, _i, _j, _len, _len1;
72
+ if (maxDepth == null) {
73
+ maxDepth = 20;
74
+ }
75
+ if (maxDepth <= 0) {
76
+ console.warn('_.deepExtend(): Maximum depth of recursion hit.');
77
+ return _.extend(destination, source);
78
+ }
79
+ sharedObjectKeys = _.intersection(basicObjects(destination), basicObjects(source));
80
+ recurse = function(key) {
81
+ return source[key] = deepExtendCouple(destination[key], source[key], maxDepth - 1);
82
+ };
83
+ for (_i = 0, _len = sharedObjectKeys.length; _i < _len; _i++) {
84
+ sharedObjectKey = sharedObjectKeys[_i];
85
+ recurse(sharedObjectKey);
86
+ }
87
+ sharedArrayKeys = _.intersection(arrays(destination), arrays(source));
88
+ combine = function(key) {
89
+ return source[key] = _.union(destination[key], source[key]);
90
+ };
91
+ for (_j = 0, _len1 = sharedArrayKeys.length; _j < _len1; _j++) {
92
+ sharedArrayKey = sharedArrayKeys[_j];
93
+ combine(sharedArrayKey);
94
+ }
95
+ return _.extend(destination, source);
96
+ };
97
+
98
+ deepExtend = function() {
99
+ var finalObj, maxDepth, objects, _i;
100
+ objects = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), maxDepth = arguments[_i++];
101
+ if (!_.isNumber(maxDepth)) {
102
+ objects.push(maxDepth);
103
+ maxDepth = 20;
104
+ }
105
+ if (objects.length <= 1) {
106
+ return objects[0];
107
+ }
108
+ if (maxDepth <= 0) {
109
+ return _.extend.apply(this, objects);
110
+ }
111
+ finalObj = objects.shift();
112
+ while (objects.length > 0) {
113
+ finalObj = deepExtendCouple(finalObj, deepClone(objects.shift()), maxDepth);
114
+ }
115
+ return finalObj;
116
+ };
117
+
118
+ _.mixin({
119
+ deepClone: deepClone,
120
+ isBasicObject: isBasicObject,
121
+ basicObjects: basicObjects,
122
+ arrays: arrays,
123
+ deepExtend: deepExtend
124
+ });
125
+
126
+ }).call(this);
127
+ // Rivets.js
128
+ // version: 0.5.13
129
+ // author: Michael Richards
130
+ // license: MIT
131
+ (function() {
132
+ var Rivets, jQuery,
133
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
134
+ __slice = [].slice,
135
+ __hasProp = {}.hasOwnProperty,
136
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
137
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
138
+
139
+ Rivets = {};
140
+
141
+ jQuery = window.jQuery || window.Zepto;
142
+
143
+ if (!String.prototype.trim) {
144
+ String.prototype.trim = function() {
145
+ return this.replace(/^\s+|\s+$/g, '');
146
+ };
147
+ }
148
+
149
+ Rivets.Binding = (function() {
150
+ function Binding(view, el, type, key, keypath, options) {
151
+ var identifier, regexp, value, _ref;
152
+ this.view = view;
153
+ this.el = el;
154
+ this.type = type;
155
+ this.key = key;
156
+ this.keypath = keypath;
157
+ this.options = options != null ? options : {};
158
+ this.update = __bind(this.update, this);
159
+ this.unbind = __bind(this.unbind, this);
160
+ this.bind = __bind(this.bind, this);
161
+ this.publish = __bind(this.publish, this);
162
+ this.sync = __bind(this.sync, this);
163
+ this.set = __bind(this.set, this);
164
+ this.eventHandler = __bind(this.eventHandler, this);
165
+ this.formattedValue = __bind(this.formattedValue, this);
166
+ if (!(this.binder = this.view.binders[type])) {
167
+ _ref = this.view.binders;
168
+ for (identifier in _ref) {
169
+ value = _ref[identifier];
170
+ if (identifier !== '*' && identifier.indexOf('*') !== -1) {
171
+ regexp = new RegExp("^" + (identifier.replace('*', '.+')) + "$");
172
+ if (regexp.test(type)) {
173
+ this.binder = value;
174
+ this.args = new RegExp("^" + (identifier.replace('*', '(.+)')) + "$").exec(type);
175
+ this.args.shift();
176
+ }
177
+ }
178
+ }
179
+ }
180
+ this.binder || (this.binder = this.view.binders['*']);
181
+ if (this.binder instanceof Function) {
182
+ this.binder = {
183
+ routine: this.binder
184
+ };
185
+ }
186
+ this.formatters = this.options.formatters || [];
187
+ this.model = this.key ? this.view.models[this.key] : this.view.models;
188
+ }
189
+
190
+ Binding.prototype.formattedValue = function(value) {
191
+ var args, formatter, id, _i, _len, _ref;
192
+ _ref = this.formatters;
193
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
194
+ formatter = _ref[_i];
195
+ args = formatter.split(/\s+/);
196
+ id = args.shift();
197
+ formatter = this.model[id] instanceof Function ? this.model[id] : this.view.formatters[id];
198
+ if ((formatter != null ? formatter.read : void 0) instanceof Function) {
199
+ value = formatter.read.apply(formatter, [value].concat(__slice.call(args)));
200
+ } else if (formatter instanceof Function) {
201
+ value = formatter.apply(null, [value].concat(__slice.call(args)));
202
+ }
203
+ }
204
+ return value;
205
+ };
206
+
207
+ Binding.prototype.eventHandler = function(fn) {
208
+ var binding, handler;
209
+ handler = (binding = this).view.config.handler;
210
+ return function(ev) {
211
+ return handler.call(fn, this, ev, binding);
212
+ };
213
+ };
214
+
215
+ Binding.prototype.set = function(value) {
216
+ var _ref;
217
+ value = value instanceof Function && !this.binder["function"] ? this.formattedValue(value.call(this.model)) : this.formattedValue(value);
218
+ return (_ref = this.binder.routine) != null ? _ref.call(this, this.el, value) : void 0;
219
+ };
220
+
221
+ Binding.prototype.sync = function() {
222
+ return this.set(this.options.bypass ? this.model[this.keypath] : this.view.config.adapter.read(this.model, this.keypath));
223
+ };
224
+
225
+ Binding.prototype.publish = function() {
226
+ var args, formatter, id, value, _i, _len, _ref, _ref1, _ref2;
227
+ value = Rivets.Util.getInputValue(this.el);
228
+ _ref = this.formatters.slice(0).reverse();
229
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
230
+ formatter = _ref[_i];
231
+ args = formatter.split(/\s+/);
232
+ id = args.shift();
233
+ if ((_ref1 = this.view.formatters[id]) != null ? _ref1.publish : void 0) {
234
+ value = (_ref2 = this.view.formatters[id]).publish.apply(_ref2, [value].concat(__slice.call(args)));
235
+ }
236
+ }
237
+ return this.view.config.adapter.publish(this.model, this.keypath, value);
238
+ };
239
+
240
+ Binding.prototype.bind = function() {
241
+ var dependency, keypath, model, _i, _len, _ref, _ref1, _ref2, _results;
242
+ if ((_ref = this.binder.bind) != null) {
243
+ _ref.call(this, this.el);
244
+ }
245
+ if (this.options.bypass) {
246
+ this.sync();
247
+ } else {
248
+ this.view.config.adapter.subscribe(this.model, this.keypath, this.sync);
249
+ if (this.view.config.preloadData) {
250
+ this.sync();
251
+ }
252
+ }
253
+ if ((_ref1 = this.options.dependencies) != null ? _ref1.length : void 0) {
254
+ _ref2 = this.options.dependencies;
255
+ _results = [];
256
+ for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
257
+ dependency = _ref2[_i];
258
+ if (/^\./.test(dependency)) {
259
+ model = this.model;
260
+ keypath = dependency.substr(1);
261
+ } else {
262
+ dependency = dependency.split('.');
263
+ model = this.view.models[dependency.shift()];
264
+ keypath = dependency.join('.');
265
+ }
266
+ _results.push(this.view.config.adapter.subscribe(model, keypath, this.sync));
267
+ }
268
+ return _results;
269
+ }
270
+ };
271
+
272
+ Binding.prototype.unbind = function() {
273
+ var dependency, keypath, model, _i, _len, _ref, _ref1, _ref2, _results;
274
+ if ((_ref = this.binder.unbind) != null) {
275
+ _ref.call(this, this.el);
276
+ }
277
+ if (!this.options.bypass) {
278
+ this.view.config.adapter.unsubscribe(this.model, this.keypath, this.sync);
279
+ }
280
+ if ((_ref1 = this.options.dependencies) != null ? _ref1.length : void 0) {
281
+ _ref2 = this.options.dependencies;
282
+ _results = [];
283
+ for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
284
+ dependency = _ref2[_i];
285
+ if (/^\./.test(dependency)) {
286
+ model = this.model;
287
+ keypath = dependency.substr(1);
288
+ } else {
289
+ dependency = dependency.split('.');
290
+ model = this.view.models[dependency.shift()];
291
+ keypath = dependency.join('.');
292
+ }
293
+ _results.push(this.view.config.adapter.unsubscribe(model, keypath, this.sync));
294
+ }
295
+ return _results;
296
+ }
297
+ };
298
+
299
+ Binding.prototype.update = function(models) {
300
+ var _ref;
301
+ if (models == null) {
302
+ models = {};
303
+ }
304
+ if (this.key) {
305
+ if (models[this.key]) {
306
+ if (!this.options.bypass) {
307
+ this.view.config.adapter.unsubscribe(this.model, this.keypath, this.sync);
308
+ }
309
+ this.model = models[this.key];
310
+ if (this.options.bypass) {
311
+ this.sync();
312
+ } else {
313
+ this.view.config.adapter.subscribe(this.model, this.keypath, this.sync);
314
+ if (this.view.config.preloadData) {
315
+ this.sync();
316
+ }
317
+ }
318
+ }
319
+ } else {
320
+ this.sync();
321
+ }
322
+ return (_ref = this.binder.update) != null ? _ref.call(this, models) : void 0;
323
+ };
324
+
325
+ return Binding;
326
+
327
+ })();
328
+
329
+ Rivets.ComponentBinding = (function(_super) {
330
+ __extends(ComponentBinding, _super);
331
+
332
+ function ComponentBinding(view, el, type) {
333
+ var attribute, _i, _len, _ref, _ref1;
334
+ this.view = view;
335
+ this.el = el;
336
+ this.type = type;
337
+ this.unbind = __bind(this.unbind, this);
338
+ this.bind = __bind(this.bind, this);
339
+ this.update = __bind(this.update, this);
340
+ this.locals = __bind(this.locals, this);
341
+ this.component = Rivets.components[this.type];
342
+ this.attributes = {};
343
+ this.inflections = {};
344
+ _ref = this.el.attributes || [];
345
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
346
+ attribute = _ref[_i];
347
+ if (_ref1 = attribute.name, __indexOf.call(this.component.attributes, _ref1) >= 0) {
348
+ this.attributes[attribute.name] = attribute.value;
349
+ } else {
350
+ this.inflections[attribute.name] = attribute.value;
351
+ }
352
+ }
353
+ }
354
+
355
+ ComponentBinding.prototype.sync = function() {};
356
+
357
+ ComponentBinding.prototype.locals = function(models) {
358
+ var inverse, key, model, path, result, _i, _len, _ref, _ref1;
359
+ if (models == null) {
360
+ models = this.view.models;
361
+ }
362
+ result = {};
363
+ _ref = this.inflections;
364
+ for (key in _ref) {
365
+ inverse = _ref[key];
366
+ _ref1 = inverse.split('.');
367
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
368
+ path = _ref1[_i];
369
+ result[key] = (result[key] || models)[path];
370
+ }
371
+ }
372
+ for (key in models) {
373
+ model = models[key];
374
+ if (result[key] == null) {
375
+ result[key] = model;
376
+ }
377
+ }
378
+ return result;
379
+ };
380
+
381
+ ComponentBinding.prototype.update = function(models) {
382
+ var _ref;
383
+ return (_ref = this.componentView) != null ? _ref.update(this.locals(models)) : void 0;
384
+ };
385
+
386
+ ComponentBinding.prototype.bind = function() {
387
+ var el, _ref;
388
+ if (this.componentView != null) {
389
+ return (_ref = this.componentView) != null ? _ref.bind() : void 0;
390
+ } else {
391
+ el = this.component.build.call(this.attributes);
392
+ (this.componentView = new Rivets.View(el, this.locals(), this.view.options)).bind();
393
+ return this.el.parentNode.replaceChild(el, this.el);
394
+ }
395
+ };
396
+
397
+ ComponentBinding.prototype.unbind = function() {
398
+ var _ref;
399
+ return (_ref = this.componentView) != null ? _ref.unbind() : void 0;
400
+ };
401
+
402
+ return ComponentBinding;
403
+
404
+ })(Rivets.Binding);
405
+
406
+ Rivets.TextBinding = (function(_super) {
407
+ __extends(TextBinding, _super);
408
+
409
+ function TextBinding(view, el, type, key, keypath, options) {
410
+ this.view = view;
411
+ this.el = el;
412
+ this.type = type;
413
+ this.key = key;
414
+ this.keypath = keypath;
415
+ this.options = options != null ? options : {};
416
+ this.sync = __bind(this.sync, this);
417
+ this.formatters = this.options.formatters || [];
418
+ this.model = this.key ? this.view.models[this.key] : this.view.models;
419
+ }
420
+
421
+ TextBinding.prototype.binder = {
422
+ routine: function(node, value) {
423
+ return node.data = value != null ? value : '';
424
+ }
425
+ };
426
+
427
+ TextBinding.prototype.sync = function() {
428
+ return TextBinding.__super__.sync.apply(this, arguments);
429
+ };
430
+
431
+ return TextBinding;
432
+
433
+ })(Rivets.Binding);
434
+
435
+ Rivets.View = (function() {
436
+ function View(els, models, options) {
437
+ var k, option, v, _base, _i, _len, _ref, _ref1, _ref2;
438
+ this.els = els;
439
+ this.models = models;
440
+ this.options = options != null ? options : {};
441
+ this.update = __bind(this.update, this);
442
+ this.publish = __bind(this.publish, this);
443
+ this.sync = __bind(this.sync, this);
444
+ this.unbind = __bind(this.unbind, this);
445
+ this.bind = __bind(this.bind, this);
446
+ this.select = __bind(this.select, this);
447
+ this.build = __bind(this.build, this);
448
+ this.componentRegExp = __bind(this.componentRegExp, this);
449
+ this.bindingRegExp = __bind(this.bindingRegExp, this);
450
+ if (typeof this.els.length === 'undefined') {
451
+ this.els = [this.els];
452
+ }
453
+ _ref = ['config', 'binders', 'formatters'];
454
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
455
+ option = _ref[_i];
456
+ this[option] = {};
457
+ if (this.options[option]) {
458
+ _ref1 = this.options[option];
459
+ for (k in _ref1) {
460
+ v = _ref1[k];
461
+ this[option][k] = v;
462
+ }
463
+ }
464
+ _ref2 = Rivets[option];
465
+ for (k in _ref2) {
466
+ v = _ref2[k];
467
+ if ((_base = this[option])[k] == null) {
468
+ _base[k] = v;
469
+ }
470
+ }
471
+ }
472
+ this.build();
473
+ }
474
+
475
+ View.prototype.bindingRegExp = function() {
476
+ var prefix;
477
+ prefix = this.config.prefix;
478
+ if (prefix) {
479
+ return new RegExp("^data-" + prefix + "-");
480
+ } else {
481
+ return /^data-/;
482
+ }
483
+ };
484
+
485
+ View.prototype.componentRegExp = function() {
486
+ var _ref, _ref1;
487
+ return new RegExp("^" + ((_ref = (_ref1 = this.config.prefix) != null ? _ref1.toUpperCase() : void 0) != null ? _ref : 'RV') + "-");
488
+ };
489
+
490
+ View.prototype.build = function() {
491
+ var bindingRegExp, buildBinding, componentRegExp, el, parse, skipNodes, _i, _len, _ref,
492
+ _this = this;
493
+ this.bindings = [];
494
+ skipNodes = [];
495
+ bindingRegExp = this.bindingRegExp();
496
+ componentRegExp = this.componentRegExp();
497
+ buildBinding = function(binding, node, type, declaration) {
498
+ var context, ctx, dependencies, key, keypath, options, path, pipe, pipes, splitPath;
499
+ options = {};
500
+ pipes = (function() {
501
+ var _i, _len, _ref, _results;
502
+ _ref = declaration.split('|');
503
+ _results = [];
504
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
505
+ pipe = _ref[_i];
506
+ _results.push(pipe.trim());
507
+ }
508
+ return _results;
509
+ })();
510
+ context = (function() {
511
+ var _i, _len, _ref, _results;
512
+ _ref = pipes.shift().split('<');
513
+ _results = [];
514
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
515
+ ctx = _ref[_i];
516
+ _results.push(ctx.trim());
517
+ }
518
+ return _results;
519
+ })();
520
+ path = context.shift();
521
+ splitPath = path.split(/\.|:/);
522
+ options.formatters = pipes;
523
+ options.bypass = path.indexOf(':') !== -1;
524
+ if (splitPath[0]) {
525
+ key = splitPath.shift();
526
+ } else {
527
+ key = null;
528
+ splitPath.shift();
529
+ }
530
+ keypath = splitPath.join('.');
531
+ if (dependencies = context.shift()) {
532
+ options.dependencies = dependencies.split(/\s+/);
533
+ }
534
+ return _this.bindings.push(new Rivets[binding](_this, node, type, key, keypath, options));
535
+ };
536
+ parse = function(node) {
537
+ var attribute, attributes, binder, childNode, delimiters, identifier, n, parser, regexp, restTokens, startToken, text, token, tokens, type, value, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4, _results;
538
+ if (__indexOf.call(skipNodes, node) < 0) {
539
+ if (node.nodeType === Node.TEXT_NODE) {
540
+ parser = Rivets.TextTemplateParser;
541
+ if (delimiters = _this.config.templateDelimiters) {
542
+ if ((tokens = parser.parse(node.data, delimiters)).length) {
543
+ if (!(tokens.length === 1 && tokens[0].type === parser.types.text)) {
544
+ startToken = tokens[0], restTokens = 2 <= tokens.length ? __slice.call(tokens, 1) : [];
545
+ node.data = startToken.value;
546
+ if (startToken.type === 0) {
547
+ node.data = startToken.value;
548
+ } else {
549
+ buildBinding('TextBinding', node, null, startToken.value);
550
+ }
551
+ for (_i = 0, _len = restTokens.length; _i < _len; _i++) {
552
+ token = restTokens[_i];
553
+ text = document.createTextNode(token.value);
554
+ node.parentNode.appendChild(text);
555
+ if (token.type === 1) {
556
+ buildBinding('TextBinding', text, null, token.value);
557
+ }
558
+ }
559
+ }
560
+ }
561
+ }
562
+ } else if (componentRegExp.test(node.tagName)) {
563
+ type = node.tagName.replace(componentRegExp, '').toLowerCase();
564
+ _this.bindings.push(new Rivets.ComponentBinding(_this, node, type));
565
+ } else if (node.attributes != null) {
566
+ _ref = node.attributes;
567
+ for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
568
+ attribute = _ref[_j];
569
+ if (bindingRegExp.test(attribute.name)) {
570
+ type = attribute.name.replace(bindingRegExp, '');
571
+ if (!(binder = _this.binders[type])) {
572
+ _ref1 = _this.binders;
573
+ for (identifier in _ref1) {
574
+ value = _ref1[identifier];
575
+ if (identifier !== '*' && identifier.indexOf('*') !== -1) {
576
+ regexp = new RegExp("^" + (identifier.replace('*', '.+')) + "$");
577
+ if (regexp.test(type)) {
578
+ binder = value;
579
+ }
580
+ }
581
+ }
582
+ }
583
+ binder || (binder = _this.binders['*']);
584
+ if (binder.block) {
585
+ _ref2 = node.childNodes;
586
+ for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
587
+ n = _ref2[_k];
588
+ skipNodes.push(n);
589
+ }
590
+ attributes = [attribute];
591
+ }
592
+ }
593
+ }
594
+ _ref3 = attributes || node.attributes;
595
+ for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
596
+ attribute = _ref3[_l];
597
+ if (bindingRegExp.test(attribute.name)) {
598
+ type = attribute.name.replace(bindingRegExp, '');
599
+ buildBinding('Binding', node, type, attribute.value);
600
+ }
601
+ }
602
+ }
603
+ _ref4 = node.childNodes;
604
+ _results = [];
605
+ for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) {
606
+ childNode = _ref4[_m];
607
+ _results.push(parse(childNode));
608
+ }
609
+ return _results;
610
+ }
611
+ };
612
+ _ref = this.els;
613
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
614
+ el = _ref[_i];
615
+ parse(el);
616
+ }
617
+ };
618
+
619
+ View.prototype.select = function(fn) {
620
+ var binding, _i, _len, _ref, _results;
621
+ _ref = this.bindings;
622
+ _results = [];
623
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
624
+ binding = _ref[_i];
625
+ if (fn(binding)) {
626
+ _results.push(binding);
627
+ }
628
+ }
629
+ return _results;
630
+ };
631
+
632
+ View.prototype.bind = function() {
633
+ var binding, _i, _len, _ref, _results;
634
+ _ref = this.bindings;
635
+ _results = [];
636
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
637
+ binding = _ref[_i];
638
+ _results.push(binding.bind());
639
+ }
640
+ return _results;
641
+ };
642
+
643
+ View.prototype.unbind = function() {
644
+ var binding, _i, _len, _ref, _results;
645
+ _ref = this.bindings;
646
+ _results = [];
647
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
648
+ binding = _ref[_i];
649
+ _results.push(binding.unbind());
650
+ }
651
+ return _results;
652
+ };
653
+
654
+ View.prototype.sync = function() {
655
+ var binding, _i, _len, _ref, _results;
656
+ _ref = this.bindings;
657
+ _results = [];
658
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
659
+ binding = _ref[_i];
660
+ _results.push(binding.sync());
661
+ }
662
+ return _results;
663
+ };
664
+
665
+ View.prototype.publish = function() {
666
+ var binding, _i, _len, _ref, _results;
667
+ _ref = this.select(function(b) {
668
+ return b.binder.publishes;
669
+ });
670
+ _results = [];
671
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
672
+ binding = _ref[_i];
673
+ _results.push(binding.publish());
674
+ }
675
+ return _results;
676
+ };
677
+
678
+ View.prototype.update = function(models) {
679
+ var binding, key, model, _i, _len, _ref, _results;
680
+ if (models == null) {
681
+ models = {};
682
+ }
683
+ for (key in models) {
684
+ model = models[key];
685
+ this.models[key] = model;
686
+ }
687
+ _ref = this.bindings;
688
+ _results = [];
689
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
690
+ binding = _ref[_i];
691
+ _results.push(binding.update(models));
692
+ }
693
+ return _results;
694
+ };
695
+
696
+ return View;
697
+
698
+ })();
699
+
700
+ Rivets.TextTemplateParser = (function() {
701
+ function TextTemplateParser() {}
702
+
703
+ TextTemplateParser.types = {
704
+ text: 0,
705
+ binding: 1
706
+ };
707
+
708
+ TextTemplateParser.parse = function(template, delimiters) {
709
+ var index, lastIndex, lastToken, length, substring, tokens, value;
710
+ tokens = [];
711
+ length = template.length;
712
+ index = 0;
713
+ lastIndex = 0;
714
+ while (lastIndex < length) {
715
+ index = template.indexOf(delimiters[0], lastIndex);
716
+ if (index < 0) {
717
+ tokens.push({
718
+ type: this.types.text,
719
+ value: template.slice(lastIndex)
720
+ });
721
+ break;
722
+ } else {
723
+ if (index > 0 && lastIndex < index) {
724
+ tokens.push({
725
+ type: this.types.text,
726
+ value: template.slice(lastIndex, index)
727
+ });
728
+ }
729
+ lastIndex = index + 2;
730
+ index = template.indexOf(delimiters[1], lastIndex);
731
+ if (index < 0) {
732
+ substring = template.slice(lastIndex - 2);
733
+ lastToken = tokens[tokens.length - 1];
734
+ if ((lastToken != null ? lastToken.type : void 0) === this.types.text) {
735
+ lastToken.value += substring;
736
+ } else {
737
+ tokens.push({
738
+ type: this.types.text,
739
+ value: substring
740
+ });
741
+ }
742
+ break;
743
+ }
744
+ value = template.slice(lastIndex, index).trim();
745
+ tokens.push({
746
+ type: this.types.binding,
747
+ value: value
748
+ });
749
+ lastIndex = index + 2;
750
+ }
751
+ }
752
+ return tokens;
753
+ };
754
+
755
+ return TextTemplateParser;
756
+
757
+ })();
758
+
759
+ Rivets.Util = {
760
+ bindEvent: function(el, event, handler) {
761
+ if (window.jQuery != null) {
762
+ el = jQuery(el);
763
+ if (el.on != null) {
764
+ return el.on(event, handler);
765
+ } else {
766
+ return el.bind(event, handler);
767
+ }
768
+ } else if (window.addEventListener != null) {
769
+ return el.addEventListener(event, handler, false);
770
+ } else {
771
+ event = 'on' + event;
772
+ return el.attachEvent(event, handler);
773
+ }
774
+ },
775
+ unbindEvent: function(el, event, handler) {
776
+ if (window.jQuery != null) {
777
+ el = jQuery(el);
778
+ if (el.off != null) {
779
+ return el.off(event, handler);
780
+ } else {
781
+ return el.unbind(event, handler);
782
+ }
783
+ } else if (window.removeEventListener != null) {
784
+ return el.removeEventListener(event, handler, false);
785
+ } else {
786
+ event = 'on' + event;
787
+ return el.detachEvent(event, handler);
788
+ }
789
+ },
790
+ getInputValue: function(el) {
791
+ var o, _i, _len, _results;
792
+ if (window.jQuery != null) {
793
+ el = jQuery(el);
794
+ switch (el[0].type) {
795
+ case 'checkbox':
796
+ return el.is(':checked');
797
+ default:
798
+ return el.val();
799
+ }
800
+ } else {
801
+ switch (el.type) {
802
+ case 'checkbox':
803
+ return el.checked;
804
+ case 'select-multiple':
805
+ _results = [];
806
+ for (_i = 0, _len = el.length; _i < _len; _i++) {
807
+ o = el[_i];
808
+ if (o.selected) {
809
+ _results.push(o.value);
810
+ }
811
+ }
812
+ return _results;
813
+ break;
814
+ default:
815
+ return el.value;
816
+ }
817
+ }
818
+ }
819
+ };
820
+
821
+ Rivets.binders = {
822
+ enabled: function(el, value) {
823
+ return el.disabled = !value;
824
+ },
825
+ disabled: function(el, value) {
826
+ return el.disabled = !!value;
827
+ },
828
+ checked: {
829
+ publishes: true,
830
+ bind: function(el) {
831
+ return Rivets.Util.bindEvent(el, 'change', this.publish);
832
+ },
833
+ unbind: function(el) {
834
+ return Rivets.Util.unbindEvent(el, 'change', this.publish);
835
+ },
836
+ routine: function(el, value) {
837
+ var _ref;
838
+ if (el.type === 'radio') {
839
+ return el.checked = ((_ref = el.value) != null ? _ref.toString() : void 0) === (value != null ? value.toString() : void 0);
840
+ } else {
841
+ return el.checked = !!value;
842
+ }
843
+ }
844
+ },
845
+ unchecked: {
846
+ publishes: true,
847
+ bind: function(el) {
848
+ return Rivets.Util.bindEvent(el, 'change', this.publish);
849
+ },
850
+ unbind: function(el) {
851
+ return Rivets.Util.unbindEvent(el, 'change', this.publish);
852
+ },
853
+ routine: function(el, value) {
854
+ var _ref;
855
+ if (el.type === 'radio') {
856
+ return el.checked = ((_ref = el.value) != null ? _ref.toString() : void 0) !== (value != null ? value.toString() : void 0);
857
+ } else {
858
+ return el.checked = !value;
859
+ }
860
+ }
861
+ },
862
+ show: function(el, value) {
863
+ return el.style.display = value ? '' : 'none';
864
+ },
865
+ hide: function(el, value) {
866
+ return el.style.display = value ? 'none' : '';
867
+ },
868
+ html: function(el, value) {
869
+ return el.innerHTML = value != null ? value : '';
870
+ },
871
+ value: {
872
+ publishes: true,
873
+ bind: function(el) {
874
+ return Rivets.Util.bindEvent(el, 'change', this.publish);
875
+ },
876
+ unbind: function(el) {
877
+ return Rivets.Util.unbindEvent(el, 'change', this.publish);
878
+ },
879
+ routine: function(el, value) {
880
+ var o, _i, _len, _ref, _ref1, _ref2, _results;
881
+ if (window.jQuery != null) {
882
+ el = jQuery(el);
883
+ if ((value != null ? value.toString() : void 0) !== ((_ref = el.val()) != null ? _ref.toString() : void 0)) {
884
+ return el.val(value != null ? value : '');
885
+ }
886
+ } else {
887
+ if (el.type === 'select-multiple') {
888
+ if (value != null) {
889
+ _results = [];
890
+ for (_i = 0, _len = el.length; _i < _len; _i++) {
891
+ o = el[_i];
892
+ _results.push(o.selected = (_ref1 = o.value, __indexOf.call(value, _ref1) >= 0));
893
+ }
894
+ return _results;
895
+ }
896
+ } else if ((value != null ? value.toString() : void 0) !== ((_ref2 = el.value) != null ? _ref2.toString() : void 0)) {
897
+ return el.value = value != null ? value : '';
898
+ }
899
+ }
900
+ }
901
+ },
902
+ text: function(el, value) {
903
+ if (el.innerText != null) {
904
+ return el.innerText = value != null ? value : '';
905
+ } else {
906
+ return el.textContent = value != null ? value : '';
907
+ }
908
+ },
909
+ "if": {
910
+ block: true,
911
+ bind: function(el) {
912
+ var attr, declaration;
913
+ if (this.marker == null) {
914
+ attr = ['data', this.view.config.prefix, this.type].join('-').replace('--', '-');
915
+ declaration = el.getAttribute(attr);
916
+ this.marker = document.createComment(" rivets: " + this.type + " " + declaration + " ");
917
+ el.removeAttribute(attr);
918
+ el.parentNode.insertBefore(this.marker, el);
919
+ return el.parentNode.removeChild(el);
920
+ }
921
+ },
922
+ unbind: function() {
923
+ var _ref;
924
+ return (_ref = this.nested) != null ? _ref.unbind() : void 0;
925
+ },
926
+ routine: function(el, value) {
927
+ var key, model, models, options, _ref;
928
+ if (!!value === (this.nested == null)) {
929
+ if (value) {
930
+ models = {};
931
+ _ref = this.view.models;
932
+ for (key in _ref) {
933
+ model = _ref[key];
934
+ models[key] = model;
935
+ }
936
+ options = {
937
+ binders: this.view.options.binders,
938
+ formatters: this.view.options.formatters,
939
+ config: this.view.options.config
940
+ };
941
+ (this.nested = new Rivets.View(el, models, options)).bind();
942
+ return this.marker.parentNode.insertBefore(el, this.marker.nextSibling);
943
+ } else {
944
+ el.parentNode.removeChild(el);
945
+ this.nested.unbind();
946
+ return delete this.nested;
947
+ }
948
+ }
949
+ },
950
+ update: function(models) {
951
+ var _ref;
952
+ return (_ref = this.nested) != null ? _ref.update(models) : void 0;
953
+ }
954
+ },
955
+ unless: {
956
+ block: true,
957
+ bind: function(el) {
958
+ return Rivets.binders["if"].bind.call(this, el);
959
+ },
960
+ unbind: function() {
961
+ return Rivets.binders["if"].unbind.call(this);
962
+ },
963
+ routine: function(el, value) {
964
+ return Rivets.binders["if"].routine.call(this, el, !value);
965
+ },
966
+ update: function(models) {
967
+ return Rivets.binders["if"].update.call(this, models);
968
+ }
969
+ },
970
+ "on-*": {
971
+ "function": true,
972
+ unbind: function(el) {
973
+ if (this.handler) {
974
+ return Rivets.Util.unbindEvent(el, this.args[0], this.handler);
975
+ }
976
+ },
977
+ routine: function(el, value) {
978
+ if (this.handler) {
979
+ Rivets.Util.unbindEvent(el, this.args[0], this.handler);
980
+ }
981
+ return Rivets.Util.bindEvent(el, this.args[0], this.handler = this.eventHandler(value));
982
+ }
983
+ },
984
+ "each-*": {
985
+ block: true,
986
+ bind: function(el) {
987
+ var attr;
988
+ if (this.marker == null) {
989
+ attr = ['data', this.view.config.prefix, this.type].join('-').replace('--', '-');
990
+ this.marker = document.createComment(" rivets: " + this.type + " ");
991
+ this.iterated = [];
992
+ el.removeAttribute(attr);
993
+ el.parentNode.insertBefore(this.marker, el);
994
+ return el.parentNode.removeChild(el);
995
+ }
996
+ },
997
+ unbind: function(el) {
998
+ var view, _i, _len, _ref, _results;
999
+ if (this.iterated != null) {
1000
+ _ref = this.iterated;
1001
+ _results = [];
1002
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1003
+ view = _ref[_i];
1004
+ _results.push(view.unbind());
1005
+ }
1006
+ return _results;
1007
+ }
1008
+ },
1009
+ routine: function(el, collection) {
1010
+ var data, i, index, k, key, model, modelName, options, previous, template, v, view, _i, _j, _len, _len1, _ref, _ref1, _ref2, _results;
1011
+ modelName = this.args[0];
1012
+ collection = collection || [];
1013
+ if (this.iterated.length > collection.length) {
1014
+ _ref = Array(this.iterated.length - collection.length);
1015
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1016
+ i = _ref[_i];
1017
+ view = this.iterated.pop();
1018
+ view.unbind();
1019
+ this.marker.parentNode.removeChild(view.els[0]);
1020
+ }
1021
+ }
1022
+ _results = [];
1023
+ for (index = _j = 0, _len1 = collection.length; _j < _len1; index = ++_j) {
1024
+ model = collection[index];
1025
+ data = {};
1026
+ data[modelName] = model;
1027
+ if (this.iterated[index] == null) {
1028
+ _ref1 = this.view.models;
1029
+ for (key in _ref1) {
1030
+ model = _ref1[key];
1031
+ if (data[key] == null) {
1032
+ data[key] = model;
1033
+ }
1034
+ }
1035
+ previous = this.iterated.length ? this.iterated[this.iterated.length - 1].els[0] : this.marker;
1036
+ options = {
1037
+ binders: this.view.options.binders,
1038
+ formatters: this.view.options.formatters,
1039
+ config: {}
1040
+ };
1041
+ _ref2 = this.view.options.config;
1042
+ for (k in _ref2) {
1043
+ v = _ref2[k];
1044
+ options.config[k] = v;
1045
+ }
1046
+ options.config.preloadData = true;
1047
+ template = el.cloneNode(true);
1048
+ view = new Rivets.View(template, data, options);
1049
+ view.bind();
1050
+ this.iterated.push(view);
1051
+ _results.push(this.marker.parentNode.insertBefore(template, previous.nextSibling));
1052
+ } else if (this.iterated[index].models[modelName] !== model) {
1053
+ _results.push(this.iterated[index].update(data));
1054
+ } else {
1055
+ _results.push(void 0);
1056
+ }
1057
+ }
1058
+ return _results;
1059
+ },
1060
+ update: function(models) {
1061
+ var data, key, model, view, _i, _len, _ref, _results;
1062
+ data = {};
1063
+ for (key in models) {
1064
+ model = models[key];
1065
+ if (key !== this.args[0]) {
1066
+ data[key] = model;
1067
+ }
1068
+ }
1069
+ _ref = this.iterated;
1070
+ _results = [];
1071
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1072
+ view = _ref[_i];
1073
+ _results.push(view.update(data));
1074
+ }
1075
+ return _results;
1076
+ }
1077
+ },
1078
+ "class-*": function(el, value) {
1079
+ var elClass;
1080
+ elClass = " " + el.className + " ";
1081
+ if (!value === (elClass.indexOf(" " + this.args[0] + " ") !== -1)) {
1082
+ return el.className = value ? "" + el.className + " " + this.args[0] : elClass.replace(" " + this.args[0] + " ", ' ').trim();
1083
+ }
1084
+ },
1085
+ "*": function(el, value) {
1086
+ if (value) {
1087
+ return el.setAttribute(this.type, value);
1088
+ } else {
1089
+ return el.removeAttribute(this.type);
1090
+ }
1091
+ }
1092
+ };
1093
+
1094
+ Rivets.components = {};
1095
+
1096
+ Rivets.config = {
1097
+ preloadData: true,
1098
+ handler: function(context, ev, binding) {
1099
+ return this.call(context, ev, binding.view.models);
1100
+ }
1101
+ };
1102
+
1103
+ Rivets.formatters = {};
1104
+
1105
+ Rivets.factory = function(exports) {
1106
+ exports._ = Rivets;
1107
+ exports.binders = Rivets.binders;
1108
+ exports.components = Rivets.components;
1109
+ exports.formatters = Rivets.formatters;
1110
+ exports.config = Rivets.config;
1111
+ exports.configure = function(options) {
1112
+ var property, value;
1113
+ if (options == null) {
1114
+ options = {};
1115
+ }
1116
+ for (property in options) {
1117
+ value = options[property];
1118
+ Rivets.config[property] = value;
1119
+ }
1120
+ };
1121
+ return exports.bind = function(el, models, options) {
1122
+ var view;
1123
+ if (models == null) {
1124
+ models = {};
1125
+ }
1126
+ if (options == null) {
1127
+ options = {};
1128
+ }
1129
+ view = new Rivets.View(el, models, options);
1130
+ view.bind();
1131
+ return view;
1132
+ };
1133
+ };
1134
+
1135
+ if (typeof exports === 'object') {
1136
+ Rivets.factory(exports);
1137
+ } else if (typeof define === 'function' && define.amd) {
1138
+ define(['exports'], function(exports) {
1139
+ Rivets.factory(this.rivets = exports);
1140
+ return exports;
1141
+ });
1142
+ } else {
1143
+ Rivets.factory(this.rivets = {});
1144
+ }
1145
+
1146
+ }).call(this);
1147
+
1148
+ // Backbone.js 1.1.2
1149
+
1150
+ // (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
1151
+ // Backbone may be freely distributed under the MIT license.
1152
+ // For all details and documentation:
1153
+ // http://backbonejs.org
1154
+
1155
+ (function(root, factory) {
1156
+
1157
+ // Set up Backbone appropriately for the environment. Start with AMD.
1158
+ if (typeof define === 'function' && define.amd) {
1159
+ define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
1160
+ // Export global even in AMD case in case this script is loaded with
1161
+ // others that may still expect a global Backbone.
1162
+ root.Backbone = factory(root, exports, _, $);
1163
+ });
1164
+
1165
+ // Next for Node.js or CommonJS. jQuery may not be needed as a module.
1166
+ } else if (typeof exports !== 'undefined') {
1167
+ var _ = require('underscore');
1168
+ factory(root, exports, _);
1169
+
1170
+ // Finally, as a browser global.
1171
+ } else {
1172
+ root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
1173
+ }
1174
+
1175
+ }(this, function(root, Backbone, _, $) {
1176
+
1177
+ // Initial Setup
1178
+ // -------------
1179
+
1180
+ // Save the previous value of the `Backbone` variable, so that it can be
1181
+ // restored later on, if `noConflict` is used.
1182
+ var previousBackbone = root.Backbone;
1183
+
1184
+ // Create local references to array methods we'll want to use later.
1185
+ var array = [];
1186
+ var push = array.push;
1187
+ var slice = array.slice;
1188
+ var splice = array.splice;
1189
+
1190
+ // Current version of the library. Keep in sync with `package.json`.
1191
+ Backbone.VERSION = '1.1.2';
1192
+
1193
+ // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
1194
+ // the `$` variable.
1195
+ Backbone.$ = $;
1196
+
1197
+ // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
1198
+ // to its previous owner. Returns a reference to this Backbone object.
1199
+ Backbone.noConflict = function() {
1200
+ root.Backbone = previousBackbone;
1201
+ return this;
1202
+ };
1203
+
1204
+ // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
1205
+ // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
1206
+ // set a `X-Http-Method-Override` header.
1207
+ Backbone.emulateHTTP = false;
1208
+
1209
+ // Turn on `emulateJSON` to support legacy servers that can't deal with direct
1210
+ // `application/json` requests ... will encode the body as
1211
+ // `application/x-www-form-urlencoded` instead and will send the model in a
1212
+ // form param named `model`.
1213
+ Backbone.emulateJSON = false;
1214
+
1215
+ // Backbone.Events
1216
+ // ---------------
1217
+
1218
+ // A module that can be mixed in to *any object* in order to provide it with
1219
+ // custom events. You may bind with `on` or remove with `off` callback
1220
+ // functions to an event; `trigger`-ing an event fires all callbacks in
1221
+ // succession.
1222
+ //
1223
+ // var object = {};
1224
+ // _.extend(object, Backbone.Events);
1225
+ // object.on('expand', function(){ alert('expanded'); });
1226
+ // object.trigger('expand');
1227
+ //
1228
+ var Events = Backbone.Events = {
1229
+
1230
+ // Bind an event to a `callback` function. Passing `"all"` will bind
1231
+ // the callback to all events fired.
1232
+ on: function(name, callback, context) {
1233
+ if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
1234
+ this._events || (this._events = {});
1235
+ var events = this._events[name] || (this._events[name] = []);
1236
+ events.push({callback: callback, context: context, ctx: context || this});
1237
+ return this;
1238
+ },
1239
+
1240
+ // Bind an event to only be triggered a single time. After the first time
1241
+ // the callback is invoked, it will be removed.
1242
+ once: function(name, callback, context) {
1243
+ if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
1244
+ var self = this;
1245
+ var once = _.once(function() {
1246
+ self.off(name, once);
1247
+ callback.apply(this, arguments);
1248
+ });
1249
+ once._callback = callback;
1250
+ return this.on(name, once, context);
1251
+ },
1252
+
1253
+ // Remove one or many callbacks. If `context` is null, removes all
1254
+ // callbacks with that function. If `callback` is null, removes all
1255
+ // callbacks for the event. If `name` is null, removes all bound
1256
+ // callbacks for all events.
1257
+ off: function(name, callback, context) {
1258
+ var retain, ev, events, names, i, l, j, k;
1259
+ if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
1260
+ if (!name && !callback && !context) {
1261
+ this._events = void 0;
1262
+ return this;
1263
+ }
1264
+ names = name ? [name] : _.keys(this._events);
1265
+ for (i = 0, l = names.length; i < l; i++) {
1266
+ name = names[i];
1267
+ if (events = this._events[name]) {
1268
+ this._events[name] = retain = [];
1269
+ if (callback || context) {
1270
+ for (j = 0, k = events.length; j < k; j++) {
1271
+ ev = events[j];
1272
+ if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
1273
+ (context && context !== ev.context)) {
1274
+ retain.push(ev);
1275
+ }
1276
+ }
1277
+ }
1278
+ if (!retain.length) delete this._events[name];
1279
+ }
1280
+ }
1281
+
1282
+ return this;
1283
+ },
1284
+
1285
+ // Trigger one or many events, firing all bound callbacks. Callbacks are
1286
+ // passed the same arguments as `trigger` is, apart from the event name
1287
+ // (unless you're listening on `"all"`, which will cause your callback to
1288
+ // receive the true name of the event as the first argument).
1289
+ trigger: function(name) {
1290
+ if (!this._events) return this;
1291
+ var args = slice.call(arguments, 1);
1292
+ if (!eventsApi(this, 'trigger', name, args)) return this;
1293
+ var events = this._events[name];
1294
+ var allEvents = this._events.all;
1295
+ if (events) triggerEvents(events, args);
1296
+ if (allEvents) triggerEvents(allEvents, arguments);
1297
+ return this;
1298
+ },
1299
+
1300
+ // Tell this object to stop listening to either specific events ... or
1301
+ // to every object it's currently listening to.
1302
+ stopListening: function(obj, name, callback) {
1303
+ var listeningTo = this._listeningTo;
1304
+ if (!listeningTo) return this;
1305
+ var remove = !name && !callback;
1306
+ if (!callback && typeof name === 'object') callback = this;
1307
+ if (obj) (listeningTo = {})[obj._listenId] = obj;
1308
+ for (var id in listeningTo) {
1309
+ obj = listeningTo[id];
1310
+ obj.off(name, callback, this);
1311
+ if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
1312
+ }
1313
+ return this;
1314
+ }
1315
+
1316
+ };
1317
+
1318
+ // Regular expression used to split event strings.
1319
+ var eventSplitter = /\s+/;
1320
+
1321
+ // Implement fancy features of the Events API such as multiple event
1322
+ // names `"change blur"` and jQuery-style event maps `{change: action}`
1323
+ // in terms of the existing API.
1324
+ var eventsApi = function(obj, action, name, rest) {
1325
+ if (!name) return true;
1326
+
1327
+ // Handle event maps.
1328
+ if (typeof name === 'object') {
1329
+ for (var key in name) {
1330
+ obj[action].apply(obj, [key, name[key]].concat(rest));
1331
+ }
1332
+ return false;
1333
+ }
1334
+
1335
+ // Handle space separated event names.
1336
+ if (eventSplitter.test(name)) {
1337
+ var names = name.split(eventSplitter);
1338
+ for (var i = 0, l = names.length; i < l; i++) {
1339
+ obj[action].apply(obj, [names[i]].concat(rest));
1340
+ }
1341
+ return false;
1342
+ }
1343
+
1344
+ return true;
1345
+ };
1346
+
1347
+ // A difficult-to-believe, but optimized internal dispatch function for
1348
+ // triggering events. Tries to keep the usual cases speedy (most internal
1349
+ // Backbone events have 3 arguments).
1350
+ var triggerEvents = function(events, args) {
1351
+ var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
1352
+ switch (args.length) {
1353
+ case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
1354
+ case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
1355
+ case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
1356
+ case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
1357
+ default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
1358
+ }
1359
+ };
1360
+
1361
+ var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
1362
+
1363
+ // Inversion-of-control versions of `on` and `once`. Tell *this* object to
1364
+ // listen to an event in another object ... keeping track of what it's
1365
+ // listening to.
1366
+ _.each(listenMethods, function(implementation, method) {
1367
+ Events[method] = function(obj, name, callback) {
1368
+ var listeningTo = this._listeningTo || (this._listeningTo = {});
1369
+ var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
1370
+ listeningTo[id] = obj;
1371
+ if (!callback && typeof name === 'object') callback = this;
1372
+ obj[implementation](name, callback, this);
1373
+ return this;
1374
+ };
1375
+ });
1376
+
1377
+ // Aliases for backwards compatibility.
1378
+ Events.bind = Events.on;
1379
+ Events.unbind = Events.off;
1380
+
1381
+ // Allow the `Backbone` object to serve as a global event bus, for folks who
1382
+ // want global "pubsub" in a convenient place.
1383
+ _.extend(Backbone, Events);
1384
+
1385
+ // Backbone.Model
1386
+ // --------------
1387
+
1388
+ // Backbone **Models** are the basic data object in the framework --
1389
+ // frequently representing a row in a table in a database on your server.
1390
+ // A discrete chunk of data and a bunch of useful, related methods for
1391
+ // performing computations and transformations on that data.
1392
+
1393
+ // Create a new model with the specified attributes. A client id (`cid`)
1394
+ // is automatically generated and assigned for you.
1395
+ var Model = Backbone.Model = function(attributes, options) {
1396
+ var attrs = attributes || {};
1397
+ options || (options = {});
1398
+ this.cid = _.uniqueId('c');
1399
+ this.attributes = {};
1400
+ if (options.collection) this.collection = options.collection;
1401
+ if (options.parse) attrs = this.parse(attrs, options) || {};
1402
+ attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
1403
+ this.set(attrs, options);
1404
+ this.changed = {};
1405
+ this.initialize.apply(this, arguments);
1406
+ };
1407
+
1408
+ // Attach all inheritable methods to the Model prototype.
1409
+ _.extend(Model.prototype, Events, {
1410
+
1411
+ // A hash of attributes whose current and previous value differ.
1412
+ changed: null,
1413
+
1414
+ // The value returned during the last failed validation.
1415
+ validationError: null,
1416
+
1417
+ // The default name for the JSON `id` attribute is `"id"`. MongoDB and
1418
+ // CouchDB users may want to set this to `"_id"`.
1419
+ idAttribute: 'id',
1420
+
1421
+ // Initialize is an empty function by default. Override it with your own
1422
+ // initialization logic.
1423
+ initialize: function(){},
1424
+
1425
+ // Return a copy of the model's `attributes` object.
1426
+ toJSON: function(options) {
1427
+ return _.clone(this.attributes);
1428
+ },
1429
+
1430
+ // Proxy `Backbone.sync` by default -- but override this if you need
1431
+ // custom syncing semantics for *this* particular model.
1432
+ sync: function() {
1433
+ return Backbone.sync.apply(this, arguments);
1434
+ },
1435
+
1436
+ // Get the value of an attribute.
1437
+ get: function(attr) {
1438
+ return this.attributes[attr];
1439
+ },
1440
+
1441
+ // Get the HTML-escaped value of an attribute.
1442
+ escape: function(attr) {
1443
+ return _.escape(this.get(attr));
1444
+ },
1445
+
1446
+ // Returns `true` if the attribute contains a value that is not null
1447
+ // or undefined.
1448
+ has: function(attr) {
1449
+ return this.get(attr) != null;
1450
+ },
1451
+
1452
+ // Set a hash of model attributes on the object, firing `"change"`. This is
1453
+ // the core primitive operation of a model, updating the data and notifying
1454
+ // anyone who needs to know about the change in state. The heart of the beast.
1455
+ set: function(key, val, options) {
1456
+ var attr, attrs, unset, changes, silent, changing, prev, current;
1457
+ if (key == null) return this;
1458
+
1459
+ // Handle both `"key", value` and `{key: value}` -style arguments.
1460
+ if (typeof key === 'object') {
1461
+ attrs = key;
1462
+ options = val;
1463
+ } else {
1464
+ (attrs = {})[key] = val;
1465
+ }
1466
+
1467
+ options || (options = {});
1468
+
1469
+ // Run validation.
1470
+ if (!this._validate(attrs, options)) return false;
1471
+
1472
+ // Extract attributes and options.
1473
+ unset = options.unset;
1474
+ silent = options.silent;
1475
+ changes = [];
1476
+ changing = this._changing;
1477
+ this._changing = true;
1478
+
1479
+ if (!changing) {
1480
+ this._previousAttributes = _.clone(this.attributes);
1481
+ this.changed = {};
1482
+ }
1483
+ current = this.attributes, prev = this._previousAttributes;
1484
+
1485
+ // Check for changes of `id`.
1486
+ if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
1487
+
1488
+ // For each `set` attribute, update or delete the current value.
1489
+ for (attr in attrs) {
1490
+ val = attrs[attr];
1491
+ if (!_.isEqual(current[attr], val)) changes.push(attr);
1492
+ if (!_.isEqual(prev[attr], val)) {
1493
+ this.changed[attr] = val;
1494
+ } else {
1495
+ delete this.changed[attr];
1496
+ }
1497
+ unset ? delete current[attr] : current[attr] = val;
1498
+ }
1499
+
1500
+ // Trigger all relevant attribute changes.
1501
+ if (!silent) {
1502
+ if (changes.length) this._pending = options;
1503
+ for (var i = 0, l = changes.length; i < l; i++) {
1504
+ this.trigger('change:' + changes[i], this, current[changes[i]], options);
1505
+ }
1506
+ }
1507
+
1508
+ // You might be wondering why there's a `while` loop here. Changes can
1509
+ // be recursively nested within `"change"` events.
1510
+ if (changing) return this;
1511
+ if (!silent) {
1512
+ while (this._pending) {
1513
+ options = this._pending;
1514
+ this._pending = false;
1515
+ this.trigger('change', this, options);
1516
+ }
1517
+ }
1518
+ this._pending = false;
1519
+ this._changing = false;
1520
+ return this;
1521
+ },
1522
+
1523
+ // Remove an attribute from the model, firing `"change"`. `unset` is a noop
1524
+ // if the attribute doesn't exist.
1525
+ unset: function(attr, options) {
1526
+ return this.set(attr, void 0, _.extend({}, options, {unset: true}));
1527
+ },
1528
+
1529
+ // Clear all attributes on the model, firing `"change"`.
1530
+ clear: function(options) {
1531
+ var attrs = {};
1532
+ for (var key in this.attributes) attrs[key] = void 0;
1533
+ return this.set(attrs, _.extend({}, options, {unset: true}));
1534
+ },
1535
+
1536
+ // Determine if the model has changed since the last `"change"` event.
1537
+ // If you specify an attribute name, determine if that attribute has changed.
1538
+ hasChanged: function(attr) {
1539
+ if (attr == null) return !_.isEmpty(this.changed);
1540
+ return _.has(this.changed, attr);
1541
+ },
1542
+
1543
+ // Return an object containing all the attributes that have changed, or
1544
+ // false if there are no changed attributes. Useful for determining what
1545
+ // parts of a view need to be updated and/or what attributes need to be
1546
+ // persisted to the server. Unset attributes will be set to undefined.
1547
+ // You can also pass an attributes object to diff against the model,
1548
+ // determining if there *would be* a change.
1549
+ changedAttributes: function(diff) {
1550
+ if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
1551
+ var val, changed = false;
1552
+ var old = this._changing ? this._previousAttributes : this.attributes;
1553
+ for (var attr in diff) {
1554
+ if (_.isEqual(old[attr], (val = diff[attr]))) continue;
1555
+ (changed || (changed = {}))[attr] = val;
1556
+ }
1557
+ return changed;
1558
+ },
1559
+
1560
+ // Get the previous value of an attribute, recorded at the time the last
1561
+ // `"change"` event was fired.
1562
+ previous: function(attr) {
1563
+ if (attr == null || !this._previousAttributes) return null;
1564
+ return this._previousAttributes[attr];
1565
+ },
1566
+
1567
+ // Get all of the attributes of the model at the time of the previous
1568
+ // `"change"` event.
1569
+ previousAttributes: function() {
1570
+ return _.clone(this._previousAttributes);
1571
+ },
1572
+
1573
+ // Fetch the model from the server. If the server's representation of the
1574
+ // model differs from its current attributes, they will be overridden,
1575
+ // triggering a `"change"` event.
1576
+ fetch: function(options) {
1577
+ options = options ? _.clone(options) : {};
1578
+ if (options.parse === void 0) options.parse = true;
1579
+ var model = this;
1580
+ var success = options.success;
1581
+ options.success = function(resp) {
1582
+ if (!model.set(model.parse(resp, options), options)) return false;
1583
+ if (success) success(model, resp, options);
1584
+ model.trigger('sync', model, resp, options);
1585
+ };
1586
+ wrapError(this, options);
1587
+ return this.sync('read', this, options);
1588
+ },
1589
+
1590
+ // Set a hash of model attributes, and sync the model to the server.
1591
+ // If the server returns an attributes hash that differs, the model's
1592
+ // state will be `set` again.
1593
+ save: function(key, val, options) {
1594
+ var attrs, method, xhr, attributes = this.attributes;
1595
+
1596
+ // Handle both `"key", value` and `{key: value}` -style arguments.
1597
+ if (key == null || typeof key === 'object') {
1598
+ attrs = key;
1599
+ options = val;
1600
+ } else {
1601
+ (attrs = {})[key] = val;
1602
+ }
1603
+
1604
+ options = _.extend({validate: true}, options);
1605
+
1606
+ // If we're not waiting and attributes exist, save acts as
1607
+ // `set(attr).save(null, opts)` with validation. Otherwise, check if
1608
+ // the model will be valid when the attributes, if any, are set.
1609
+ if (attrs && !options.wait) {
1610
+ if (!this.set(attrs, options)) return false;
1611
+ } else {
1612
+ if (!this._validate(attrs, options)) return false;
1613
+ }
1614
+
1615
+ // Set temporary attributes if `{wait: true}`.
1616
+ if (attrs && options.wait) {
1617
+ this.attributes = _.extend({}, attributes, attrs);
1618
+ }
1619
+
1620
+ // After a successful server-side save, the client is (optionally)
1621
+ // updated with the server-side state.
1622
+ if (options.parse === void 0) options.parse = true;
1623
+ var model = this;
1624
+ var success = options.success;
1625
+ options.success = function(resp) {
1626
+ // Ensure attributes are restored during synchronous saves.
1627
+ model.attributes = attributes;
1628
+ var serverAttrs = model.parse(resp, options);
1629
+ if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
1630
+ if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
1631
+ return false;
1632
+ }
1633
+ if (success) success(model, resp, options);
1634
+ model.trigger('sync', model, resp, options);
1635
+ };
1636
+ wrapError(this, options);
1637
+
1638
+ method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
1639
+ if (method === 'patch') options.attrs = attrs;
1640
+ xhr = this.sync(method, this, options);
1641
+
1642
+ // Restore attributes.
1643
+ if (attrs && options.wait) this.attributes = attributes;
1644
+
1645
+ return xhr;
1646
+ },
1647
+
1648
+ // Destroy this model on the server if it was already persisted.
1649
+ // Optimistically removes the model from its collection, if it has one.
1650
+ // If `wait: true` is passed, waits for the server to respond before removal.
1651
+ destroy: function(options) {
1652
+ options = options ? _.clone(options) : {};
1653
+ var model = this;
1654
+ var success = options.success;
1655
+
1656
+ var destroy = function() {
1657
+ model.trigger('destroy', model, model.collection, options);
1658
+ };
1659
+
1660
+ options.success = function(resp) {
1661
+ if (options.wait || model.isNew()) destroy();
1662
+ if (success) success(model, resp, options);
1663
+ if (!model.isNew()) model.trigger('sync', model, resp, options);
1664
+ };
1665
+
1666
+ if (this.isNew()) {
1667
+ options.success();
1668
+ return false;
1669
+ }
1670
+ wrapError(this, options);
1671
+
1672
+ var xhr = this.sync('delete', this, options);
1673
+ if (!options.wait) destroy();
1674
+ return xhr;
1675
+ },
1676
+
1677
+ // Default URL for the model's representation on the server -- if you're
1678
+ // using Backbone's restful methods, override this to change the endpoint
1679
+ // that will be called.
1680
+ url: function() {
1681
+ var base =
1682
+ _.result(this, 'urlRoot') ||
1683
+ _.result(this.collection, 'url') ||
1684
+ urlError();
1685
+ if (this.isNew()) return base;
1686
+ return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
1687
+ },
1688
+
1689
+ // **parse** converts a response into the hash of attributes to be `set` on
1690
+ // the model. The default implementation is just to pass the response along.
1691
+ parse: function(resp, options) {
1692
+ return resp;
1693
+ },
1694
+
1695
+ // Create a new model with identical attributes to this one.
1696
+ clone: function() {
1697
+ return new this.constructor(this.attributes);
1698
+ },
1699
+
1700
+ // A model is new if it has never been saved to the server, and lacks an id.
1701
+ isNew: function() {
1702
+ return !this.has(this.idAttribute);
1703
+ },
1704
+
1705
+ // Check if the model is currently in a valid state.
1706
+ isValid: function(options) {
1707
+ return this._validate({}, _.extend(options || {}, { validate: true }));
1708
+ },
1709
+
1710
+ // Run validation against the next complete set of model attributes,
1711
+ // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
1712
+ _validate: function(attrs, options) {
1713
+ if (!options.validate || !this.validate) return true;
1714
+ attrs = _.extend({}, this.attributes, attrs);
1715
+ var error = this.validationError = this.validate(attrs, options) || null;
1716
+ if (!error) return true;
1717
+ this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
1718
+ return false;
1719
+ }
1720
+
1721
+ });
1722
+
1723
+ // Underscore methods that we want to implement on the Model.
1724
+ var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
1725
+
1726
+ // Mix in each Underscore method as a proxy to `Model#attributes`.
1727
+ _.each(modelMethods, function(method) {
1728
+ Model.prototype[method] = function() {
1729
+ var args = slice.call(arguments);
1730
+ args.unshift(this.attributes);
1731
+ return _[method].apply(_, args);
1732
+ };
1733
+ });
1734
+
1735
+ // Backbone.Collection
1736
+ // -------------------
1737
+
1738
+ // If models tend to represent a single row of data, a Backbone Collection is
1739
+ // more analagous to a table full of data ... or a small slice or page of that
1740
+ // table, or a collection of rows that belong together for a particular reason
1741
+ // -- all of the messages in this particular folder, all of the documents
1742
+ // belonging to this particular author, and so on. Collections maintain
1743
+ // indexes of their models, both in order, and for lookup by `id`.
1744
+
1745
+ // Create a new **Collection**, perhaps to contain a specific type of `model`.
1746
+ // If a `comparator` is specified, the Collection will maintain
1747
+ // its models in sort order, as they're added and removed.
1748
+ var Collection = Backbone.Collection = function(models, options) {
1749
+ options || (options = {});
1750
+ if (options.model) this.model = options.model;
1751
+ if (options.comparator !== void 0) this.comparator = options.comparator;
1752
+ this._reset();
1753
+ this.initialize.apply(this, arguments);
1754
+ if (models) this.reset(models, _.extend({silent: true}, options));
1755
+ };
1756
+
1757
+ // Default options for `Collection#set`.
1758
+ var setOptions = {add: true, remove: true, merge: true};
1759
+ var addOptions = {add: true, remove: false};
1760
+
1761
+ // Define the Collection's inheritable methods.
1762
+ _.extend(Collection.prototype, Events, {
1763
+
1764
+ // The default model for a collection is just a **Backbone.Model**.
1765
+ // This should be overridden in most cases.
1766
+ model: Model,
1767
+
1768
+ // Initialize is an empty function by default. Override it with your own
1769
+ // initialization logic.
1770
+ initialize: function(){},
1771
+
1772
+ // The JSON representation of a Collection is an array of the
1773
+ // models' attributes.
1774
+ toJSON: function(options) {
1775
+ return this.map(function(model){ return model.toJSON(options); });
1776
+ },
1777
+
1778
+ // Proxy `Backbone.sync` by default.
1779
+ sync: function() {
1780
+ return Backbone.sync.apply(this, arguments);
1781
+ },
1782
+
1783
+ // Add a model, or list of models to the set.
1784
+ add: function(models, options) {
1785
+ return this.set(models, _.extend({merge: false}, options, addOptions));
1786
+ },
1787
+
1788
+ // Remove a model, or a list of models from the set.
1789
+ remove: function(models, options) {
1790
+ var singular = !_.isArray(models);
1791
+ models = singular ? [models] : _.clone(models);
1792
+ options || (options = {});
1793
+ var i, l, index, model;
1794
+ for (i = 0, l = models.length; i < l; i++) {
1795
+ model = models[i] = this.get(models[i]);
1796
+ if (!model) continue;
1797
+ delete this._byId[model.id];
1798
+ delete this._byId[model.cid];
1799
+ index = this.indexOf(model);
1800
+ this.models.splice(index, 1);
1801
+ this.length--;
1802
+ if (!options.silent) {
1803
+ options.index = index;
1804
+ model.trigger('remove', model, this, options);
1805
+ }
1806
+ this._removeReference(model, options);
1807
+ }
1808
+ return singular ? models[0] : models;
1809
+ },
1810
+
1811
+ // Update a collection by `set`-ing a new list of models, adding new ones,
1812
+ // removing models that are no longer present, and merging models that
1813
+ // already exist in the collection, as necessary. Similar to **Model#set**,
1814
+ // the core operation for updating the data contained by the collection.
1815
+ set: function(models, options) {
1816
+ options = _.defaults({}, options, setOptions);
1817
+ if (options.parse) models = this.parse(models, options);
1818
+ var singular = !_.isArray(models);
1819
+ models = singular ? (models ? [models] : []) : _.clone(models);
1820
+ var i, l, id, model, attrs, existing, sort;
1821
+ var at = options.at;
1822
+ var targetModel = this.model;
1823
+ var sortable = this.comparator && (at == null) && options.sort !== false;
1824
+ var sortAttr = _.isString(this.comparator) ? this.comparator : null;
1825
+ var toAdd = [], toRemove = [], modelMap = {};
1826
+ var add = options.add, merge = options.merge, remove = options.remove;
1827
+ var order = !sortable && add && remove ? [] : false;
1828
+
1829
+ // Turn bare objects into model references, and prevent invalid models
1830
+ // from being added.
1831
+ for (i = 0, l = models.length; i < l; i++) {
1832
+ attrs = models[i] || {};
1833
+ if (attrs instanceof Model) {
1834
+ id = model = attrs;
1835
+ } else {
1836
+ id = attrs[targetModel.prototype.idAttribute || 'id'];
1837
+ }
1838
+
1839
+ // If a duplicate is found, prevent it from being added and
1840
+ // optionally merge it into the existing model.
1841
+ if (existing = this.get(id)) {
1842
+ if (remove) modelMap[existing.cid] = true;
1843
+ if (merge) {
1844
+ attrs = attrs === model ? model.attributes : attrs;
1845
+ if (options.parse) attrs = existing.parse(attrs, options);
1846
+ existing.set(attrs, options);
1847
+ if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
1848
+ }
1849
+ models[i] = existing;
1850
+
1851
+ // If this is a new, valid model, push it to the `toAdd` list.
1852
+ } else if (add) {
1853
+ model = models[i] = this._prepareModel(attrs, options);
1854
+ if (!model) continue;
1855
+ toAdd.push(model);
1856
+ this._addReference(model, options);
1857
+ }
1858
+
1859
+ // Do not add multiple models with the same `id`.
1860
+ model = existing || model;
1861
+ if (order && (model.isNew() || !modelMap[model.id])) order.push(model);
1862
+ modelMap[model.id] = true;
1863
+ }
1864
+
1865
+ // Remove nonexistent models if appropriate.
1866
+ if (remove) {
1867
+ for (i = 0, l = this.length; i < l; ++i) {
1868
+ if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
1869
+ }
1870
+ if (toRemove.length) this.remove(toRemove, options);
1871
+ }
1872
+
1873
+ // See if sorting is needed, update `length` and splice in new models.
1874
+ if (toAdd.length || (order && order.length)) {
1875
+ if (sortable) sort = true;
1876
+ this.length += toAdd.length;
1877
+ if (at != null) {
1878
+ for (i = 0, l = toAdd.length; i < l; i++) {
1879
+ this.models.splice(at + i, 0, toAdd[i]);
1880
+ }
1881
+ } else {
1882
+ if (order) this.models.length = 0;
1883
+ var orderedModels = order || toAdd;
1884
+ for (i = 0, l = orderedModels.length; i < l; i++) {
1885
+ this.models.push(orderedModels[i]);
1886
+ }
1887
+ }
1888
+ }
1889
+
1890
+ // Silently sort the collection if appropriate.
1891
+ if (sort) this.sort({silent: true});
1892
+
1893
+ // Unless silenced, it's time to fire all appropriate add/sort events.
1894
+ if (!options.silent) {
1895
+ for (i = 0, l = toAdd.length; i < l; i++) {
1896
+ (model = toAdd[i]).trigger('add', model, this, options);
1897
+ }
1898
+ if (sort || (order && order.length)) this.trigger('sort', this, options);
1899
+ }
1900
+
1901
+ // Return the added (or merged) model (or models).
1902
+ return singular ? models[0] : models;
1903
+ },
1904
+
1905
+ // When you have more items than you want to add or remove individually,
1906
+ // you can reset the entire set with a new list of models, without firing
1907
+ // any granular `add` or `remove` events. Fires `reset` when finished.
1908
+ // Useful for bulk operations and optimizations.
1909
+ reset: function(models, options) {
1910
+ options || (options = {});
1911
+ for (var i = 0, l = this.models.length; i < l; i++) {
1912
+ this._removeReference(this.models[i], options);
1913
+ }
1914
+ options.previousModels = this.models;
1915
+ this._reset();
1916
+ models = this.add(models, _.extend({silent: true}, options));
1917
+ if (!options.silent) this.trigger('reset', this, options);
1918
+ return models;
1919
+ },
1920
+
1921
+ // Add a model to the end of the collection.
1922
+ push: function(model, options) {
1923
+ return this.add(model, _.extend({at: this.length}, options));
1924
+ },
1925
+
1926
+ // Remove a model from the end of the collection.
1927
+ pop: function(options) {
1928
+ var model = this.at(this.length - 1);
1929
+ this.remove(model, options);
1930
+ return model;
1931
+ },
1932
+
1933
+ // Add a model to the beginning of the collection.
1934
+ unshift: function(model, options) {
1935
+ return this.add(model, _.extend({at: 0}, options));
1936
+ },
1937
+
1938
+ // Remove a model from the beginning of the collection.
1939
+ shift: function(options) {
1940
+ var model = this.at(0);
1941
+ this.remove(model, options);
1942
+ return model;
1943
+ },
1944
+
1945
+ // Slice out a sub-array of models from the collection.
1946
+ slice: function() {
1947
+ return slice.apply(this.models, arguments);
1948
+ },
1949
+
1950
+ // Get a model from the set by id.
1951
+ get: function(obj) {
1952
+ if (obj == null) return void 0;
1953
+ return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid];
1954
+ },
1955
+
1956
+ // Get the model at the given index.
1957
+ at: function(index) {
1958
+ return this.models[index];
1959
+ },
1960
+
1961
+ // Return models with matching attributes. Useful for simple cases of
1962
+ // `filter`.
1963
+ where: function(attrs, first) {
1964
+ if (_.isEmpty(attrs)) return first ? void 0 : [];
1965
+ return this[first ? 'find' : 'filter'](function(model) {
1966
+ for (var key in attrs) {
1967
+ if (attrs[key] !== model.get(key)) return false;
1968
+ }
1969
+ return true;
1970
+ });
1971
+ },
1972
+
1973
+ // Return the first model with matching attributes. Useful for simple cases
1974
+ // of `find`.
1975
+ findWhere: function(attrs) {
1976
+ return this.where(attrs, true);
1977
+ },
1978
+
1979
+ // Force the collection to re-sort itself. You don't need to call this under
1980
+ // normal circumstances, as the set will maintain sort order as each item
1981
+ // is added.
1982
+ sort: function(options) {
1983
+ if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
1984
+ options || (options = {});
1985
+
1986
+ // Run sort based on type of `comparator`.
1987
+ if (_.isString(this.comparator) || this.comparator.length === 1) {
1988
+ this.models = this.sortBy(this.comparator, this);
1989
+ } else {
1990
+ this.models.sort(_.bind(this.comparator, this));
1991
+ }
1992
+
1993
+ if (!options.silent) this.trigger('sort', this, options);
1994
+ return this;
1995
+ },
1996
+
1997
+ // Pluck an attribute from each model in the collection.
1998
+ pluck: function(attr) {
1999
+ return _.invoke(this.models, 'get', attr);
2000
+ },
2001
+
2002
+ // Fetch the default set of models for this collection, resetting the
2003
+ // collection when they arrive. If `reset: true` is passed, the response
2004
+ // data will be passed through the `reset` method instead of `set`.
2005
+ fetch: function(options) {
2006
+ options = options ? _.clone(options) : {};
2007
+ if (options.parse === void 0) options.parse = true;
2008
+ var success = options.success;
2009
+ var collection = this;
2010
+ options.success = function(resp) {
2011
+ var method = options.reset ? 'reset' : 'set';
2012
+ collection[method](resp, options);
2013
+ if (success) success(collection, resp, options);
2014
+ collection.trigger('sync', collection, resp, options);
2015
+ };
2016
+ wrapError(this, options);
2017
+ return this.sync('read', this, options);
2018
+ },
2019
+
2020
+ // Create a new instance of a model in this collection. Add the model to the
2021
+ // collection immediately, unless `wait: true` is passed, in which case we
2022
+ // wait for the server to agree.
2023
+ create: function(model, options) {
2024
+ options = options ? _.clone(options) : {};
2025
+ if (!(model = this._prepareModel(model, options))) return false;
2026
+ if (!options.wait) this.add(model, options);
2027
+ var collection = this;
2028
+ var success = options.success;
2029
+ options.success = function(model, resp) {
2030
+ if (options.wait) collection.add(model, options);
2031
+ if (success) success(model, resp, options);
2032
+ };
2033
+ model.save(null, options);
2034
+ return model;
2035
+ },
2036
+
2037
+ // **parse** converts a response into a list of models to be added to the
2038
+ // collection. The default implementation is just to pass it through.
2039
+ parse: function(resp, options) {
2040
+ return resp;
2041
+ },
2042
+
2043
+ // Create a new collection with an identical list of models as this one.
2044
+ clone: function() {
2045
+ return new this.constructor(this.models);
2046
+ },
2047
+
2048
+ // Private method to reset all internal state. Called when the collection
2049
+ // is first initialized or reset.
2050
+ _reset: function() {
2051
+ this.length = 0;
2052
+ this.models = [];
2053
+ this._byId = {};
2054
+ },
2055
+
2056
+ // Prepare a hash of attributes (or other model) to be added to this
2057
+ // collection.
2058
+ _prepareModel: function(attrs, options) {
2059
+ if (attrs instanceof Model) return attrs;
2060
+ options = options ? _.clone(options) : {};
2061
+ options.collection = this;
2062
+ var model = new this.model(attrs, options);
2063
+ if (!model.validationError) return model;
2064
+ this.trigger('invalid', this, model.validationError, options);
2065
+ return false;
2066
+ },
2067
+
2068
+ // Internal method to create a model's ties to a collection.
2069
+ _addReference: function(model, options) {
2070
+ this._byId[model.cid] = model;
2071
+ if (model.id != null) this._byId[model.id] = model;
2072
+ if (!model.collection) model.collection = this;
2073
+ model.on('all', this._onModelEvent, this);
2074
+ },
2075
+
2076
+ // Internal method to sever a model's ties to a collection.
2077
+ _removeReference: function(model, options) {
2078
+ if (this === model.collection) delete model.collection;
2079
+ model.off('all', this._onModelEvent, this);
2080
+ },
2081
+
2082
+ // Internal method called every time a model in the set fires an event.
2083
+ // Sets need to update their indexes when models change ids. All other
2084
+ // events simply proxy through. "add" and "remove" events that originate
2085
+ // in other collections are ignored.
2086
+ _onModelEvent: function(event, model, collection, options) {
2087
+ if ((event === 'add' || event === 'remove') && collection !== this) return;
2088
+ if (event === 'destroy') this.remove(model, options);
2089
+ if (model && event === 'change:' + model.idAttribute) {
2090
+ delete this._byId[model.previous(model.idAttribute)];
2091
+ if (model.id != null) this._byId[model.id] = model;
2092
+ }
2093
+ this.trigger.apply(this, arguments);
2094
+ }
2095
+
2096
+ });
2097
+
2098
+ // Underscore methods that we want to implement on the Collection.
2099
+ // 90% of the core usefulness of Backbone Collections is actually implemented
2100
+ // right here:
2101
+ var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
2102
+ 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
2103
+ 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
2104
+ 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
2105
+ 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
2106
+ 'lastIndexOf', 'isEmpty', 'chain', 'sample'];
2107
+
2108
+ // Mix in each Underscore method as a proxy to `Collection#models`.
2109
+ _.each(methods, function(method) {
2110
+ Collection.prototype[method] = function() {
2111
+ var args = slice.call(arguments);
2112
+ args.unshift(this.models);
2113
+ return _[method].apply(_, args);
2114
+ };
2115
+ });
2116
+
2117
+ // Underscore methods that take a property name as an argument.
2118
+ var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];
2119
+
2120
+ // Use attributes instead of properties.
2121
+ _.each(attributeMethods, function(method) {
2122
+ Collection.prototype[method] = function(value, context) {
2123
+ var iterator = _.isFunction(value) ? value : function(model) {
2124
+ return model.get(value);
2125
+ };
2126
+ return _[method](this.models, iterator, context);
2127
+ };
2128
+ });
2129
+
2130
+ // Backbone.View
2131
+ // -------------
2132
+
2133
+ // Backbone Views are almost more convention than they are actual code. A View
2134
+ // is simply a JavaScript object that represents a logical chunk of UI in the
2135
+ // DOM. This might be a single item, an entire list, a sidebar or panel, or
2136
+ // even the surrounding frame which wraps your whole app. Defining a chunk of
2137
+ // UI as a **View** allows you to define your DOM events declaratively, without
2138
+ // having to worry about render order ... and makes it easy for the view to
2139
+ // react to specific changes in the state of your models.
2140
+
2141
+ // Creating a Backbone.View creates its initial element outside of the DOM,
2142
+ // if an existing element is not provided...
2143
+ var View = Backbone.View = function(options) {
2144
+ this.cid = _.uniqueId('view');
2145
+ options || (options = {});
2146
+ _.extend(this, _.pick(options, viewOptions));
2147
+ this._ensureElement();
2148
+ this.initialize.apply(this, arguments);
2149
+ this.delegateEvents();
2150
+ };
2151
+
2152
+ // Cached regex to split keys for `delegate`.
2153
+ var delegateEventSplitter = /^(\S+)\s*(.*)$/;
2154
+
2155
+ // List of view options to be merged as properties.
2156
+ var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
2157
+
2158
+ // Set up all inheritable **Backbone.View** properties and methods.
2159
+ _.extend(View.prototype, Events, {
2160
+
2161
+ // The default `tagName` of a View's element is `"div"`.
2162
+ tagName: 'div',
2163
+
2164
+ // jQuery delegate for element lookup, scoped to DOM elements within the
2165
+ // current view. This should be preferred to global lookups where possible.
2166
+ $: function(selector) {
2167
+ return this.$el.find(selector);
2168
+ },
2169
+
2170
+ // Initialize is an empty function by default. Override it with your own
2171
+ // initialization logic.
2172
+ initialize: function(){},
2173
+
2174
+ // **render** is the core function that your view should override, in order
2175
+ // to populate its element (`this.el`), with the appropriate HTML. The
2176
+ // convention is for **render** to always return `this`.
2177
+ render: function() {
2178
+ return this;
2179
+ },
2180
+
2181
+ // Remove this view by taking the element out of the DOM, and removing any
2182
+ // applicable Backbone.Events listeners.
2183
+ remove: function() {
2184
+ this.$el.remove();
2185
+ this.stopListening();
2186
+ return this;
2187
+ },
2188
+
2189
+ // Change the view's element (`this.el` property), including event
2190
+ // re-delegation.
2191
+ setElement: function(element, delegate) {
2192
+ if (this.$el) this.undelegateEvents();
2193
+ this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
2194
+ this.el = this.$el[0];
2195
+ if (delegate !== false) this.delegateEvents();
2196
+ return this;
2197
+ },
2198
+
2199
+ // Set callbacks, where `this.events` is a hash of
2200
+ //
2201
+ // *{"event selector": "callback"}*
2202
+ //
2203
+ // {
2204
+ // 'mousedown .title': 'edit',
2205
+ // 'click .button': 'save',
2206
+ // 'click .open': function(e) { ... }
2207
+ // }
2208
+ //
2209
+ // pairs. Callbacks will be bound to the view, with `this` set properly.
2210
+ // Uses event delegation for efficiency.
2211
+ // Omitting the selector binds the event to `this.el`.
2212
+ // This only works for delegate-able events: not `focus`, `blur`, and
2213
+ // not `change`, `submit`, and `reset` in Internet Explorer.
2214
+ delegateEvents: function(events) {
2215
+ if (!(events || (events = _.result(this, 'events')))) return this;
2216
+ this.undelegateEvents();
2217
+ for (var key in events) {
2218
+ var method = events[key];
2219
+ if (!_.isFunction(method)) method = this[events[key]];
2220
+ if (!method) continue;
2221
+
2222
+ var match = key.match(delegateEventSplitter);
2223
+ var eventName = match[1], selector = match[2];
2224
+ method = _.bind(method, this);
2225
+ eventName += '.delegateEvents' + this.cid;
2226
+ if (selector === '') {
2227
+ this.$el.on(eventName, method);
2228
+ } else {
2229
+ this.$el.on(eventName, selector, method);
2230
+ }
2231
+ }
2232
+ return this;
2233
+ },
2234
+
2235
+ // Clears all callbacks previously bound to the view with `delegateEvents`.
2236
+ // You usually don't need to use this, but may wish to if you have multiple
2237
+ // Backbone views attached to the same DOM element.
2238
+ undelegateEvents: function() {
2239
+ this.$el.off('.delegateEvents' + this.cid);
2240
+ return this;
2241
+ },
2242
+
2243
+ // Ensure that the View has a DOM element to render into.
2244
+ // If `this.el` is a string, pass it through `$()`, take the first
2245
+ // matching element, and re-assign it to `el`. Otherwise, create
2246
+ // an element from the `id`, `className` and `tagName` properties.
2247
+ _ensureElement: function() {
2248
+ if (!this.el) {
2249
+ var attrs = _.extend({}, _.result(this, 'attributes'));
2250
+ if (this.id) attrs.id = _.result(this, 'id');
2251
+ if (this.className) attrs['class'] = _.result(this, 'className');
2252
+ var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
2253
+ this.setElement($el, false);
2254
+ } else {
2255
+ this.setElement(_.result(this, 'el'), false);
2256
+ }
2257
+ }
2258
+
2259
+ });
2260
+
2261
+ // Backbone.sync
2262
+ // -------------
2263
+
2264
+ // Override this function to change the manner in which Backbone persists
2265
+ // models to the server. You will be passed the type of request, and the
2266
+ // model in question. By default, makes a RESTful Ajax request
2267
+ // to the model's `url()`. Some possible customizations could be:
2268
+ //
2269
+ // * Use `setTimeout` to batch rapid-fire updates into a single request.
2270
+ // * Send up the models as XML instead of JSON.
2271
+ // * Persist models via WebSockets instead of Ajax.
2272
+ //
2273
+ // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
2274
+ // as `POST`, with a `_method` parameter containing the true HTTP method,
2275
+ // as well as all requests with the body as `application/x-www-form-urlencoded`
2276
+ // instead of `application/json` with the model in a param named `model`.
2277
+ // Useful when interfacing with server-side languages like **PHP** that make
2278
+ // it difficult to read the body of `PUT` requests.
2279
+ Backbone.sync = function(method, model, options) {
2280
+ var type = methodMap[method];
2281
+
2282
+ // Default options, unless specified.
2283
+ _.defaults(options || (options = {}), {
2284
+ emulateHTTP: Backbone.emulateHTTP,
2285
+ emulateJSON: Backbone.emulateJSON
2286
+ });
2287
+
2288
+ // Default JSON-request options.
2289
+ var params = {type: type, dataType: 'json'};
2290
+
2291
+ // Ensure that we have a URL.
2292
+ if (!options.url) {
2293
+ params.url = _.result(model, 'url') || urlError();
2294
+ }
2295
+
2296
+ // Ensure that we have the appropriate request data.
2297
+ if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
2298
+ params.contentType = 'application/json';
2299
+ params.data = JSON.stringify(options.attrs || model.toJSON(options));
2300
+ }
2301
+
2302
+ // For older servers, emulate JSON by encoding the request into an HTML-form.
2303
+ if (options.emulateJSON) {
2304
+ params.contentType = 'application/x-www-form-urlencoded';
2305
+ params.data = params.data ? {model: params.data} : {};
2306
+ }
2307
+
2308
+ // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
2309
+ // And an `X-HTTP-Method-Override` header.
2310
+ if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
2311
+ params.type = 'POST';
2312
+ if (options.emulateJSON) params.data._method = type;
2313
+ var beforeSend = options.beforeSend;
2314
+ options.beforeSend = function(xhr) {
2315
+ xhr.setRequestHeader('X-HTTP-Method-Override', type);
2316
+ if (beforeSend) return beforeSend.apply(this, arguments);
2317
+ };
2318
+ }
2319
+
2320
+ // Don't process data on a non-GET request.
2321
+ if (params.type !== 'GET' && !options.emulateJSON) {
2322
+ params.processData = false;
2323
+ }
2324
+
2325
+ // If we're sending a `PATCH` request, and we're in an old Internet Explorer
2326
+ // that still has ActiveX enabled by default, override jQuery to use that
2327
+ // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
2328
+ if (params.type === 'PATCH' && noXhrPatch) {
2329
+ params.xhr = function() {
2330
+ return new ActiveXObject("Microsoft.XMLHTTP");
2331
+ };
2332
+ }
2333
+
2334
+ // Make the request, allowing the user to override any Ajax options.
2335
+ var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
2336
+ model.trigger('request', model, xhr, options);
2337
+ return xhr;
2338
+ };
2339
+
2340
+ var noXhrPatch =
2341
+ typeof window !== 'undefined' && !!window.ActiveXObject &&
2342
+ !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
2343
+
2344
+ // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
2345
+ var methodMap = {
2346
+ 'create': 'POST',
2347
+ 'update': 'PUT',
2348
+ 'patch': 'PATCH',
2349
+ 'delete': 'DELETE',
2350
+ 'read': 'GET'
2351
+ };
2352
+
2353
+ // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
2354
+ // Override this if you'd like to use a different library.
2355
+ Backbone.ajax = function() {
2356
+ return Backbone.$.ajax.apply(Backbone.$, arguments);
2357
+ };
2358
+
2359
+ // Backbone.Router
2360
+ // ---------------
2361
+
2362
+ // Routers map faux-URLs to actions, and fire events when routes are
2363
+ // matched. Creating a new one sets its `routes` hash, if not set statically.
2364
+ var Router = Backbone.Router = function(options) {
2365
+ options || (options = {});
2366
+ if (options.routes) this.routes = options.routes;
2367
+ this._bindRoutes();
2368
+ this.initialize.apply(this, arguments);
2369
+ };
2370
+
2371
+ // Cached regular expressions for matching named param parts and splatted
2372
+ // parts of route strings.
2373
+ var optionalParam = /\((.*?)\)/g;
2374
+ var namedParam = /(\(\?)?:\w+/g;
2375
+ var splatParam = /\*\w+/g;
2376
+ var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
2377
+
2378
+ // Set up all inheritable **Backbone.Router** properties and methods.
2379
+ _.extend(Router.prototype, Events, {
2380
+
2381
+ // Initialize is an empty function by default. Override it with your own
2382
+ // initialization logic.
2383
+ initialize: function(){},
2384
+
2385
+ // Manually bind a single named route to a callback. For example:
2386
+ //
2387
+ // this.route('search/:query/p:num', 'search', function(query, num) {
2388
+ // ...
2389
+ // });
2390
+ //
2391
+ route: function(route, name, callback) {
2392
+ if (!_.isRegExp(route)) route = this._routeToRegExp(route);
2393
+ if (_.isFunction(name)) {
2394
+ callback = name;
2395
+ name = '';
2396
+ }
2397
+ if (!callback) callback = this[name];
2398
+ var router = this;
2399
+ Backbone.history.route(route, function(fragment) {
2400
+ var args = router._extractParameters(route, fragment);
2401
+ router.execute(callback, args);
2402
+ router.trigger.apply(router, ['route:' + name].concat(args));
2403
+ router.trigger('route', name, args);
2404
+ Backbone.history.trigger('route', router, name, args);
2405
+ });
2406
+ return this;
2407
+ },
2408
+
2409
+ // Execute a route handler with the provided parameters. This is an
2410
+ // excellent place to do pre-route setup or post-route cleanup.
2411
+ execute: function(callback, args) {
2412
+ if (callback) callback.apply(this, args);
2413
+ },
2414
+
2415
+ // Simple proxy to `Backbone.history` to save a fragment into the history.
2416
+ navigate: function(fragment, options) {
2417
+ Backbone.history.navigate(fragment, options);
2418
+ return this;
2419
+ },
2420
+
2421
+ // Bind all defined routes to `Backbone.history`. We have to reverse the
2422
+ // order of the routes here to support behavior where the most general
2423
+ // routes can be defined at the bottom of the route map.
2424
+ _bindRoutes: function() {
2425
+ if (!this.routes) return;
2426
+ this.routes = _.result(this, 'routes');
2427
+ var route, routes = _.keys(this.routes);
2428
+ while ((route = routes.pop()) != null) {
2429
+ this.route(route, this.routes[route]);
2430
+ }
2431
+ },
2432
+
2433
+ // Convert a route string into a regular expression, suitable for matching
2434
+ // against the current location hash.
2435
+ _routeToRegExp: function(route) {
2436
+ route = route.replace(escapeRegExp, '\\$&')
2437
+ .replace(optionalParam, '(?:$1)?')
2438
+ .replace(namedParam, function(match, optional) {
2439
+ return optional ? match : '([^/?]+)';
2440
+ })
2441
+ .replace(splatParam, '([^?]*?)');
2442
+ return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
2443
+ },
2444
+
2445
+ // Given a route, and a URL fragment that it matches, return the array of
2446
+ // extracted decoded parameters. Empty or unmatched parameters will be
2447
+ // treated as `null` to normalize cross-browser behavior.
2448
+ _extractParameters: function(route, fragment) {
2449
+ var params = route.exec(fragment).slice(1);
2450
+ return _.map(params, function(param, i) {
2451
+ // Don't decode the search params.
2452
+ if (i === params.length - 1) return param || null;
2453
+ return param ? decodeURIComponent(param) : null;
2454
+ });
2455
+ }
2456
+
2457
+ });
2458
+
2459
+ // Backbone.History
2460
+ // ----------------
2461
+
2462
+ // Handles cross-browser history management, based on either
2463
+ // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
2464
+ // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
2465
+ // and URL fragments. If the browser supports neither (old IE, natch),
2466
+ // falls back to polling.
2467
+ var History = Backbone.History = function() {
2468
+ this.handlers = [];
2469
+ _.bindAll(this, 'checkUrl');
2470
+
2471
+ // Ensure that `History` can be used outside of the browser.
2472
+ if (typeof window !== 'undefined') {
2473
+ this.location = window.location;
2474
+ this.history = window.history;
2475
+ }
2476
+ };
2477
+
2478
+ // Cached regex for stripping a leading hash/slash and trailing space.
2479
+ var routeStripper = /^[#\/]|\s+$/g;
2480
+
2481
+ // Cached regex for stripping leading and trailing slashes.
2482
+ var rootStripper = /^\/+|\/+$/g;
2483
+
2484
+ // Cached regex for detecting MSIE.
2485
+ var isExplorer = /msie [\w.]+/;
2486
+
2487
+ // Cached regex for removing a trailing slash.
2488
+ var trailingSlash = /\/$/;
2489
+
2490
+ // Cached regex for stripping urls of hash.
2491
+ var pathStripper = /#.*$/;
2492
+
2493
+ // Has the history handling already been started?
2494
+ History.started = false;
2495
+
2496
+ // Set up all inheritable **Backbone.History** properties and methods.
2497
+ _.extend(History.prototype, Events, {
2498
+
2499
+ // The default interval to poll for hash changes, if necessary, is
2500
+ // twenty times a second.
2501
+ interval: 50,
2502
+
2503
+ // Are we at the app root?
2504
+ atRoot: function() {
2505
+ return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root;
2506
+ },
2507
+
2508
+ // Gets the true hash value. Cannot use location.hash directly due to bug
2509
+ // in Firefox where location.hash will always be decoded.
2510
+ getHash: function(window) {
2511
+ var match = (window || this).location.href.match(/#(.*)$/);
2512
+ return match ? match[1] : '';
2513
+ },
2514
+
2515
+ // Get the cross-browser normalized URL fragment, either from the URL,
2516
+ // the hash, or the override.
2517
+ getFragment: function(fragment, forcePushState) {
2518
+ if (fragment == null) {
2519
+ if (this._hasPushState || !this._wantsHashChange || forcePushState) {
2520
+ fragment = decodeURI(this.location.pathname + this.location.search);
2521
+ var root = this.root.replace(trailingSlash, '');
2522
+ if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
2523
+ } else {
2524
+ fragment = this.getHash();
2525
+ }
2526
+ }
2527
+ return fragment.replace(routeStripper, '');
2528
+ },
2529
+
2530
+ // Start the hash change handling, returning `true` if the current URL matches
2531
+ // an existing route, and `false` otherwise.
2532
+ start: function(options) {
2533
+ if (History.started) throw new Error("Backbone.history has already been started");
2534
+ History.started = true;
2535
+
2536
+ // Figure out the initial configuration. Do we need an iframe?
2537
+ // Is pushState desired ... is it available?
2538
+ this.options = _.extend({root: '/'}, this.options, options);
2539
+ this.root = this.options.root;
2540
+ this._wantsHashChange = this.options.hashChange !== false;
2541
+ this._wantsPushState = !!this.options.pushState;
2542
+ this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);
2543
+ var fragment = this.getFragment();
2544
+ var docMode = document.documentMode;
2545
+ var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
2546
+
2547
+ // Normalize root to always include a leading and trailing slash.
2548
+ this.root = ('/' + this.root + '/').replace(rootStripper, '/');
2549
+
2550
+ if (oldIE && this._wantsHashChange) {
2551
+ var frame = Backbone.$('<iframe src="javascript:0" tabindex="-1">');
2552
+ this.iframe = frame.hide().appendTo('body')[0].contentWindow;
2553
+ this.navigate(fragment);
2554
+ }
2555
+
2556
+ // Depending on whether we're using pushState or hashes, and whether
2557
+ // 'onhashchange' is supported, determine how we check the URL state.
2558
+ if (this._hasPushState) {
2559
+ Backbone.$(window).on('popstate', this.checkUrl);
2560
+ } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
2561
+ Backbone.$(window).on('hashchange', this.checkUrl);
2562
+ } else if (this._wantsHashChange) {
2563
+ this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
2564
+ }
2565
+
2566
+ // Determine if we need to change the base url, for a pushState link
2567
+ // opened by a non-pushState browser.
2568
+ this.fragment = fragment;
2569
+ var loc = this.location;
2570
+
2571
+ // Transition from hashChange to pushState or vice versa if both are
2572
+ // requested.
2573
+ if (this._wantsHashChange && this._wantsPushState) {
2574
+
2575
+ // If we've started off with a route from a `pushState`-enabled
2576
+ // browser, but we're currently in a browser that doesn't support it...
2577
+ if (!this._hasPushState && !this.atRoot()) {
2578
+ this.fragment = this.getFragment(null, true);
2579
+ this.location.replace(this.root + '#' + this.fragment);
2580
+ // Return immediately as browser will do redirect to new url
2581
+ return true;
2582
+
2583
+ // Or if we've started out with a hash-based route, but we're currently
2584
+ // in a browser where it could be `pushState`-based instead...
2585
+ } else if (this._hasPushState && this.atRoot() && loc.hash) {
2586
+ this.fragment = this.getHash().replace(routeStripper, '');
2587
+ this.history.replaceState({}, document.title, this.root + this.fragment);
2588
+ }
2589
+
2590
+ }
2591
+
2592
+ if (!this.options.silent) return this.loadUrl();
2593
+ },
2594
+
2595
+ // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
2596
+ // but possibly useful for unit testing Routers.
2597
+ stop: function() {
2598
+ Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
2599
+ if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
2600
+ History.started = false;
2601
+ },
2602
+
2603
+ // Add a route to be tested when the fragment changes. Routes added later
2604
+ // may override previous routes.
2605
+ route: function(route, callback) {
2606
+ this.handlers.unshift({route: route, callback: callback});
2607
+ },
2608
+
2609
+ // Checks the current URL to see if it has changed, and if it has,
2610
+ // calls `loadUrl`, normalizing across the hidden iframe.
2611
+ checkUrl: function(e) {
2612
+ var current = this.getFragment();
2613
+ if (current === this.fragment && this.iframe) {
2614
+ current = this.getFragment(this.getHash(this.iframe));
2615
+ }
2616
+ if (current === this.fragment) return false;
2617
+ if (this.iframe) this.navigate(current);
2618
+ this.loadUrl();
2619
+ },
2620
+
2621
+ // Attempt to load the current URL fragment. If a route succeeds with a
2622
+ // match, returns `true`. If no defined routes matches the fragment,
2623
+ // returns `false`.
2624
+ loadUrl: function(fragment) {
2625
+ fragment = this.fragment = this.getFragment(fragment);
2626
+ return _.any(this.handlers, function(handler) {
2627
+ if (handler.route.test(fragment)) {
2628
+ handler.callback(fragment);
2629
+ return true;
2630
+ }
2631
+ });
2632
+ },
2633
+
2634
+ // Save a fragment into the hash history, or replace the URL state if the
2635
+ // 'replace' option is passed. You are responsible for properly URL-encoding
2636
+ // the fragment in advance.
2637
+ //
2638
+ // The options object can contain `trigger: true` if you wish to have the
2639
+ // route callback be fired (not usually desirable), or `replace: true`, if
2640
+ // you wish to modify the current URL without adding an entry to the history.
2641
+ navigate: function(fragment, options) {
2642
+ if (!History.started) return false;
2643
+ if (!options || options === true) options = {trigger: !!options};
2644
+
2645
+ var url = this.root + (fragment = this.getFragment(fragment || ''));
2646
+
2647
+ // Strip the hash for matching.
2648
+ fragment = fragment.replace(pathStripper, '');
2649
+
2650
+ if (this.fragment === fragment) return;
2651
+ this.fragment = fragment;
2652
+
2653
+ // Don't include a trailing slash on the root.
2654
+ if (fragment === '' && url !== '/') url = url.slice(0, -1);
2655
+
2656
+ // If pushState is available, we use it to set the fragment as a real URL.
2657
+ if (this._hasPushState) {
2658
+ this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
2659
+
2660
+ // If hash changes haven't been explicitly disabled, update the hash
2661
+ // fragment to store history.
2662
+ } else if (this._wantsHashChange) {
2663
+ this._updateHash(this.location, fragment, options.replace);
2664
+ if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
2665
+ // Opening and closing the iframe tricks IE7 and earlier to push a
2666
+ // history entry on hash-tag change. When replace is true, we don't
2667
+ // want this.
2668
+ if(!options.replace) this.iframe.document.open().close();
2669
+ this._updateHash(this.iframe.location, fragment, options.replace);
2670
+ }
2671
+
2672
+ // If you've told us that you explicitly don't want fallback hashchange-
2673
+ // based history, then `navigate` becomes a page refresh.
2674
+ } else {
2675
+ return this.location.assign(url);
2676
+ }
2677
+ if (options.trigger) return this.loadUrl(fragment);
2678
+ },
2679
+
2680
+ // Update the hash location, either replacing the current entry, or adding
2681
+ // a new one to the browser history.
2682
+ _updateHash: function(location, fragment, replace) {
2683
+ if (replace) {
2684
+ var href = location.href.replace(/(javascript:|#).*$/, '');
2685
+ location.replace(href + '#' + fragment);
2686
+ } else {
2687
+ // Some browsers require that `hash` contains a leading #.
2688
+ location.hash = '#' + fragment;
2689
+ }
2690
+ }
2691
+
2692
+ });
2693
+
2694
+ // Create the default Backbone.history.
2695
+ Backbone.history = new History;
2696
+
2697
+ // Helpers
2698
+ // -------
2699
+
2700
+ // Helper function to correctly set up the prototype chain, for subclasses.
2701
+ // Similar to `goog.inherits`, but uses a hash of prototype properties and
2702
+ // class properties to be extended.
2703
+ var extend = function(protoProps, staticProps) {
2704
+ var parent = this;
2705
+ var child;
2706
+
2707
+ // The constructor function for the new subclass is either defined by you
2708
+ // (the "constructor" property in your `extend` definition), or defaulted
2709
+ // by us to simply call the parent's constructor.
2710
+ if (protoProps && _.has(protoProps, 'constructor')) {
2711
+ child = protoProps.constructor;
2712
+ } else {
2713
+ child = function(){ return parent.apply(this, arguments); };
2714
+ }
2715
+
2716
+ // Add static properties to the constructor function, if supplied.
2717
+ _.extend(child, parent, staticProps);
2718
+
2719
+ // Set the prototype chain to inherit from `parent`, without calling
2720
+ // `parent`'s constructor function.
2721
+ var Surrogate = function(){ this.constructor = child; };
2722
+ Surrogate.prototype = parent.prototype;
2723
+ child.prototype = new Surrogate;
2724
+
2725
+ // Add prototype properties (instance properties) to the subclass,
2726
+ // if supplied.
2727
+ if (protoProps) _.extend(child.prototype, protoProps);
2728
+
2729
+ // Set a convenience property in case the parent's prototype is needed
2730
+ // later.
2731
+ child.__super__ = parent.prototype;
2732
+
2733
+ return child;
2734
+ };
2735
+
2736
+ // Set up inheritance for the model, collection, router, view and history.
2737
+ Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
2738
+
2739
+ // Throw an error when a URL is needed, and none is supplied.
2740
+ var urlError = function() {
2741
+ throw new Error('A "url" property or function must be specified');
2742
+ };
2743
+
2744
+ // Wrap an optional error callback with a fallback error event.
2745
+ var wrapError = function(model, options) {
2746
+ var error = options.error;
2747
+ options.error = function(resp) {
2748
+ if (error) error(model, resp, options);
2749
+ model.trigger('error', model, resp, options);
2750
+ };
2751
+ };
2752
+
2753
+ return Backbone;
2754
+
2755
+ }));
2756
+
2757
+ /**
2758
+ * Main source
2759
+ */
2760
+
2761
+ ;(function(factory) {
2762
+ if (typeof define === 'function' && define.amd) {
2763
+ // AMD
2764
+ define(['underscore', 'backbone'], factory);
2765
+ } else {
2766
+ // globals
2767
+ factory(_, Backbone);
2768
+ }
2769
+ }(function(_, Backbone) {
2770
+
2771
+ /**
2772
+ * Takes a nested object and returns a shallow object keyed with the path names
2773
+ * e.g. { "level1.level2": "value" }
2774
+ *
2775
+ * @param {Object} Nested object e.g. { level1: { level2: 'value' } }
2776
+ * @return {Object} Shallow object with path names e.g. { 'level1.level2': 'value' }
2777
+ */
2778
+ function objToPaths(obj) {
2779
+ var ret = {},
2780
+ separator = DeepModel.keyPathSeparator;
2781
+
2782
+ for (var key in obj) {
2783
+ var val = obj[key];
2784
+
2785
+ if (val && val.constructor === Object && !_.isEmpty(val)) {
2786
+ //Recursion for embedded objects
2787
+ var obj2 = objToPaths(val);
2788
+
2789
+ for (var key2 in obj2) {
2790
+ var val2 = obj2[key2];
2791
+
2792
+ ret[key + separator + key2] = val2;
2793
+ }
2794
+ } else {
2795
+ ret[key] = val;
2796
+ }
2797
+ }
2798
+
2799
+ return ret;
2800
+ }
2801
+
2802
+ /**
2803
+ * @param {Object} Object to fetch attribute from
2804
+ * @param {String} Object path e.g. 'user.name'
2805
+ * @return {Mixed}
2806
+ */
2807
+ function getNested(obj, path, return_exists) {
2808
+ var separator = DeepModel.keyPathSeparator;
2809
+
2810
+ var fields = path.split(separator);
2811
+ var result = obj;
2812
+ return_exists || (return_exists === false);
2813
+ for (var i = 0, n = fields.length; i < n; i++) {
2814
+ if (return_exists && !_.has(result, fields[i])) {
2815
+ return false;
2816
+ }
2817
+ result = result[fields[i]];
2818
+
2819
+ if (result == null && i < n - 1) {
2820
+ result = {};
2821
+ }
2822
+
2823
+ if (typeof result === 'undefined') {
2824
+ if (return_exists)
2825
+ {
2826
+ return true;
2827
+ }
2828
+ return result;
2829
+ }
2830
+ }
2831
+ if (return_exists)
2832
+ {
2833
+ return true;
2834
+ }
2835
+ return result;
2836
+ }
2837
+
2838
+ /**
2839
+ * @param {Object} obj Object to fetch attribute from
2840
+ * @param {String} path Object path e.g. 'user.name'
2841
+ * @param {Object} [options] Options
2842
+ * @param {Boolean} [options.unset] Whether to delete the value
2843
+ * @param {Mixed} Value to set
2844
+ */
2845
+ function setNested(obj, path, val, options) {
2846
+ options = options || {};
2847
+
2848
+ var separator = DeepModel.keyPathSeparator;
2849
+
2850
+ var fields = path.split(separator);
2851
+ var result = obj;
2852
+ for (var i = 0, n = fields.length; i < n && result !== undefined ; i++) {
2853
+ var field = fields[i];
2854
+
2855
+ //If the last in the path, set the value
2856
+ if (i === n - 1) {
2857
+ options.unset ? delete result[field] : result[field] = val;
2858
+ } else {
2859
+ //Create the child object if it doesn't exist, or isn't an object
2860
+ if (typeof result[field] === 'undefined' || ! _.isObject(result[field])) {
2861
+ result[field] = {};
2862
+ }
2863
+
2864
+ //Move onto the next part of the path
2865
+ result = result[field];
2866
+ }
2867
+ }
2868
+ }
2869
+
2870
+ function deleteNested(obj, path) {
2871
+ setNested(obj, path, null, { unset: true });
2872
+ }
2873
+
2874
+ var DeepModel = Backbone.Model.extend({
2875
+
2876
+ // Override constructor
2877
+ // Support having nested defaults by using _.deepExtend instead of _.extend
2878
+ constructor: function(attributes, options) {
2879
+ var defaults;
2880
+ var attrs = attributes || {};
2881
+ this.cid = _.uniqueId('c');
2882
+ this.attributes = {};
2883
+ if (options && options.collection) this.collection = options.collection;
2884
+ if (options && options.parse) attrs = this.parse(attrs, options) || {};
2885
+ if (defaults = _.result(this, 'defaults')) {
2886
+ //<custom code>
2887
+ // Replaced the call to _.defaults with _.deepExtend.
2888
+ attrs = _.deepExtend({}, defaults, attrs);
2889
+ //</custom code>
2890
+ }
2891
+ this.set(attrs, options);
2892
+ this.changed = {};
2893
+ this.initialize.apply(this, arguments);
2894
+ },
2895
+
2896
+ // Return a copy of the model's `attributes` object.
2897
+ toJSON: function(options) {
2898
+ return _.deepClone(this.attributes);
2899
+ },
2900
+
2901
+ // Override get
2902
+ // Supports nested attributes via the syntax 'obj.attr' e.g. 'author.user.name'
2903
+ get: function(attr) {
2904
+ return getNested(this.attributes, attr);
2905
+ },
2906
+
2907
+ // Override set
2908
+ // Supports nested attributes via the syntax 'obj.attr' e.g. 'author.user.name'
2909
+ set: function(key, val, options) {
2910
+ var attr, attrs, unset, changes, silent, changing, prev, current;
2911
+ if (key == null) return this;
2912
+
2913
+ // Handle both `"key", value` and `{key: value}` -style arguments.
2914
+ if (typeof key === 'object') {
2915
+ attrs = key;
2916
+ options = val || {};
2917
+ } else {
2918
+ (attrs = {})[key] = val;
2919
+ }
2920
+
2921
+ options || (options = {});
2922
+
2923
+ // Run validation.
2924
+ if (!this._validate(attrs, options)) return false;
2925
+
2926
+ // Extract attributes and options.
2927
+ unset = options.unset;
2928
+ silent = options.silent;
2929
+ changes = [];
2930
+ changing = this._changing;
2931
+ this._changing = true;
2932
+
2933
+ if (!changing) {
2934
+ this._previousAttributes = _.deepClone(this.attributes); //<custom>: Replaced _.clone with _.deepClone
2935
+ this.changed = {};
2936
+ }
2937
+ current = this.attributes, prev = this._previousAttributes;
2938
+
2939
+ // Check for changes of `id`.
2940
+ if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
2941
+
2942
+ //<custom code>
2943
+ attrs = objToPaths(attrs);
2944
+ //</custom code>
2945
+
2946
+ // For each `set` attribute, update or delete the current value.
2947
+ for (attr in attrs) {
2948
+ val = attrs[attr];
2949
+
2950
+ //<custom code>: Using getNested, setNested and deleteNested
2951
+ if (!_.isEqual(getNested(current, attr), val)) changes.push(attr);
2952
+ if (!_.isEqual(getNested(prev, attr), val)) {
2953
+ setNested(this.changed, attr, val);
2954
+ } else {
2955
+ deleteNested(this.changed, attr);
2956
+ }
2957
+ unset ? deleteNested(current, attr) : setNested(current, attr, val);
2958
+ //</custom code>
2959
+ }
2960
+
2961
+ // Trigger all relevant attribute changes.
2962
+ if (!silent) {
2963
+ if (changes.length) this._pending = true;
2964
+
2965
+ //<custom code>
2966
+ var separator = DeepModel.keyPathSeparator;
2967
+
2968
+ for (var i = 0, l = changes.length; i < l; i++) {
2969
+ var key = changes[i];
2970
+
2971
+ this.trigger('change:' + key, this, getNested(current, key), options);
2972
+
2973
+ var fields = key.split(separator);
2974
+
2975
+ //Trigger change events for parent keys with wildcard (*) notation
2976
+ for(var n = fields.length - 1; n > 0; n--) {
2977
+ var parentKey = _.first(fields, n).join(separator),
2978
+ wildcardKey = parentKey + separator + '*';
2979
+
2980
+ this.trigger('change:' + wildcardKey, this, getNested(current, parentKey), options);
2981
+ }
2982
+ //</custom code>
2983
+ }
2984
+ }
2985
+
2986
+ if (changing) return this;
2987
+ if (!silent) {
2988
+ while (this._pending) {
2989
+ this._pending = false;
2990
+ this.trigger('change', this, options);
2991
+ }
2992
+ }
2993
+ this._pending = false;
2994
+ this._changing = false;
2995
+ return this;
2996
+ },
2997
+
2998
+ // Clear all attributes on the model, firing `"change"` unless you choose
2999
+ // to silence it.
3000
+ clear: function(options) {
3001
+ var attrs = {};
3002
+ var shallowAttributes = objToPaths(this.attributes);
3003
+ for (var key in shallowAttributes) attrs[key] = void 0;
3004
+ return this.set(attrs, _.extend({}, options, {unset: true}));
3005
+ },
3006
+
3007
+ // Determine if the model has changed since the last `"change"` event.
3008
+ // If you specify an attribute name, determine if that attribute has changed.
3009
+ hasChanged: function(attr) {
3010
+ if (attr == null) return !_.isEmpty(this.changed);
3011
+ return getNested(this.changed, attr) !== undefined;
3012
+ },
3013
+
3014
+ // Return an object containing all the attributes that have changed, or
3015
+ // false if there are no changed attributes. Useful for determining what
3016
+ // parts of a view need to be updated and/or what attributes need to be
3017
+ // persisted to the server. Unset attributes will be set to undefined.
3018
+ // You can also pass an attributes object to diff against the model,
3019
+ // determining if there *would be* a change.
3020
+ changedAttributes: function(diff) {
3021
+ //<custom code>: objToPaths
3022
+ if (!diff) return this.hasChanged() ? objToPaths(this.changed) : false;
3023
+ //</custom code>
3024
+
3025
+ var old = this._changing ? this._previousAttributes : this.attributes;
3026
+
3027
+ //<custom code>
3028
+ diff = objToPaths(diff);
3029
+ old = objToPaths(old);
3030
+ //</custom code>
3031
+
3032
+ var val, changed = false;
3033
+ for (var attr in diff) {
3034
+ if (_.isEqual(old[attr], (val = diff[attr]))) continue;
3035
+ (changed || (changed = {}))[attr] = val;
3036
+ }
3037
+ return changed;
3038
+ },
3039
+
3040
+ // Get the previous value of an attribute, recorded at the time the last
3041
+ // `"change"` event was fired.
3042
+ previous: function(attr) {
3043
+ if (attr == null || !this._previousAttributes) return null;
3044
+
3045
+ //<custom code>
3046
+ return getNested(this._previousAttributes, attr);
3047
+ //</custom code>
3048
+ },
3049
+
3050
+ // Get all of the attributes of the model at the time of the previous
3051
+ // `"change"` event.
3052
+ previousAttributes: function() {
3053
+ //<custom code>
3054
+ return _.deepClone(this._previousAttributes);
3055
+ //</custom code>
3056
+ }
3057
+ });
3058
+
3059
+
3060
+ //Config; override in your app to customise
3061
+ DeepModel.keyPathSeparator = '.';
3062
+
3063
+
3064
+ //Exports
3065
+ Backbone.DeepModel = DeepModel;
3066
+
3067
+ //For use in NodeJS
3068
+ if (typeof module != 'undefined') module.exports = DeepModel;
3069
+
3070
+ return Backbone;
3071
+
3072
+ }));