ab_admin 0.3.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (419) hide show
  1. checksums.yaml +7 -0
  2. data/{LICENSE.txt → MIT-LICENSE} +2 -4
  3. data/README.md +3 -3
  4. data/Rakefile +21 -3
  5. data/app/assets/images/admin/flags/b_de.png +0 -0
  6. data/app/assets/images/admin/flags/b_es.png +0 -0
  7. data/app/assets/images/admin/flags/b_fr.png +0 -0
  8. data/app/assets/images/admin/flags/flag_de.gif +0 -0
  9. data/app/assets/images/admin/flags/flag_de_nonact.gif +0 -0
  10. data/app/assets/images/admin/flags/flag_es.gif +0 -0
  11. data/app/assets/images/admin/flags/flag_es_nonact.gif +0 -0
  12. data/app/assets/images/admin/flags/flag_fr.gif +0 -0
  13. data/app/assets/images/admin/flags/flag_fr_nonact.gif +0 -0
  14. data/app/assets/javascripts/ab_admin/components/admin_assets.js.coffee +50 -60
  15. data/app/assets/javascripts/ab_admin/components/base_assets.js.coffee +86 -0
  16. data/app/assets/javascripts/ab_admin/components/croppable_image.js.coffee +4 -4
  17. data/app/assets/javascripts/ab_admin/components/gmaps.js.coffee +5 -0
  18. data/app/assets/javascripts/ab_admin/components/google_translate.js.coffee +0 -1
  19. data/app/assets/javascripts/ab_admin/components/in_place_edit.js.coffee +7 -3
  20. data/app/assets/javascripts/ab_admin/components/locator.js.coffee +1 -1
  21. data/app/assets/javascripts/ab_admin/components/select2_bridge.js.coffee +152 -0
  22. data/app/assets/javascripts/ab_admin/components/sortable_tree.js.coffee +1 -1
  23. data/app/assets/javascripts/ab_admin/core/batch_actions.js.coffee +5 -4
  24. data/app/assets/javascripts/ab_admin/core/columns_hider.js.coffee +4 -11
  25. data/app/assets/javascripts/ab_admin/core/confirmation.js.coffee +4 -43
  26. data/app/assets/javascripts/ab_admin/core/deprecated_utils.js.coffee +44 -0
  27. data/app/assets/javascripts/ab_admin/core/init.js.coffee +8 -25
  28. data/app/assets/javascripts/ab_admin/core/pjax.js.coffee +18 -4
  29. data/app/assets/javascripts/ab_admin/core/search_form.js.coffee +5 -5
  30. data/app/assets/javascripts/ab_admin/core/ui_utils.js.coffee +15 -86
  31. data/app/assets/javascripts/ab_admin/core/utils.js.coffee +38 -51
  32. data/app/assets/javascripts/ab_admin/core/view_layout.js.coffee +1 -1
  33. data/app/assets/javascripts/ab_admin/inputs/datetime_input.js.coffee +16 -18
  34. data/app/assets/javascripts/ab_admin/main.js +7 -7
  35. data/app/assets/stylesheets/ab_admin/bootstrap_and_overrides.css.scss +21 -8
  36. data/app/assets/stylesheets/ab_admin/components/_admin_comments.css.scss +17 -5
  37. data/app/assets/stylesheets/ab_admin/components/_base.css.scss +4 -0
  38. data/app/assets/stylesheets/ab_admin/components/_colored_tabs.css.scss +1 -4
  39. data/app/assets/stylesheets/ab_admin/components/_columns_hider.css.scss +5 -0
  40. data/app/assets/stylesheets/ab_admin/components/_form.css.scss +45 -16
  41. data/app/assets/stylesheets/ab_admin/components/_grid_view.css.scss +11 -0
  42. data/app/assets/stylesheets/ab_admin/components/_locale_tabs.css.scss +1 -1
  43. data/app/assets/stylesheets/ab_admin/components/_navigation.css.scss +11 -9
  44. data/app/assets/stylesheets/ab_admin/components/_table_view.css.scss +1 -1
  45. data/app/assets/stylesheets/ab_admin/components/_translations.css.scss +1 -0
  46. data/app/assets/stylesheets/ab_admin/components/{_sortable_tree.css.scss → _tree_view.css.scss} +8 -3
  47. data/app/assets/stylesheets/ab_admin/fileupload.css.scss +51 -43
  48. data/app/assets/stylesheets/ab_admin/main.css.scss +1 -3
  49. data/app/controllers/admin/admin_comments_controller.rb +14 -4
  50. data/app/controllers/admin/assets_controller.rb +44 -35
  51. data/app/controllers/admin/base_controller.rb +93 -38
  52. data/app/controllers/admin/locators_controller.rb +3 -2
  53. data/app/controllers/admin/manager_controller.rb +20 -9
  54. data/app/controllers/admin/structures_controller.rb +9 -1
  55. data/app/controllers/admin/users_controller.rb +1 -2
  56. data/app/views/ab_admin/devise/sessions/new.html.slim +1 -1
  57. data/app/views/admin/admin_comments/_comment.html.slim +21 -7
  58. data/app/views/admin/admin_comments/_comments.html.slim +1 -1
  59. data/app/views/admin/admin_comments/_form.html.slim +8 -3
  60. data/app/views/admin/admin_comments/_list_block.html.slim +7 -0
  61. data/app/views/admin/admin_comments/create.js.erb +3 -2
  62. data/app/views/admin/admin_comments/destroy.js.erb +1 -1
  63. data/app/views/admin/admin_comments/index.js.erb +14 -0
  64. data/app/views/admin/assets/batch_edit.html.slim +14 -0
  65. data/app/views/admin/base/_modal_form_layout.html.slim +12 -0
  66. data/app/views/admin/base/_search_layout.html.slim +4 -3
  67. data/app/views/admin/base/_table.html.slim +2 -2
  68. data/app/views/admin/base/create.js.erb +13 -8
  69. data/app/views/admin/base/history.html.slim +18 -0
  70. data/app/views/admin/base/index.html.slim +3 -3
  71. data/app/views/admin/base/new.js.erb +14 -8
  72. data/app/views/admin/base/show.html.slim +1 -2
  73. data/app/views/admin/base/show.js.erb +7 -0
  74. data/app/views/admin/fileupload/_asset_templates.html.slim +29 -0
  75. data/app/views/admin/fileupload/_container.html.slim +13 -12
  76. data/app/views/admin/fileupload/_file.html.slim +2 -2
  77. data/app/views/admin/fileupload/_image.html.slim +8 -0
  78. data/app/views/admin/fileupload/_video.html.slim +2 -2
  79. data/app/views/admin/headers/_form.html.slim +2 -0
  80. data/app/views/admin/locators/edit.html.slim +1 -1
  81. data/app/views/admin/locators/show.html.slim +1 -2
  82. data/app/views/admin/manager/_form.html.slim +17 -3
  83. data/app/views/admin/manager/_modal_form.html.slim +19 -0
  84. data/app/views/admin/manager/_table.html.slim +3 -3
  85. data/app/views/admin/manager/show.html.slim +1 -0
  86. data/app/views/admin/shared/_content_actions.html.slim +39 -0
  87. data/app/views/admin/shared/_locale_tabs.html.slim +3 -3
  88. data/app/views/admin/shared/inputs/_checkbox_tree.html.slim +9 -3
  89. data/app/views/admin/structures/_form.html.slim +4 -7
  90. data/app/views/admin/users/_form.html.slim +2 -4
  91. data/app/views/admin/users/_search_form.html.slim +0 -1
  92. data/app/views/admin/users/_table.html.slim +8 -10
  93. data/app/views/layouts/admin/_footer.html.slim +3 -4
  94. data/app/views/layouts/admin/_navigation.html.slim +2 -2
  95. data/app/views/layouts/admin/application.html.slim +7 -9
  96. data/app/views/layouts/admin/devise.html.slim +3 -4
  97. data/config/locales/de.yml +319 -0
  98. data/config/locales/en.devise.yml +44 -31
  99. data/config/locales/en.yml +166 -143
  100. data/config/locales/it.devise.yml +54 -41
  101. data/config/locales/it.yml +178 -163
  102. data/config/locales/ru.devise.yml +70 -56
  103. data/config/locales/ru.yml +280 -264
  104. data/config/routes.rb +6 -3
  105. data/db/migrate/20130101000001_create_users.rb +7 -11
  106. data/db/migrate/20130101000003_create_assets.rb +5 -5
  107. data/db/migrate/20130101000004_create_headers.rb +2 -6
  108. data/db/migrate/20130101000005_create_static_pages.rb +1 -5
  109. data/db/migrate/20130101000006_create_structures.rb +5 -9
  110. data/db/migrate/20130101000007_base_translations.rb +3 -1
  111. data/db/migrate/20130101000008_create_admin_comments.rb +5 -5
  112. data/db/migrate/20130101000009_create_tracks.rb +20 -0
  113. data/lib/ab_admin.rb +36 -4
  114. data/lib/ab_admin/abstract_resource.rb +28 -7
  115. data/lib/ab_admin/carrierwave/base_uploader.rb +85 -5
  116. data/lib/ab_admin/carrierwave/glue.rb +6 -1
  117. data/lib/ab_admin/concerns/admin_addition.rb +26 -9
  118. data/lib/ab_admin/concerns/asset_human_names.rb +23 -0
  119. data/lib/ab_admin/concerns/fileuploads.rb +102 -0
  120. data/lib/ab_admin/concerns/has_tracking.rb +32 -0
  121. data/lib/ab_admin/concerns/nested_set.rb +9 -9
  122. data/lib/ab_admin/concerns/silencer.rb +5 -5
  123. data/lib/ab_admin/concerns/utilities.rb +28 -16
  124. data/lib/ab_admin/concerns/validations.rb +1 -1
  125. data/lib/ab_admin/config/base.rb +10 -2
  126. data/lib/ab_admin/controllers/callbacks.rb +2 -2
  127. data/lib/ab_admin/controllers/can_can_manager_resource.rb +3 -2
  128. data/lib/ab_admin/controllers/fv.rb +16 -0
  129. data/lib/ab_admin/controllers/head_options.rb +6 -4
  130. data/lib/ab_admin/core_ext/hash.rb +25 -70
  131. data/lib/ab_admin/core_ext/string.rb +17 -4
  132. data/lib/ab_admin/engine.rb +2 -8
  133. data/lib/ab_admin/hooks.rb +8 -3
  134. data/lib/ab_admin/hooks/active_model_hooks.rb +1 -1
  135. data/lib/ab_admin/hooks/globalize_hooks.rb +35 -0
  136. data/lib/ab_admin/hooks/paginate_hooks.rb +3 -1
  137. data/lib/ab_admin/i18n_tools/model_translator.rb +1 -1
  138. data/lib/ab_admin/menu/abstract_builder.rb +14 -0
  139. data/lib/ab_admin/menu/base_group.rb +26 -0
  140. data/lib/ab_admin/menu/builder.rb +16 -0
  141. data/lib/ab_admin/menu/group.rb +24 -0
  142. data/lib/ab_admin/menu/item.rb +23 -0
  143. data/lib/ab_admin/models/admin_comment.rb +68 -0
  144. data/lib/ab_admin/models/asset.rb +30 -19
  145. data/lib/ab_admin/models/attachment_file.rb +2 -1
  146. data/lib/ab_admin/models/header.rb +15 -0
  147. data/lib/ab_admin/models/locator.rb +1 -3
  148. data/lib/ab_admin/models/settings.rb +10 -10
  149. data/lib/ab_admin/models/structure.rb +20 -9
  150. data/lib/ab_admin/models/track.rb +61 -0
  151. data/lib/ab_admin/models/type_model.rb +30 -17
  152. data/lib/ab_admin/models/user.rb +18 -49
  153. data/lib/ab_admin/utils.rb +16 -7
  154. data/lib/ab_admin/utils/logger.rb +11 -7
  155. data/lib/ab_admin/utils/mysql.rb +10 -0
  156. data/lib/ab_admin/version.rb +1 -1
  157. data/lib/ab_admin/views/admin_helpers.rb +20 -9
  158. data/lib/ab_admin/views/admin_navigation_helpers.rb +44 -21
  159. data/lib/ab_admin/views/content_only_wrapper.rb +14 -0
  160. data/lib/ab_admin/views/form_builder.rb +27 -89
  161. data/lib/ab_admin/views/helpers.rb +1 -28
  162. data/lib/ab_admin/views/inputs/capture_block_input.rb +16 -0
  163. data/lib/ab_admin/views/inputs/date_time_picker_input.rb +45 -0
  164. data/lib/ab_admin/views/inputs/token_input.rb +2 -1
  165. data/lib/ab_admin/views/inputs/uploader_input.rb +101 -0
  166. data/lib/ab_admin/views/manager_helpers.rb +12 -0
  167. data/lib/ab_admin/views/search_form_builder.rb +36 -13
  168. data/lib/generators/ab_admin/ckeditor_assets/ckeditor_assets_generator.rb +1 -1
  169. data/lib/generators/ab_admin/install/install_generator.rb +3 -2
  170. data/lib/generators/ab_admin/install/templates/config/ab_admin.rb.erb +1 -1
  171. data/lib/generators/ab_admin/install/templates/config/admin_menu.rb +1 -1
  172. data/lib/generators/ab_admin/install/templates/config/i18n-js.yml +1 -1
  173. data/lib/generators/ab_admin/install/templates/config/nginx.conf +22 -9
  174. data/lib/generators/ab_admin/install/templates/config/robots.txt +7 -0
  175. data/lib/generators/ab_admin/install/templates/config/seeds.rb +2 -1
  176. data/lib/generators/ab_admin/install/templates/models/ability.rb +2 -7
  177. data/lib/generators/ab_admin/install/templates/models/admin_comment.rb +3 -28
  178. data/lib/generators/ab_admin/install/templates/models/asset.rb +4 -2
  179. data/lib/generators/ab_admin/install/templates/models/attachment_file.rb +1 -1
  180. data/lib/generators/ab_admin/install/templates/models/avatar.rb +2 -2
  181. data/lib/generators/ab_admin/install/templates/models/gender_type.rb +1 -1
  182. data/lib/generators/ab_admin/install/templates/models/header.rb +2 -2
  183. data/lib/generators/ab_admin/install/templates/models/locator.rb +0 -1
  184. data/lib/generators/ab_admin/install/templates/models/picture.rb +1 -1
  185. data/lib/generators/ab_admin/install/templates/models/settings.rb +1 -1
  186. data/lib/generators/ab_admin/install/templates/models/structure.rb +6 -8
  187. data/lib/generators/ab_admin/install/templates/models/track.rb +5 -0
  188. data/lib/generators/ab_admin/install/templates/models/user.rb +2 -3
  189. data/lib/generators/ab_admin/install/templates/script/unicorn.sh +0 -2
  190. data/lib/generators/ab_admin/install/templates/spec/factories/user_factory.rb +1 -2
  191. data/lib/generators/ab_admin/install/templates/uploaders/attachment_file_uploader.rb +1 -1
  192. data/lib/generators/ab_admin/install/templates/uploaders/avatar_uploader.rb +1 -1
  193. data/lib/generators/ab_admin/install/templates/uploaders/picture_uploader.rb +1 -1
  194. data/lib/generators/ab_admin/model/templates/resource.erb +1 -1
  195. data/lib/generators/ab_admin/resource/templates/_form.slim.erb +49 -45
  196. data/lib/generators/ab_admin/resource/templates/_table.slim.erb +1 -1
  197. data/lib/generators/template.rb +32 -56
  198. data/lib/tasks/assets.rake +1 -0
  199. metadata +197 -698
  200. data/.gitignore +0 -41
  201. data/Gemfile +0 -77
  202. data/Guardfile +0 -35
  203. data/TODO +0 -4
  204. data/ab_admin.gemspec +0 -59
  205. data/app/views/admin/fileupload/_asset.html.slim +0 -8
  206. data/app/views/admin/fileupload/_ftmpl.html.slim +0 -6
  207. data/app/views/admin/fileupload/_tmpl.html.slim +0 -9
  208. data/app/views/admin/fileupload/_vtmpl.html.slim +0 -10
  209. data/app/views/admin/shared/_batch_actions.html.slim +0 -27
  210. data/cucumber.yml +0 -10
  211. data/features/dsl/action_items.feature +0 -79
  212. data/features/dsl/admin_comments.feature +0 -30
  213. data/features/dsl/batch_actions.feature +0 -69
  214. data/features/dsl/config.feature +0 -44
  215. data/features/dsl/custom_actions.feature +0 -62
  216. data/features/dsl/export.feature +0 -42
  217. data/features/dsl/form.feature +0 -77
  218. data/features/dsl/in_place_edit.feature +0 -26
  219. data/features/dsl/list_edit.feature +0 -46
  220. data/features/dsl/parent_resource.feature +0 -18
  221. data/features/dsl/resource_action_items.feature +0 -40
  222. data/features/dsl/search.feature +0 -41
  223. data/features/dsl/show.feature +0 -37
  224. data/features/dsl/table.feature +0 -71
  225. data/features/dsl/tree.feature +0 -37
  226. data/features/index_views/table.feature +0 -20
  227. data/features/locators.feature +0 -40
  228. data/features/menu.feature +0 -35
  229. data/features/settings.feature +0 -36
  230. data/features/step_definitions/configuration_steps.rb +0 -94
  231. data/features/step_definitions/dsl/action_items_steps.rb +0 -13
  232. data/features/step_definitions/dsl/admin_comments_steps.rb +0 -12
  233. data/features/step_definitions/dsl/batch_actions_steps.rb +0 -27
  234. data/features/step_definitions/dsl/in_place_edit_steps.rb +0 -7
  235. data/features/step_definitions/dsl/parent_resource_steps.rb +0 -8
  236. data/features/step_definitions/dsl/resource_action_items_steps.rb +0 -14
  237. data/features/step_definitions/dsl/show_steps.rb +0 -8
  238. data/features/step_definitions/dsl/table_steps.rb +0 -38
  239. data/features/step_definitions/dsl/tree_steps.rb +0 -28
  240. data/features/step_definitions/menu_steps.rb +0 -20
  241. data/features/step_definitions/settings_steps.rb +0 -7
  242. data/features/step_definitions/structure_steps.rb +0 -7
  243. data/features/step_definitions/table_steps.rb +0 -23
  244. data/features/step_definitions/user_steps.rb +0 -72
  245. data/features/step_definitions/web_steps/browsing_steps.rb +0 -108
  246. data/features/step_definitions/web_steps/debug_steps.rb +0 -6
  247. data/features/step_definitions/web_steps/form_steps.rb +0 -190
  248. data/features/step_definitions/web_steps/i18n_steps.rb +0 -8
  249. data/features/step_definitions/web_steps/step_scoper.rb +0 -9
  250. data/features/step_definitions/web_steps/transforms_steps.rb +0 -3
  251. data/features/structure/tree.feature +0 -21
  252. data/features/support/capybara_helpers.rb +0 -15
  253. data/features/support/carrierwave.rb +0 -21
  254. data/features/support/env.rb +0 -40
  255. data/features/support/paths.rb +0 -55
  256. data/features/support/selector_helpers.rb +0 -9
  257. data/features/support/selectors.rb +0 -43
  258. data/features/support/tolerance_for_selenium_sync_issues.rb +0 -27
  259. data/features/user/sign_in.feature +0 -27
  260. data/features/user/sign_out.feature +0 -11
  261. data/features/user/user_edit.feature +0 -19
  262. data/features/user/user_show.feature +0 -9
  263. data/lib/ab_admin/hooks/quiet_scope_page.rb +0 -16
  264. data/lib/ab_admin/hooks/simple_form_hooks.rb +0 -31
  265. data/lib/ab_admin/menu_builder.rb +0 -89
  266. data/lib/ab_admin/views/inputs/date_time_input.rb +0 -65
  267. data/lib/generators/ab_admin/install/templates/models/user_state.rb +0 -6
  268. data/lib/generators/ab_admin/resource/templates/_form.haml.erb +0 -71
  269. data/lib/generators/ab_admin/resource/templates/_search_form.haml.erb +0 -35
  270. data/lib/generators/ab_admin/resource/templates/_table.haml.erb +0 -33
  271. data/spec/ab_admin_spec.rb +0 -23
  272. data/spec/dummy/Rakefile +0 -7
  273. data/spec/dummy/app/assets/images/rails.png +0 -0
  274. data/spec/dummy/app/assets/javascripts/application.js +0 -15
  275. data/spec/dummy/app/assets/stylesheets/application.css +0 -13
  276. data/spec/dummy/app/controllers/application_controller.rb +0 -3
  277. data/spec/dummy/app/controllers/products_controller.rb +0 -5
  278. data/spec/dummy/app/controllers/welcome_controller.rb +0 -5
  279. data/spec/dummy/app/mailers/.gitkeep +0 -0
  280. data/spec/dummy/app/models/ab_admin/ab_admin_catalogue.rb +0 -5
  281. data/spec/dummy/app/models/ab_admin/ab_admin_collection.rb +0 -35
  282. data/spec/dummy/app/models/ab_admin/ab_admin_product.rb +0 -74
  283. data/spec/dummy/app/models/admin_menu.rb +0 -18
  284. data/spec/dummy/app/models/catalogue.rb +0 -10
  285. data/spec/dummy/app/models/ckeditor/asset.rb +0 -7
  286. data/spec/dummy/app/models/ckeditor/attachment_file.rb +0 -7
  287. data/spec/dummy/app/models/ckeditor/picture.rb +0 -7
  288. data/spec/dummy/app/models/collection.rb +0 -20
  289. data/spec/dummy/app/models/product.rb +0 -32
  290. data/spec/dummy/app/uploaders/ckeditor_attachment_file_uploader.rb +0 -42
  291. data/spec/dummy/app/uploaders/ckeditor_picture_uploader.rb +0 -53
  292. data/spec/dummy/app/views/admin/collections/_form.html.slim +0 -24
  293. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  294. data/spec/dummy/app/views/products/show.html.erb +0 -2
  295. data/spec/dummy/app/views/welcome/index.html.erb +0 -2
  296. data/spec/dummy/config.ru +0 -4
  297. data/spec/dummy/config/application.rb +0 -82
  298. data/spec/dummy/config/boot.rb +0 -10
  299. data/spec/dummy/config/database.yml +0 -26
  300. data/spec/dummy/config/environment.rb +0 -5
  301. data/spec/dummy/config/environments/development.rb +0 -37
  302. data/spec/dummy/config/environments/production.rb +0 -67
  303. data/spec/dummy/config/environments/test.rb +0 -37
  304. data/spec/dummy/config/i18n-js.yml +0 -21
  305. data/spec/dummy/config/initializers/ab_admin.rb +0 -15
  306. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  307. data/spec/dummy/config/initializers/ckeditor.rb +0 -18
  308. data/spec/dummy/config/initializers/devise.rb +0 -232
  309. data/spec/dummy/config/initializers/inflections.rb +0 -15
  310. data/spec/dummy/config/initializers/mime_types.rb +0 -5
  311. data/spec/dummy/config/initializers/secret_token.rb +0 -7
  312. data/spec/dummy/config/initializers/session_store.rb +0 -8
  313. data/spec/dummy/config/initializers/simple_form.rb +0 -142
  314. data/spec/dummy/config/initializers/simple_form_bootstrap.rb +0 -45
  315. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  316. data/spec/dummy/config/locales/devise.en.yml +0 -60
  317. data/spec/dummy/config/locales/en.yml +0 -5
  318. data/spec/dummy/config/locales/simple_form.en.yml +0 -26
  319. data/spec/dummy/config/routes.rb +0 -14
  320. data/spec/dummy/config/settings/development.local.yml +0 -9
  321. data/spec/dummy/config/settings/development.yml +0 -7
  322. data/spec/dummy/db/migrate/20130129151853_create_ckeditor_assets.rb +0 -26
  323. data/spec/dummy/db/migrate/20130130161853_create_collections.rb +0 -10
  324. data/spec/dummy/db/migrate/20130130162046_create_products.rb +0 -13
  325. data/spec/dummy/db/migrate/20130130175446_create_globalize_collection_product.rb +0 -11
  326. data/spec/dummy/db/migrate/20130207224516_create_catalogues.rb +0 -16
  327. data/spec/dummy/db/migrate/20130209223506_add_lat_lon_zoom_to_products.rb +0 -7
  328. data/spec/dummy/db/seeds.rb +0 -46
  329. data/spec/dummy/lib/assets/.gitkeep +0 -0
  330. data/spec/dummy/lib/capybara_irb.rb +0 -42
  331. data/spec/dummy/lib/tasks/.gitkeep +0 -0
  332. data/spec/dummy/lib/tasks/cucumber.rake +0 -65
  333. data/spec/dummy/lib/templates/slim/scaffold/_form.html.slim +0 -10
  334. data/spec/dummy/log/.gitkeep +0 -0
  335. data/spec/dummy/public/404.html +0 -26
  336. data/spec/dummy/public/422.html +0 -26
  337. data/spec/dummy/public/500.html +0 -25
  338. data/spec/dummy/public/favicon.ico +0 -0
  339. data/spec/dummy/script/cucumber +0 -10
  340. data/spec/dummy/script/rails +0 -6
  341. data/spec/factories/assets.rb +0 -52
  342. data/spec/factories/catalogues.rb +0 -7
  343. data/spec/factories/collections.rb +0 -8
  344. data/spec/factories/files/rails.png +0 -0
  345. data/spec/factories/products.rb +0 -17
  346. data/spec/factories/sequences.rb +0 -7
  347. data/spec/factories/structures.rb +0 -17
  348. data/spec/factories/users.rb +0 -30
  349. data/spec/generators/ckeditor_assets_generator_spec.rb +0 -19
  350. data/spec/generators/install_generator_spec.rb +0 -48
  351. data/spec/generators/model_generator_spec.rb +0 -31
  352. data/spec/generators/resource_generator_spec.rb +0 -36
  353. data/spec/models/ab_admin/abstract_resource_spec.rb +0 -5
  354. data/spec/models/avatar_spec.rb +0 -162
  355. data/spec/models/picture_uploader_spec.rb +0 -49
  356. data/spec/models/structure_spec.rb +0 -69
  357. data/spec/models/user_spec.rb +0 -159
  358. data/spec/shared_behaviors/concerns.rb +0 -20
  359. data/spec/spec_helper.rb +0 -96
  360. data/spec/support/controller_macros.rb +0 -52
  361. data/spec/support/custom_capybara_matchers.rb +0 -58
  362. data/spec/support/mailer_macros.rb +0 -17
  363. data/spec/support/shared_connection.rb +0 -11
  364. data/vendor/assets/images/ab_admin/clear.png +0 -0
  365. data/vendor/assets/images/ab_admin/loading.gif +0 -0
  366. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/core.js +0 -899
  367. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/index.js +0 -2
  368. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.bg.js +0 -14
  369. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.br.js +0 -13
  370. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.cs.js +0 -14
  371. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.da.js +0 -14
  372. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.de.js +0 -14
  373. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.es.js +0 -14
  374. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.fi.js +0 -14
  375. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.fr.js +0 -14
  376. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.id.js +0 -13
  377. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.is.js +0 -14
  378. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.it.js +0 -14
  379. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.ja.js +0 -13
  380. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.kr.js +0 -13
  381. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.lt.js +0 -15
  382. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.lv.js +0 -16
  383. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.ms.js +0 -14
  384. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.nb.js +0 -14
  385. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.nl.js +0 -14
  386. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.pl.js +0 -14
  387. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.pt-BR.js +0 -14
  388. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.pt.js +0 -14
  389. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.ro.js +0 -15
  390. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.ru.js +0 -14
  391. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.sk.js +0 -14
  392. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.sl.js +0 -14
  393. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.sv.js +0 -14
  394. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.th.js +0 -14
  395. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.tr.js +0 -15
  396. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.js +0 -14
  397. data/vendor/assets/javascripts/ab_admin/bootstrap-datepicker/locales/bootstrap-datepicker.zh-TW.js +0 -13
  398. data/vendor/assets/javascripts/ab_admin/bootstrap-editable-inline.js +0 -2895
  399. data/vendor/assets/javascripts/ab_admin/bootstrap-editable.js +0 -4523
  400. data/vendor/assets/javascripts/ab_admin/bootstrap-tab-multi.js +0 -135
  401. data/vendor/assets/javascripts/ab_admin/bootstrap-timepicker.js +0 -377
  402. data/vendor/assets/javascripts/ab_admin/fileupload/admin-fileuploader.js.coffee +0 -111
  403. data/vendor/assets/javascripts/ab_admin/fileupload/fileuploader.js +0 -1301
  404. data/vendor/assets/javascripts/ab_admin/jquery.Jcrop.js +0 -1083
  405. data/vendor/assets/javascripts/ab_admin/jquery_nested_form.js.coffee +0 -66
  406. data/vendor/assets/javascripts/bootstrap-wysihtml5/locales/ru.js +0 -48
  407. data/vendor/assets/javascripts/handlebars.min.js +0 -1
  408. data/vendor/assets/javascripts/jquery.cookie.js +0 -42
  409. data/vendor/assets/javascripts/jquery.hotkeys.js +0 -106
  410. data/vendor/assets/javascripts/jquery.pjax.js +0 -688
  411. data/vendor/assets/javascripts/jquery.tmpl.min.js +0 -10
  412. data/vendor/assets/javascripts/jquery.ui.nestedSortable.js +0 -356
  413. data/vendor/assets/javascripts/jquery/jquery-ui.min.js +0 -15
  414. data/vendor/assets/javascripts/jquery/jquery.min.js +0 -4
  415. data/vendor/assets/javascripts/underscore.min.js +0 -5
  416. data/vendor/assets/stylesheets/ab_admin/bootstrap-datepicker.css.scss +0 -274
  417. data/vendor/assets/stylesheets/ab_admin/bootstrap-editable.scss +0 -461
  418. data/vendor/assets/stylesheets/ab_admin/bootstrap-timepicker.css.scss +0 -88
  419. data/vendor/assets/stylesheets/ab_admin/jquery.Jcrop.min.css.scss +0 -28
@@ -1,4523 +0,0 @@
1
- // !!! removed datepicker
2
- /*! X-editable - v1.4.1
3
- * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
4
- * http://github.com/vitalets/x-editable
5
- * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
6
-
7
- /**
8
- Form with single input element, two buttons and two states: normal/loading.
9
- Applied as jQuery method to DIV tag (not to form tag!). This is because form can be in loading state when spinner shown.
10
- Editableform is linked with one of input types, e.g. 'text', 'select' etc.
11
-
12
- @class editableform
13
- @uses text
14
- @uses textarea
15
- **/
16
- (function ($) {
17
-
18
- var EditableForm = function (div, options) {
19
- this.options = $.extend({}, $.fn.editableform.defaults, options);
20
- this.$div = $(div); //div, containing form. Not form tag. Not editable-element.
21
- if(!this.options.scope) {
22
- this.options.scope = this;
23
- }
24
- //nothing shown after init
25
- };
26
-
27
- EditableForm.prototype = {
28
- constructor: EditableForm,
29
- initInput: function() { //called once
30
- //take input from options (as it is created in editable-element)
31
- this.input = this.options.input;
32
-
33
- //set initial value
34
- //todo: may be add check: typeof str === 'string' ?
35
- this.value = this.input.str2value(this.options.value);
36
- },
37
- initTemplate: function() {
38
- this.$form = $($.fn.editableform.template);
39
- },
40
- initButtons: function() {
41
- this.$form.find('.editable-buttons').append($.fn.editableform.buttons);
42
- },
43
- /**
44
- Renders editableform
45
-
46
- @method render
47
- **/
48
- render: function() {
49
- //init loader
50
- this.$loading = $($.fn.editableform.loading);
51
- this.$div.empty().append(this.$loading);
52
-
53
- //init form template and buttons
54
- this.initTemplate();
55
- if(this.options.showbuttons) {
56
- this.initButtons();
57
- } else {
58
- this.$form.find('.editable-buttons').remove();
59
- }
60
-
61
- //show loading state
62
- this.showLoading();
63
-
64
- /**
65
- Fired when rendering starts
66
- @event rendering
67
- @param {Object} event event object
68
- **/
69
- this.$div.triggerHandler('rendering');
70
-
71
- //init input
72
- this.initInput();
73
-
74
- //append input to form
75
- this.input.prerender();
76
- this.$form.find('div.editable-input').append(this.input.$tpl);
77
-
78
- //append form to container
79
- this.$div.append(this.$form);
80
-
81
- //render input
82
- $.when(this.input.render())
83
- .then($.proxy(function () {
84
- //setup input to submit automatically when no buttons shown
85
- if(!this.options.showbuttons) {
86
- this.input.autosubmit();
87
- }
88
-
89
- //attach 'cancel' handler
90
- this.$form.find('.editable-cancel').click($.proxy(this.cancel, this));
91
-
92
- if(this.input.error) {
93
- this.error(this.input.error);
94
- this.$form.find('.editable-submit').attr('disabled', true);
95
- this.input.$input.attr('disabled', true);
96
- //prevent form from submitting
97
- this.$form.submit(function(e){ e.preventDefault(); });
98
- } else {
99
- this.error(false);
100
- this.input.$input.removeAttr('disabled');
101
- this.$form.find('.editable-submit').removeAttr('disabled');
102
- this.input.value2input(this.value);
103
- //attach submit handler
104
- this.$form.submit($.proxy(this.submit, this));
105
- }
106
-
107
- /**
108
- Fired when form is rendered
109
- @event rendered
110
- @param {Object} event event object
111
- **/
112
- this.$div.triggerHandler('rendered');
113
-
114
- this.showForm();
115
-
116
- //call postrender method to perform actions required visibility of form
117
- if(this.input.postrender) {
118
- this.input.postrender();
119
- }
120
- }, this));
121
- },
122
- cancel: function() {
123
- /**
124
- Fired when form was cancelled by user
125
- @event cancel
126
- @param {Object} event event object
127
- **/
128
- this.$div.triggerHandler('cancel');
129
- },
130
- showLoading: function() {
131
- var w, h;
132
- if(this.$form) {
133
- //set loading size equal to form
134
- w = this.$form.outerWidth();
135
- h = this.$form.outerHeight();
136
- if(w) {
137
- this.$loading.width(w);
138
- }
139
- if(h) {
140
- this.$loading.height(h);
141
- }
142
- this.$form.hide();
143
- } else {
144
- //stretch loading to fill container width
145
- w = this.$loading.parent().width();
146
- if(w) {
147
- this.$loading.width(w);
148
- }
149
- }
150
- this.$loading.show();
151
- },
152
-
153
- showForm: function(activate) {
154
- this.$loading.hide();
155
- this.$form.show();
156
- if(activate !== false) {
157
- this.input.activate();
158
- }
159
- /**
160
- Fired when form is shown
161
- @event show
162
- @param {Object} event event object
163
- **/
164
- this.$div.triggerHandler('show');
165
- },
166
-
167
- error: function(msg) {
168
- var $group = this.$form.find('.control-group'),
169
- $block = this.$form.find('.editable-error-block'),
170
- lines;
171
-
172
- if(msg === false) {
173
- $group.removeClass($.fn.editableform.errorGroupClass);
174
- $block.removeClass($.fn.editableform.errorBlockClass).empty().hide();
175
- } else {
176
- //convert newline to <br> for more pretty error display
177
- if(msg) {
178
- lines = msg.split("\n");
179
- for (var i = 0; i < lines.length; i++) {
180
- lines[i] = $('<div>').text(lines[i]).html();
181
- }
182
- msg = lines.join('<br>');
183
- }
184
- $group.addClass($.fn.editableform.errorGroupClass);
185
- $block.addClass($.fn.editableform.errorBlockClass).html(msg).show();
186
- }
187
- },
188
-
189
- submit: function(e) {
190
- e.stopPropagation();
191
- e.preventDefault();
192
-
193
- var error,
194
- newValue = this.input.input2value(); //get new value from input
195
-
196
- //validation
197
- if (error = this.validate(newValue)) {
198
- this.error(error);
199
- this.showForm();
200
- return;
201
- }
202
-
203
- //if value not changed --> trigger 'nochange' event and return
204
- /*jslint eqeq: true*/
205
- if (!this.options.savenochange && this.input.value2str(newValue) == this.input.value2str(this.value)) {
206
- /*jslint eqeq: false*/
207
- /**
208
- Fired when value not changed but form is submitted. Requires savenochange = false.
209
- @event nochange
210
- @param {Object} event event object
211
- **/
212
- this.$div.triggerHandler('nochange');
213
- return;
214
- }
215
-
216
- //sending data to server
217
- $.when(this.save(newValue))
218
- .done($.proxy(function(response) {
219
- //run success callback
220
- var res = typeof this.options.success === 'function' ? this.options.success.call(this.options.scope, response, newValue) : null;
221
-
222
- //if success callback returns false --> keep form open and do not activate input
223
- if(res === false) {
224
- this.error(false);
225
- this.showForm(false);
226
- return;
227
- }
228
-
229
- //if success callback returns string --> keep form open, show error and activate input
230
- if(typeof res === 'string') {
231
- this.error(res);
232
- this.showForm();
233
- return;
234
- }
235
-
236
- //if success callback returns object like {newValue: <something>} --> use that value instead of submitted
237
- //it is usefull if you want to chnage value in url-function
238
- if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) {
239
- newValue = res.newValue;
240
- }
241
-
242
- //clear error message
243
- this.error(false);
244
- this.value = newValue;
245
- /**
246
- Fired when form is submitted
247
- @event save
248
- @param {Object} event event object
249
- @param {Object} params additional params
250
- @param {mixed} params.newValue submitted value
251
- @param {Object} params.response ajax response
252
-
253
- @example
254
- $('#form-div').on('save'), function(e, params){
255
- if(params.newValue === 'username') {...}
256
- });
257
- **/
258
- this.$div.triggerHandler('save', {newValue: newValue, response: response});
259
- }, this))
260
- .fail($.proxy(function(xhr) {
261
- this.error(typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!');
262
- this.showForm();
263
- }, this));
264
- },
265
-
266
- save: function(newValue) {
267
- //convert value for submitting to server
268
- var submitValue = this.input.value2submit(newValue);
269
-
270
- //try parse composite pk defined as json string in data-pk
271
- this.options.pk = $.fn.editableutils.tryParseJson(this.options.pk, true);
272
-
273
- var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this.options.scope) : this.options.pk,
274
- send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk)))),
275
- params;
276
-
277
- if (send) { //send to server
278
- this.showLoading();
279
-
280
- //standard params
281
- params = {
282
- name: this.options.name || '',
283
- value: submitValue,
284
- pk: pk
285
- };
286
-
287
- //additional params
288
- if(typeof this.options.params === 'function') {
289
- params = this.options.params.call(this.options.scope, params);
290
- } else {
291
- //try parse json in single quotes (from data-params attribute)
292
- this.options.params = $.fn.editableutils.tryParseJson(this.options.params, true);
293
- $.extend(params, this.options.params);
294
- }
295
-
296
- if(typeof this.options.url === 'function') { //user's function
297
- return this.options.url.call(this.options.scope, params);
298
- } else {
299
- //send ajax to server and return deferred object
300
- return $.ajax($.extend({
301
- url : this.options.url,
302
- data : params,
303
- type : 'POST'
304
- }, this.options.ajaxOptions));
305
- }
306
- }
307
- },
308
-
309
- validate: function (value) {
310
- if (value === undefined) {
311
- value = this.value;
312
- }
313
- if (typeof this.options.validate === 'function') {
314
- return this.options.validate.call(this.options.scope, value);
315
- }
316
- },
317
-
318
- option: function(key, value) {
319
- if(key in this.options) {
320
- this.options[key] = value;
321
- }
322
-
323
- if(key === 'value') {
324
- this.setValue(value);
325
- }
326
-
327
- //do not pass option to input as it is passed in editable-element
328
- },
329
-
330
- setValue: function(value, convertStr) {
331
- if(convertStr) {
332
- this.value = this.input.str2value(value);
333
- } else {
334
- this.value = value;
335
- }
336
-
337
- //if form is visible, update input
338
- if(this.$form && this.$form.is(':visible')) {
339
- this.input.value2input(this.value);
340
- }
341
- }
342
- };
343
-
344
- /*
345
- Initialize editableform. Applied to jQuery object.
346
-
347
- @method $().editableform(options)
348
- @params {Object} options
349
- @example
350
- var $form = $('&lt;div&gt;').editableform({
351
- type: 'text',
352
- name: 'username',
353
- url: '/post',
354
- value: 'vitaliy'
355
- });
356
-
357
- //to display form you should call 'render' method
358
- $form.editableform('render');
359
- */
360
- $.fn.editableform = function (option) {
361
- var args = arguments;
362
- return this.each(function () {
363
- var $this = $(this),
364
- data = $this.data('editableform'),
365
- options = typeof option === 'object' && option;
366
- if (!data) {
367
- $this.data('editableform', (data = new EditableForm(this, options)));
368
- }
369
-
370
- if (typeof option === 'string') { //call method
371
- data[option].apply(data, Array.prototype.slice.call(args, 1));
372
- }
373
- });
374
- };
375
-
376
- //keep link to constructor to allow inheritance
377
- $.fn.editableform.Constructor = EditableForm;
378
-
379
- //defaults
380
- $.fn.editableform.defaults = {
381
- /* see also defaults for input */
382
-
383
- /**
384
- Type of input. Can be <code>text|textarea|select|date|checklist</code>
385
-
386
- @property type
387
- @type string
388
- @default 'text'
389
- **/
390
- type: 'text',
391
- /**
392
- Url for submit, e.g. <code>'/post'</code>
393
- If function - it will be called instead of ajax. Function should return deferred object to run fail/done callbacks.
394
-
395
- @property url
396
- @type string|function
397
- @default null
398
- @example
399
- url: function(params) {
400
- var d = new $.Deferred;
401
- if(params.value === 'abc') {
402
- return d.reject('error message'); //returning error via deferred object
403
- } else {
404
- //async saving data in js model
405
- someModel.asyncSaveMethod({
406
- ...,
407
- success: function(){
408
- d.resolve();
409
- }
410
- });
411
- return d.promise();
412
- }
413
- }
414
- **/
415
- url:null,
416
- /**
417
- Additional params for submit. If defined as <code>object</code> - it is **appended** to original ajax data (pk, name and value).
418
- If defined as <code>function</code> - returned object **overwrites** original ajax data.
419
- @example
420
- params: function(params) {
421
- //originally params contain pk, name and value
422
- params.a = 1;
423
- return params;
424
- }
425
-
426
- @property params
427
- @type object|function
428
- @default null
429
- **/
430
- params:null,
431
- /**
432
- Name of field. Will be submitted on server. Can be taken from <code>id</code> attribute
433
-
434
- @property name
435
- @type string
436
- @default null
437
- **/
438
- name: null,
439
- /**
440
- Primary key of editable object (e.g. record id in database). For composite keys use object, e.g. <code>{id: 1, lang: 'en'}</code>.
441
- Can be calculated dynamically via function.
442
-
443
- @property pk
444
- @type string|object|function
445
- @default null
446
- **/
447
- pk: null,
448
- /**
449
- Initial value. If not defined - will be taken from element's content.
450
- For __select__ type should be defined (as it is ID of shown text).
451
-
452
- @property value
453
- @type string|object
454
- @default null
455
- **/
456
- value: null,
457
- /**
458
- Strategy for sending data on server. Can be <code>auto|always|never</code>.
459
- When 'auto' data will be sent on server only if pk defined, otherwise new value will be stored in element.
460
-
461
- @property send
462
- @type string
463
- @default 'auto'
464
- **/
465
- send: 'auto',
466
- /**
467
- Function for client-side validation. If returns string - means validation not passed and string showed as error.
468
-
469
- @property validate
470
- @type function
471
- @default null
472
- @example
473
- validate: function(value) {
474
- if($.trim(value) == '') {
475
- return 'This field is required';
476
- }
477
- }
478
- **/
479
- validate: null,
480
- /**
481
- Success callback. Called when value successfully sent on server and **response status = 200**.
482
- Useful to work with json response. For example, if your backend response can be <code>{success: true}</code>
483
- or <code>{success: false, msg: "server error"}</code> you can check it inside this callback.
484
- If it returns **string** - means error occured and string is shown as error message.
485
- If it returns **object like** <code>{newValue: &lt;something&gt;}</code> - it overwrites value, submitted by user.
486
- Otherwise newValue simply rendered into element.
487
-
488
- @property success
489
- @type function
490
- @default null
491
- @example
492
- success: function(response, newValue) {
493
- if(!response.success) return response.msg;
494
- }
495
- **/
496
- success: null,
497
- /**
498
- Additional options for ajax request.
499
- List of values: http://api.jquery.com/jQuery.ajax
500
-
501
- @property ajaxOptions
502
- @type object
503
- @default null
504
- @since 1.1.1
505
- @example
506
- ajaxOptions: {
507
- type: 'put',
508
- dataType: 'json'
509
- }
510
- **/
511
- ajaxOptions: null,
512
- /**
513
- Whether to show buttons or not.
514
- Form without buttons is auto-submitted.
515
-
516
- @property showbuttons
517
- @type boolean
518
- @default true
519
- @since 1.1.1
520
- **/
521
- showbuttons: true,
522
- /**
523
- Scope for callback methods (success, validate).
524
- If <code>null</code> means editableform instance itself.
525
-
526
- @property scope
527
- @type DOMElement|object
528
- @default null
529
- @since 1.2.0
530
- @private
531
- **/
532
- scope: null,
533
- /**
534
- Whether to save or cancel value when it was not changed but form was submitted
535
-
536
- @property savenochange
537
- @type boolean
538
- @default false
539
- @since 1.2.0
540
- **/
541
- savenochange: false
542
- };
543
-
544
- /*
545
- Note: following params could redefined in engine: bootstrap or jqueryui:
546
- Classes 'control-group' and 'editable-error-block' must always present!
547
- */
548
- $.fn.editableform.template = '<form class="form-inline editableform">'+
549
- '<div class="control-group">' +
550
- '<div><div class="editable-input"></div><div class="editable-buttons"></div></div>'+
551
- '<div class="editable-error-block"></div>' +
552
- '</div>' +
553
- '</form>';
554
-
555
- //loading div
556
- $.fn.editableform.loading = '<div class="editableform-loading"></div>';
557
-
558
- //buttons
559
- $.fn.editableform.buttons = '<button type="submit" class="editable-submit">ok</button>'+
560
- '<button type="button" class="editable-cancel">cancel</button>';
561
-
562
- //error class attached to control-group
563
- $.fn.editableform.errorGroupClass = null;
564
-
565
- //error class attached to editable-error-block
566
- $.fn.editableform.errorBlockClass = 'editable-error';
567
- }(window.jQuery));
568
- /**
569
- * EditableForm utilites
570
- */
571
- (function ($) {
572
- //utils
573
- $.fn.editableutils = {
574
- /**
575
- * classic JS inheritance function
576
- */
577
- inherit: function (Child, Parent) {
578
- var F = function() { };
579
- F.prototype = Parent.prototype;
580
- Child.prototype = new F();
581
- Child.prototype.constructor = Child;
582
- Child.superclass = Parent.prototype;
583
- },
584
-
585
- /**
586
- * set caret position in input
587
- * see http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
588
- */
589
- setCursorPosition: function(elem, pos) {
590
- if (elem.setSelectionRange) {
591
- elem.setSelectionRange(pos, pos);
592
- } else if (elem.createTextRange) {
593
- var range = elem.createTextRange();
594
- range.collapse(true);
595
- range.moveEnd('character', pos);
596
- range.moveStart('character', pos);
597
- range.select();
598
- }
599
- },
600
-
601
- /**
602
- * function to parse JSON in *single* quotes. (jquery automatically parse only double quotes)
603
- * That allows such code as: <a data-source="{'a': 'b', 'c': 'd'}">
604
- * safe = true --> means no exception will be thrown
605
- * for details see http://stackoverflow.com/questions/7410348/how-to-set-json-format-to-html5-data-attributes-in-the-jquery
606
- */
607
- tryParseJson: function(s, safe) {
608
- if (typeof s === 'string' && s.length && s.match(/^[\{\[].*[\}\]]$/)) {
609
- if (safe) {
610
- try {
611
- /*jslint evil: true*/
612
- s = (new Function('return ' + s))();
613
- /*jslint evil: false*/
614
- } catch (e) {} finally {
615
- return s;
616
- }
617
- } else {
618
- /*jslint evil: true*/
619
- s = (new Function('return ' + s))();
620
- /*jslint evil: false*/
621
- }
622
- }
623
- return s;
624
- },
625
-
626
- /**
627
- * slice object by specified keys
628
- */
629
- sliceObj: function(obj, keys, caseSensitive /* default: false */) {
630
- var key, keyLower, newObj = {};
631
-
632
- if (!$.isArray(keys) || !keys.length) {
633
- return newObj;
634
- }
635
-
636
- for (var i = 0; i < keys.length; i++) {
637
- key = keys[i];
638
- if (obj.hasOwnProperty(key)) {
639
- newObj[key] = obj[key];
640
- }
641
-
642
- if(caseSensitive === true) {
643
- continue;
644
- }
645
-
646
- //when getting data-* attributes via $.data() it's converted to lowercase.
647
- //details: http://stackoverflow.com/questions/7602565/using-data-attributes-with-jquery
648
- //workaround is code below.
649
- keyLower = key.toLowerCase();
650
- if (obj.hasOwnProperty(keyLower)) {
651
- newObj[key] = obj[keyLower];
652
- }
653
- }
654
-
655
- return newObj;
656
- },
657
-
658
- /*
659
- exclude complex objects from $.data() before pass to config
660
- */
661
- getConfigData: function($element) {
662
- var data = {};
663
- $.each($element.data(), function(k, v) {
664
- if(typeof v !== 'object' || (v && typeof v === 'object' && (v.constructor === Object || v.constructor === Array))) {
665
- data[k] = v;
666
- }
667
- });
668
- return data;
669
- },
670
-
671
- /*
672
- returns keys of object
673
- */
674
- objectKeys: function(o) {
675
- if (Object.keys) {
676
- return Object.keys(o);
677
- } else {
678
- if (o !== Object(o)) {
679
- throw new TypeError('Object.keys called on a non-object');
680
- }
681
- var k=[], p;
682
- for (p in o) {
683
- if (Object.prototype.hasOwnProperty.call(o,p)) {
684
- k.push(p);
685
- }
686
- }
687
- return k;
688
- }
689
-
690
- },
691
-
692
- /**
693
- method to escape html.
694
- **/
695
- escape: function(str) {
696
- return $('<div>').text(str).html();
697
- },
698
-
699
- /*
700
- returns array items from sourceData having value property equal or inArray of 'value'
701
- */
702
- itemsByValue: function(value, sourceData, valueProp) {
703
- if(!sourceData || value === null) {
704
- return [];
705
- }
706
-
707
- valueProp = valueProp || 'value';
708
-
709
- var isValArray = $.isArray(value),
710
- result = [],
711
- that = this;
712
-
713
- $.each(sourceData, function(i, o) {
714
- if(o.children) {
715
- result = result.concat(that.itemsByValue(value, o.children));
716
- } else {
717
- /*jslint eqeq: true*/
718
- if(isValArray) {
719
- if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? o[valueProp] : o); }).length) {
720
- result.push(o);
721
- }
722
- } else {
723
- if(value == (o && typeof o === 'object' ? o[valueProp] : o)) {
724
- result.push(o);
725
- }
726
- }
727
- /*jslint eqeq: false*/
728
- }
729
- });
730
-
731
- return result;
732
- },
733
-
734
- /*
735
- Returns input by options: type, mode.
736
- */
737
- createInput: function(options) {
738
- var TypeConstructor, typeOptions, input,
739
- type = options.type;
740
-
741
- //`date` is some kind of virtual type that is transformed to one of exact types
742
- //depending on mode and core lib
743
- if(type === 'date') {
744
- //inline
745
- if(options.mode === 'inline') {
746
- if($.fn.editabletypes.datefield) {
747
- type = 'datefield';
748
- } else if($.fn.editabletypes.dateuifield) {
749
- type = 'dateuifield';
750
- }
751
- //popup
752
- } else {
753
- if($.fn.editabletypes.date) {
754
- type = 'date';
755
- } else if($.fn.editabletypes.dateui) {
756
- type = 'dateui';
757
- }
758
- }
759
-
760
- //if type still `date` and not exist in types, replace with `combodate` that is base input
761
- if(type === 'date' && !$.fn.editabletypes.date) {
762
- type = 'combodate';
763
- }
764
- }
765
-
766
- //change wysihtml5 to textarea for jquery UI and plain versions
767
- if(type === 'wysihtml5' && !$.fn.editabletypes[type]) {
768
- type = 'textarea';
769
- }
770
-
771
- //create input of specified type. Input will be used for converting value, not in form
772
- if(typeof $.fn.editabletypes[type] === 'function') {
773
- TypeConstructor = $.fn.editabletypes[type];
774
- typeOptions = this.sliceObj(options, this.objectKeys(TypeConstructor.defaults));
775
- input = new TypeConstructor(typeOptions);
776
- return input;
777
- } else {
778
- $.error('Unknown type: '+ type);
779
- return false;
780
- }
781
- }
782
-
783
- };
784
- }(window.jQuery));
785
-
786
- /**
787
- Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.<br>
788
- This method applied internally in <code>$().editable()</code>. You should subscribe on it's events (save / cancel) to get profit of it.<br>
789
- Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.<br>
790
- Applied as jQuery method.
791
-
792
- @class editableContainer
793
- @uses editableform
794
- **/
795
- (function ($) {
796
-
797
- var Popup = function (element, options) {
798
- this.init(element, options);
799
- };
800
-
801
- var Inline = function (element, options) {
802
- this.init(element, options);
803
- };
804
-
805
- //methods
806
- Popup.prototype = {
807
- containerName: null, //tbd in child class
808
- innerCss: null, //tbd in child class
809
- init: function(element, options) {
810
- this.$element = $(element);
811
- //since 1.4.1 container do not use data-* directly as they already merged into options.
812
- this.options = $.extend({}, $.fn.editableContainer.defaults, options);
813
- this.splitOptions();
814
-
815
- //set scope of form callbacks to element
816
- this.formOptions.scope = this.$element[0];
817
-
818
- this.initContainer();
819
-
820
- //bind 'destroyed' listener to destroy container when element is removed from dom
821
- this.$element.on('destroyed', $.proxy(function(){
822
- this.destroy();
823
- }, this));
824
-
825
- //attach document handler to close containers on click / escape
826
- if(!$(document).data('editable-handlers-attached')) {
827
- //close all on escape
828
- $(document).on('keyup.editable', function (e) {
829
- if (e.which === 27) {
830
- $('.editable-open').editableContainer('hide');
831
- //todo: return focus on element
832
- }
833
- });
834
-
835
- //close containers when click outside
836
- $(document).on('click.editable', function(e) {
837
- var $target = $(e.target), i,
838
- exclude_classes = ['.editable-container',
839
- '.ui-datepicker-header',
840
- '.modal-backdrop',
841
- '.bootstrap-wysihtml5-insert-image-modal',
842
- '.bootstrap-wysihtml5-insert-link-modal'];
843
-
844
- //if click inside one of exclude classes --> no nothing
845
- for(i=0; i<exclude_classes.length; i++) {
846
- if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
847
- return;
848
- }
849
- }
850
-
851
- //close all open containers (except one - target)
852
- Popup.prototype.closeOthers(e.target);
853
- });
854
-
855
- $(document).data('editable-handlers-attached', true);
856
- }
857
- },
858
-
859
- //split options on containerOptions and formOptions
860
- splitOptions: function() {
861
- this.containerOptions = {};
862
- this.formOptions = {};
863
- var cDef = $.fn[this.containerName].defaults;
864
- //keys defined in container defaults go to container, others go to form
865
- for(var k in this.options) {
866
- if(k in cDef) {
867
- this.containerOptions[k] = this.options[k];
868
- } else {
869
- this.formOptions[k] = this.options[k];
870
- }
871
- }
872
- },
873
-
874
- /*
875
- Returns jquery object of container
876
- @method tip()
877
- */
878
- tip: function() {
879
- return this.container() ? this.container().$tip : null;
880
- },
881
-
882
- /* returns container object */
883
- container: function() {
884
- return this.$element.data(this.containerName);
885
- },
886
-
887
- call: function() {
888
- this.$element[this.containerName].apply(this.$element, arguments);
889
- },
890
-
891
- initContainer: function(){
892
- this.call(this.containerOptions);
893
- },
894
-
895
- renderForm: function() {
896
- this.$form
897
- .editableform(this.formOptions)
898
- .on({
899
- save: $.proxy(this.save, this), //click on submit button (value changed)
900
- nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
901
- cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
902
- show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
903
- rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
904
- resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
905
- rendered: $.proxy(function(){
906
- /**
907
- Fired when container is shown and form is rendered (for select will wait for loading dropdown options)
908
-
909
- @event shown
910
- @param {Object} event event object
911
- @example
912
- $('#username').on('shown', function() {
913
- var $tip = $(this).data('editableContainer').tip();
914
- $tip.find('input').val('overwriting value of input..');
915
- });
916
- **/
917
- this.$element.triggerHandler('shown');
918
- }, this)
919
- })
920
- .editableform('render');
921
- },
922
-
923
- /**
924
- Shows container with form
925
- @method show()
926
- @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
927
- **/
928
- /* Note: poshytip owerwrites this method totally! */
929
- show: function (closeAll) {
930
- this.$element.addClass('editable-open');
931
- if(closeAll !== false) {
932
- //close all open containers (except this)
933
- this.closeOthers(this.$element[0]);
934
- }
935
-
936
- //show container itself
937
- this.innerShow();
938
- this.tip().addClass('editable-container');
939
-
940
- /*
941
- Currently, form is re-rendered on every show.
942
- The main reason is that we dont know, what container will do with content when closed:
943
- remove(), detach() or just hide().
944
-
945
- Detaching form itself before hide and re-insert before show is good solution,
946
- but visually it looks ugly, as container changes size before hide.
947
- */
948
-
949
- //if form already exist - delete previous data
950
- if(this.$form) {
951
- //todo: destroy prev data!
952
- //this.$form.destroy();
953
- }
954
-
955
- this.$form = $('<div>');
956
-
957
- //insert form into container body
958
- if(this.tip().is(this.innerCss)) {
959
- //for inline container
960
- this.tip().append(this.$form);
961
- } else {
962
- this.tip().find(this.innerCss).append(this.$form);
963
- }
964
-
965
- //render form
966
- this.renderForm();
967
- },
968
-
969
- /**
970
- Hides container with form
971
- @method hide()
972
- @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|undefined (=manual)</code>
973
- **/
974
- hide: function(reason) {
975
- if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
976
- return;
977
- }
978
-
979
- this.$element.removeClass('editable-open');
980
- this.innerHide();
981
-
982
- /**
983
- Fired when container was hidden. It occurs on both save or cancel.
984
-
985
- @event hidden
986
- @param {object} event event object
987
- @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|undefined (=manual)</code>
988
- @example
989
- $('#username').on('hidden', function(e, reason) {
990
- if(reason === 'save' || reason === 'cancel') {
991
- //auto-open next editable
992
- $(this).closest('tr').next().find('.editable').editable('show');
993
- }
994
- });
995
- **/
996
- this.$element.triggerHandler('hidden', reason);
997
- },
998
-
999
- /* internal show method. To be overwritten in child classes */
1000
- innerShow: function () {
1001
-
1002
- },
1003
-
1004
- /* internal hide method. To be overwritten in child classes */
1005
- innerHide: function () {
1006
-
1007
- },
1008
-
1009
- /**
1010
- Toggles container visibility (show / hide)
1011
- @method toggle()
1012
- @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
1013
- **/
1014
- toggle: function(closeAll) {
1015
- if(this.container() && this.tip() && this.tip().is(':visible')) {
1016
- this.hide();
1017
- } else {
1018
- this.show(closeAll);
1019
- }
1020
- },
1021
-
1022
- /*
1023
- Updates the position of container when content changed.
1024
- @method setPosition()
1025
- */
1026
- setPosition: function() {
1027
- //tbd in child class
1028
- },
1029
-
1030
- save: function(e, params) {
1031
- /**
1032
- Fired when new value was submitted. You can use <code>$(this).data('editableContainer')</code> inside handler to access to editableContainer instance
1033
-
1034
- @event save
1035
- @param {Object} event event object
1036
- @param {Object} params additional params
1037
- @param {mixed} params.newValue submitted value
1038
- @param {Object} params.response ajax response
1039
- @example
1040
- $('#username').on('save', function(e, params) {
1041
- //assuming server response: '{success: true}'
1042
- var pk = $(this).data('editableContainer').options.pk;
1043
- if(params.response && params.response.success) {
1044
- alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
1045
- } else {
1046
- alert('error!');
1047
- }
1048
- });
1049
- **/
1050
- this.$element.triggerHandler('save', params);
1051
-
1052
- //hide must be after trigger, as saving value may require methods od plugin, applied to input
1053
- this.hide('save');
1054
- },
1055
-
1056
- /**
1057
- Sets new option
1058
-
1059
- @method option(key, value)
1060
- @param {string} key
1061
- @param {mixed} value
1062
- **/
1063
- option: function(key, value) {
1064
- this.options[key] = value;
1065
- if(key in this.containerOptions) {
1066
- this.containerOptions[key] = value;
1067
- this.setContainerOption(key, value);
1068
- } else {
1069
- this.formOptions[key] = value;
1070
- if(this.$form) {
1071
- this.$form.editableform('option', key, value);
1072
- }
1073
- }
1074
- },
1075
-
1076
- setContainerOption: function(key, value) {
1077
- this.call('option', key, value);
1078
- },
1079
-
1080
- /**
1081
- Destroys the container instance
1082
- @method destroy()
1083
- **/
1084
- destroy: function() {
1085
- this.hide();
1086
- this.innerDestroy();
1087
- this.$element.off('destroyed');
1088
- this.$element.removeData('editableContainer');
1089
- },
1090
-
1091
- /* to be overwritten in child classes */
1092
- innerDestroy: function() {
1093
-
1094
- },
1095
-
1096
- /*
1097
- Closes other containers except one related to passed element.
1098
- Other containers can be cancelled or submitted (depends on onblur option)
1099
- */
1100
- closeOthers: function(element) {
1101
- $('.editable-open').each(function(i, el){
1102
- //do nothing with passed element and it's children
1103
- if(el === element || $(el).find(element).length) {
1104
- return;
1105
- }
1106
-
1107
- //otherwise cancel or submit all open containers
1108
- var $el = $(el),
1109
- ec = $el.data('editableContainer');
1110
-
1111
- if(!ec) {
1112
- return;
1113
- }
1114
-
1115
- if(ec.options.onblur === 'cancel') {
1116
- $el.data('editableContainer').hide('onblur');
1117
- } else if(ec.options.onblur === 'submit') {
1118
- $el.data('editableContainer').tip().find('form').submit();
1119
- }
1120
- });
1121
-
1122
- },
1123
-
1124
- /**
1125
- Activates input of visible container (e.g. set focus)
1126
- @method activate()
1127
- **/
1128
- activate: function() {
1129
- if(this.tip && this.tip().is(':visible') && this.$form) {
1130
- this.$form.data('editableform').input.activate();
1131
- }
1132
- }
1133
-
1134
- };
1135
-
1136
- /**
1137
- jQuery method to initialize editableContainer.
1138
-
1139
- @method $().editableContainer(options)
1140
- @params {Object} options
1141
- @example
1142
- $('#edit').editableContainer({
1143
- type: 'text',
1144
- url: '/post',
1145
- pk: 1,
1146
- value: 'hello'
1147
- });
1148
- **/
1149
- $.fn.editableContainer = function (option) {
1150
- var args = arguments;
1151
- return this.each(function () {
1152
- var $this = $(this),
1153
- dataKey = 'editableContainer',
1154
- data = $this.data(dataKey),
1155
- options = typeof option === 'object' && option,
1156
- Constructor = (options.mode === 'inline') ? Inline : Popup;
1157
-
1158
- if (!data) {
1159
- $this.data(dataKey, (data = new Constructor(this, options)));
1160
- }
1161
-
1162
- if (typeof option === 'string') { //call method
1163
- data[option].apply(data, Array.prototype.slice.call(args, 1));
1164
- }
1165
- });
1166
- };
1167
-
1168
- //store constructors
1169
- $.fn.editableContainer.Popup = Popup;
1170
- $.fn.editableContainer.Inline = Inline;
1171
-
1172
- //defaults
1173
- $.fn.editableContainer.defaults = {
1174
- /**
1175
- Initial value of form input
1176
-
1177
- @property value
1178
- @type mixed
1179
- @default null
1180
- @private
1181
- **/
1182
- value: null,
1183
- /**
1184
- Placement of container relative to element. Can be <code>top|right|bottom|left</code>. Not used for inline container.
1185
-
1186
- @property placement
1187
- @type string
1188
- @default 'top'
1189
- **/
1190
- placement: 'top',
1191
- /**
1192
- Whether to hide container on save/cancel.
1193
-
1194
- @property autohide
1195
- @type boolean
1196
- @default true
1197
- @private
1198
- **/
1199
- autohide: true,
1200
- /**
1201
- Action when user clicks outside the container. Can be <code>cancel|submit|ignore</code>.
1202
- Setting <code>ignore</code> allows to have several containers open.
1203
-
1204
- @property onblur
1205
- @type string
1206
- @default 'cancel'
1207
- @since 1.1.1
1208
- **/
1209
- onblur: 'cancel',
1210
-
1211
- /**
1212
- Animation speed (inline mode)
1213
- @property anim
1214
- @type string
1215
- @default 'fast'
1216
- **/
1217
- anim: 'fast',
1218
-
1219
- /**
1220
- Mode of editable, can be `popup` or `inline`
1221
-
1222
- @property mode
1223
- @type string
1224
- @default 'popup'
1225
- @since 1.4.0
1226
- **/
1227
- mode: 'popup'
1228
- };
1229
-
1230
- /*
1231
- * workaround to have 'destroyed' event to destroy popover when element is destroyed
1232
- * see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
1233
- */
1234
- jQuery.event.special.destroyed = {
1235
- remove: function(o) {
1236
- if (o.handler) {
1237
- o.handler();
1238
- }
1239
- }
1240
- };
1241
-
1242
- }(window.jQuery));
1243
-
1244
- /**
1245
- * Editable Inline
1246
- * ---------------------
1247
- */
1248
- (function ($) {
1249
-
1250
- //copy prototype from EditableContainer
1251
- //extend methods
1252
- $.extend($.fn.editableContainer.Inline.prototype, $.fn.editableContainer.Popup.prototype, {
1253
- containerName: 'editableform',
1254
- innerCss: '.editable-inline',
1255
-
1256
- initContainer: function(){
1257
- //container is <span> element
1258
- this.$tip = $('<span></span>').addClass('editable-inline');
1259
-
1260
- //convert anim to miliseconds (int)
1261
- if(!this.options.anim) {
1262
- this.options.anim = 0;
1263
- }
1264
- },
1265
-
1266
- splitOptions: function() {
1267
- //all options are passed to form
1268
- this.containerOptions = {};
1269
- this.formOptions = this.options;
1270
- },
1271
-
1272
- tip: function() {
1273
- return this.$tip;
1274
- },
1275
-
1276
- innerShow: function () {
1277
- this.$element.hide();
1278
- this.tip().insertAfter(this.$element).show();
1279
- },
1280
-
1281
- innerHide: function () {
1282
- this.$tip.hide(this.options.anim, $.proxy(function() {
1283
- this.$element.show();
1284
- this.innerDestroy();
1285
- }, this));
1286
- },
1287
-
1288
- innerDestroy: function() {
1289
- if(this.tip()) {
1290
- this.tip().empty().remove();
1291
- }
1292
- }
1293
- });
1294
-
1295
- }(window.jQuery));
1296
- /**
1297
- Makes editable any HTML element on the page. Applied as jQuery method.
1298
-
1299
- @class editable
1300
- @uses editableContainer
1301
- **/
1302
- (function ($) {
1303
-
1304
- var Editable = function (element, options) {
1305
- this.$element = $(element);
1306
- //data-* has more priority over js options: because dynamically created elements may change data-*
1307
- this.options = $.extend({}, $.fn.editable.defaults, options, $.fn.editableutils.getConfigData(this.$element));
1308
- if(this.options.selector) {
1309
- this.initLive();
1310
- } else {
1311
- this.init();
1312
- }
1313
- };
1314
-
1315
- Editable.prototype = {
1316
- constructor: Editable,
1317
- init: function () {
1318
- var isValueByText = false,
1319
- doAutotext, finalize;
1320
-
1321
- //name
1322
- this.options.name = this.options.name || this.$element.attr('id');
1323
-
1324
- //create input of specified type. Input will be used for converting value, not in form
1325
- this.input = $.fn.editableutils.createInput(this.options);
1326
- if(!this.input) {
1327
- return;
1328
- }
1329
-
1330
- //set value from settings or by element's text
1331
- if (this.options.value === undefined || this.options.value === null) {
1332
- this.value = this.input.html2value($.trim(this.$element.html()));
1333
- isValueByText = true;
1334
- } else {
1335
- /*
1336
- value can be string when received from 'data-value' attribute
1337
- for complext objects value can be set as json string in data-value attribute,
1338
- e.g. data-value="{city: 'Moscow', street: 'Lenina'}"
1339
- */
1340
- this.options.value = $.fn.editableutils.tryParseJson(this.options.value, true);
1341
- if(typeof this.options.value === 'string') {
1342
- this.value = this.input.str2value(this.options.value);
1343
- } else {
1344
- this.value = this.options.value;
1345
- }
1346
- }
1347
-
1348
- //add 'editable' class to every editable element
1349
- this.$element.addClass('editable');
1350
-
1351
- //attach handler activating editable. In disabled mode it just prevent default action (useful for links)
1352
- if(this.options.toggle !== 'manual') {
1353
- this.$element.addClass('editable-click');
1354
- this.$element.on(this.options.toggle + '.editable', $.proxy(function(e){
1355
- //prevent following link
1356
- e.preventDefault();
1357
-
1358
- //stop propagation not required because in document click handler it checks event target
1359
- //e.stopPropagation();
1360
-
1361
- if(this.options.toggle === 'mouseenter') {
1362
- //for hover only show container
1363
- this.show();
1364
- } else {
1365
- //when toggle='click' we should not close all other containers as they will be closed automatically in document click listener
1366
- var closeAll = (this.options.toggle !== 'click');
1367
- this.toggle(closeAll);
1368
- }
1369
- }, this));
1370
- } else {
1371
- this.$element.attr('tabindex', -1); //do not stop focus on element when toggled manually
1372
- }
1373
-
1374
- //check conditions for autotext:
1375
- //if value was generated by text or value is empty, no sense to run autotext
1376
- doAutotext = !isValueByText && this.value !== null && this.value !== undefined;
1377
- doAutotext &= (this.options.autotext === 'always') || (this.options.autotext === 'auto' && !this.$element.text().length);
1378
- $.when(doAutotext ? this.render() : true).then($.proxy(function() {
1379
- if(this.options.disabled) {
1380
- this.disable();
1381
- } else {
1382
- this.enable();
1383
- }
1384
- /**
1385
- Fired when element was initialized by editable method.
1386
-
1387
- @event init
1388
- @param {Object} event event object
1389
- @param {Object} editable editable instance (as here it cannot accessed via data('editable'))
1390
- @since 1.2.0
1391
- @example
1392
- $('#username').on('init', function(e, editable) {
1393
- alert('initialized ' + editable.options.name);
1394
- });
1395
- **/
1396
- this.$element.triggerHandler('init', this);
1397
- }, this));
1398
- },
1399
-
1400
- /*
1401
- Initializes parent element for live editables
1402
- */
1403
- initLive: function() {
1404
- //store selector
1405
- var selector = this.options.selector;
1406
- //modify options for child elements
1407
- this.options.selector = false;
1408
- this.options.autotext = 'never';
1409
- //listen toggle events
1410
- this.$element.on(this.options.toggle + '.editable', selector, $.proxy(function(e){
1411
- var $target = $(e.target);
1412
- if(!$target.data('editable')) {
1413
- $target.editable(this.options).trigger(e);
1414
- }
1415
- }, this));
1416
- },
1417
-
1418
- /*
1419
- Renders value into element's text.
1420
- Can call custom display method from options.
1421
- Can return deferred object.
1422
- @method render()
1423
- @param {mixed} response server response (if exist) to pass into display function
1424
- */
1425
- render: function(response) {
1426
- //do not display anything
1427
- if(this.options.display === false) {
1428
- return;
1429
- }
1430
-
1431
- //if input has `value2htmlFinal` method, we pass callback in third param to be called when source is loaded
1432
- if(this.input.value2htmlFinal) {
1433
- return this.input.value2html(this.value, this.$element[0], this.options.display, response);
1434
- //if display method defined --> use it
1435
- } else if(typeof this.options.display === 'function') {
1436
- return this.options.display.call(this.$element[0], this.value, response);
1437
- //else use input's original value2html() method
1438
- } else {
1439
- return this.input.value2html(this.value, this.$element[0]);
1440
- }
1441
- },
1442
-
1443
- /**
1444
- Enables editable
1445
- @method enable()
1446
- **/
1447
- enable: function() {
1448
- this.options.disabled = false;
1449
- this.$element.removeClass('editable-disabled');
1450
- this.handleEmpty(this.isEmpty);
1451
- if(this.options.toggle !== 'manual') {
1452
- if(this.$element.attr('tabindex') === '-1') {
1453
- this.$element.removeAttr('tabindex');
1454
- }
1455
- }
1456
- },
1457
-
1458
- /**
1459
- Disables editable
1460
- @method disable()
1461
- **/
1462
- disable: function() {
1463
- this.options.disabled = true;
1464
- this.hide();
1465
- this.$element.addClass('editable-disabled');
1466
- this.handleEmpty(this.isEmpty);
1467
- //do not stop focus on this element
1468
- this.$element.attr('tabindex', -1);
1469
- },
1470
-
1471
- /**
1472
- Toggles enabled / disabled state of editable element
1473
- @method toggleDisabled()
1474
- **/
1475
- toggleDisabled: function() {
1476
- if(this.options.disabled) {
1477
- this.enable();
1478
- } else {
1479
- this.disable();
1480
- }
1481
- },
1482
-
1483
- /**
1484
- Sets new option
1485
-
1486
- @method option(key, value)
1487
- @param {string|object} key option name or object with several options
1488
- @param {mixed} value option new value
1489
- @example
1490
- $('.editable').editable('option', 'pk', 2);
1491
- **/
1492
- option: function(key, value) {
1493
- //set option(s) by object
1494
- if(key && typeof key === 'object') {
1495
- $.each(key, $.proxy(function(k, v){
1496
- this.option($.trim(k), v);
1497
- }, this));
1498
- return;
1499
- }
1500
-
1501
- //set option by string
1502
- this.options[key] = value;
1503
-
1504
- //disabled
1505
- if(key === 'disabled') {
1506
- return value ? this.disable() : this.enable();
1507
- }
1508
-
1509
- //value
1510
- if(key === 'value') {
1511
- this.setValue(value);
1512
- }
1513
-
1514
- //transfer new option to container!
1515
- if(this.container) {
1516
- this.container.option(key, value);
1517
- }
1518
-
1519
- //pass option to input directly (as it points to the same in form)
1520
- if(this.input.option) {
1521
- this.input.option(key, value);
1522
- }
1523
-
1524
- },
1525
-
1526
- /*
1527
- * set emptytext if element is empty
1528
- */
1529
- handleEmpty: function (isEmpty) {
1530
- //do not handle empty if we do not display anything
1531
- if(this.options.display === false) {
1532
- return;
1533
- }
1534
-
1535
- this.isEmpty = isEmpty !== undefined ? isEmpty : $.trim(this.$element.text()) === '';
1536
-
1537
- //emptytext shown only for enabled
1538
- if(!this.options.disabled) {
1539
- if (this.isEmpty) {
1540
- this.$element.text(this.options.emptytext);
1541
- if(this.options.emptyclass) {
1542
- this.$element.addClass(this.options.emptyclass);
1543
- }
1544
- } else if(this.options.emptyclass) {
1545
- this.$element.removeClass(this.options.emptyclass);
1546
- }
1547
- } else {
1548
- //below required if element disable property was changed
1549
- if(this.isEmpty) {
1550
- this.$element.empty();
1551
- if(this.options.emptyclass) {
1552
- this.$element.removeClass(this.options.emptyclass);
1553
- }
1554
- }
1555
- }
1556
- },
1557
-
1558
- /**
1559
- Shows container with form
1560
- @method show()
1561
- @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
1562
- **/
1563
- show: function (closeAll) {
1564
- if(this.options.disabled) {
1565
- return;
1566
- }
1567
-
1568
- //init editableContainer: popover, tooltip, inline, etc..
1569
- if(!this.container) {
1570
- var containerOptions = $.extend({}, this.options, {
1571
- value: this.value,
1572
- input: this.input //pass input to form (as it is already created)
1573
- });
1574
- this.$element.editableContainer(containerOptions);
1575
- //listen `save` event
1576
- this.$element.on("save.internal", $.proxy(this.save, this));
1577
- this.container = this.$element.data('editableContainer');
1578
- } else if(this.container.tip().is(':visible')) {
1579
- return;
1580
- }
1581
-
1582
- //show container
1583
- this.container.show(closeAll);
1584
- },
1585
-
1586
- /**
1587
- Hides container with form
1588
- @method hide()
1589
- **/
1590
- hide: function () {
1591
- if(this.container) {
1592
- this.container.hide();
1593
- }
1594
- },
1595
-
1596
- /**
1597
- Toggles container visibility (show / hide)
1598
- @method toggle()
1599
- @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
1600
- **/
1601
- toggle: function(closeAll) {
1602
- if(this.container && this.container.tip().is(':visible')) {
1603
- this.hide();
1604
- } else {
1605
- this.show(closeAll);
1606
- }
1607
- },
1608
-
1609
- /*
1610
- * called when form was submitted
1611
- */
1612
- save: function(e, params) {
1613
- //mark element with unsaved class if needed
1614
- if(this.options.unsavedclass) {
1615
- /*
1616
- Add unsaved css to element if:
1617
- - url is not user's function
1618
- - value was not sent to server
1619
- - params.response === undefined, that means data was not sent
1620
- - value changed
1621
- */
1622
- var sent = false;
1623
- sent = sent || typeof this.options.url === 'function';
1624
- sent = sent || this.options.display === false;
1625
- sent = sent || params.response !== undefined;
1626
- sent = sent || (this.options.savenochange && this.input.value2str(this.value) !== this.input.value2str(params.newValue));
1627
-
1628
- if(sent) {
1629
- this.$element.removeClass(this.options.unsavedclass);
1630
- } else {
1631
- this.$element.addClass(this.options.unsavedclass);
1632
- }
1633
- }
1634
-
1635
- //set new value
1636
- this.setValue(params.newValue, false, params.response);
1637
-
1638
- /**
1639
- Fired when new value was submitted. You can use <code>$(this).data('editable')</code> to access to editable instance
1640
-
1641
- @event save
1642
- @param {Object} event event object
1643
- @param {Object} params additional params
1644
- @param {mixed} params.newValue submitted value
1645
- @param {Object} params.response ajax response
1646
- @example
1647
- $('#username').on('save', function(e, params) {
1648
- alert('Saved value: ' + params.newValue);
1649
- });
1650
- **/
1651
- //event itself is triggered by editableContainer. Description here is only for documentation
1652
- },
1653
-
1654
- validate: function () {
1655
- if (typeof this.options.validate === 'function') {
1656
- return this.options.validate.call(this, this.value);
1657
- }
1658
- },
1659
-
1660
- /**
1661
- Sets new value of editable
1662
- @method setValue(value, convertStr)
1663
- @param {mixed} value new value
1664
- @param {boolean} convertStr whether to convert value from string to internal format
1665
- **/
1666
- setValue: function(value, convertStr, response) {
1667
- if(convertStr) {
1668
- this.value = this.input.str2value(value);
1669
- } else {
1670
- this.value = value;
1671
- }
1672
- if(this.container) {
1673
- this.container.option('value', this.value);
1674
- }
1675
- $.when(this.render(response))
1676
- .then($.proxy(function() {
1677
- this.handleEmpty();
1678
- }, this));
1679
- },
1680
-
1681
- /**
1682
- Activates input of visible container (e.g. set focus)
1683
- @method activate()
1684
- **/
1685
- activate: function() {
1686
- if(this.container) {
1687
- this.container.activate();
1688
- }
1689
- },
1690
-
1691
- /**
1692
- Removes editable feature from element
1693
- @method destroy()
1694
- **/
1695
- destroy: function() {
1696
- if(this.container) {
1697
- this.container.destroy();
1698
- }
1699
-
1700
- if(this.options.toggle !== 'manual') {
1701
- this.$element.removeClass('editable-click');
1702
- this.$element.off(this.options.toggle + '.editable');
1703
- }
1704
-
1705
- this.$element.off("save.internal");
1706
-
1707
- this.$element.removeClass('editable');
1708
- this.$element.removeClass('editable-open');
1709
- this.$element.removeData('editable');
1710
- }
1711
- };
1712
-
1713
- /* EDITABLE PLUGIN DEFINITION
1714
- * ======================= */
1715
-
1716
- /**
1717
- jQuery method to initialize editable element.
1718
-
1719
- @method $().editable(options)
1720
- @params {Object} options
1721
- @example
1722
- $('#username').editable({
1723
- type: 'text',
1724
- url: '/post',
1725
- pk: 1
1726
- });
1727
- **/
1728
- $.fn.editable = function (option) {
1729
- //special API methods returning non-jquery object
1730
- var result = {}, args = arguments, datakey = 'editable';
1731
- switch (option) {
1732
- /**
1733
- Runs client-side validation for all matched editables
1734
-
1735
- @method validate()
1736
- @returns {Object} validation errors map
1737
- @example
1738
- $('#username, #fullname').editable('validate');
1739
- // possible result:
1740
- {
1741
- username: "username is required",
1742
- fullname: "fullname should be minimum 3 letters length"
1743
- }
1744
- **/
1745
- case 'validate':
1746
- this.each(function () {
1747
- var $this = $(this), data = $this.data(datakey), error;
1748
- if (data && (error = data.validate())) {
1749
- result[data.options.name] = error;
1750
- }
1751
- });
1752
- return result;
1753
-
1754
- /**
1755
- Returns current values of editable elements. If value is <code>null</code> or <code>undefined</code> it will not be returned
1756
- @method getValue()
1757
- @returns {Object} object of element names and values
1758
- @example
1759
- $('#username, #fullname').editable('validate');
1760
- // possible result:
1761
- {
1762
- username: "superuser",
1763
- fullname: "John"
1764
- }
1765
- **/
1766
- case 'getValue':
1767
- this.each(function () {
1768
- var $this = $(this), data = $this.data(datakey);
1769
- if (data && data.value !== undefined && data.value !== null) {
1770
- result[data.options.name] = data.input.value2submit(data.value);
1771
- }
1772
- });
1773
- return result;
1774
-
1775
- /**
1776
- This method collects values from several editable elements and submit them all to server.
1777
- Internally it runs client-side validation for all fields and submits only in case of success.
1778
- See <a href="#newrecord">creating new records</a> for details.
1779
-
1780
- @method submit(options)
1781
- @param {object} options
1782
- @param {object} options.url url to submit data
1783
- @param {object} options.data additional data to submit
1784
- @param {object} options.ajaxOptions additional ajax options
1785
- @param {function} options.error(obj) error handler
1786
- @param {function} options.success(obj,config) success handler
1787
- @returns {Object} jQuery object
1788
- **/
1789
- case 'submit': //collects value, validate and submit to server for creating new record
1790
- var config = arguments[1] || {},
1791
- $elems = this,
1792
- errors = this.editable('validate'),
1793
- values;
1794
-
1795
- if($.isEmptyObject(errors)) {
1796
- values = this.editable('getValue');
1797
- if(config.data) {
1798
- $.extend(values, config.data);
1799
- }
1800
-
1801
- $.ajax($.extend({
1802
- url: config.url,
1803
- data: values,
1804
- type: 'POST'
1805
- }, config.ajaxOptions))
1806
- .success(function(response) {
1807
- //successful response 200 OK
1808
- if(typeof config.success === 'function') {
1809
- config.success.call($elems, response, config);
1810
- }
1811
- })
1812
- .error(function(){ //ajax error
1813
- if(typeof config.error === 'function') {
1814
- config.error.apply($elems, arguments);
1815
- }
1816
- });
1817
- } else { //client-side validation error
1818
- if(typeof config.error === 'function') {
1819
- config.error.call($elems, errors);
1820
- }
1821
- }
1822
- return this;
1823
- }
1824
-
1825
- //return jquery object
1826
- return this.each(function () {
1827
- var $this = $(this),
1828
- data = $this.data(datakey),
1829
- options = typeof option === 'object' && option;
1830
-
1831
- if (!data) {
1832
- $this.data(datakey, (data = new Editable(this, options)));
1833
- }
1834
-
1835
- if (typeof option === 'string') { //call method
1836
- data[option].apply(data, Array.prototype.slice.call(args, 1));
1837
- }
1838
- });
1839
- };
1840
-
1841
-
1842
- $.fn.editable.defaults = {
1843
- /**
1844
- Type of input. Can be <code>text|textarea|select|date|checklist</code> and more
1845
-
1846
- @property type
1847
- @type string
1848
- @default 'text'
1849
- **/
1850
- type: 'text',
1851
- /**
1852
- Sets disabled state of editable
1853
-
1854
- @property disabled
1855
- @type boolean
1856
- @default false
1857
- **/
1858
- disabled: false,
1859
- /**
1860
- How to toggle editable. Can be <code>click|dblclick|mouseenter|manual</code>.
1861
- When set to <code>manual</code> you should manually call <code>show/hide</code> methods of editable.
1862
- **Note**: if you call <code>show</code> or <code>toggle</code> inside **click** handler of some DOM element,
1863
- you need to apply <code>e.stopPropagation()</code> because containers are being closed on any click on document.
1864
-
1865
- @example
1866
- $('#edit-button').click(function(e) {
1867
- e.stopPropagation();
1868
- $('#username').editable('toggle');
1869
- });
1870
-
1871
- @property toggle
1872
- @type string
1873
- @default 'click'
1874
- **/
1875
- toggle: 'click',
1876
- /**
1877
- Text shown when element is empty.
1878
-
1879
- @property emptytext
1880
- @type string
1881
- @default 'Empty'
1882
- **/
1883
- emptytext: 'Empty',
1884
- /**
1885
- Allows to automatically set element's text based on it's value. Can be <code>auto|always|never</code>. Useful for select and date.
1886
- For example, if dropdown list is <code>{1: 'a', 2: 'b'}</code> and element's value set to <code>1</code>, it's html will be automatically set to <code>'a'</code>.
1887
- <code>auto</code> - text will be automatically set only if element is empty.
1888
- <code>always|never</code> - always(never) try to set element's text.
1889
-
1890
- @property autotext
1891
- @type string
1892
- @default 'auto'
1893
- **/
1894
- autotext: 'auto',
1895
- /**
1896
- Initial value of input. If not set, taken from element's text.
1897
-
1898
- @property value
1899
- @type mixed
1900
- @default element's text
1901
- **/
1902
- value: null,
1903
- /**
1904
- Callback to perform custom displaying of value in element's text.
1905
- If `null`, default input's display used.
1906
- If `false`, no displaying methods will be called, element's text will never change.
1907
- Runs under element's scope.
1908
- _Parameters:_
1909
-
1910
- * `value` current value to be displayed
1911
- * `response` server response (if display called after ajax submit), since 1.4.0
1912
-
1913
- For **inputs with source** (select, checklist) parameters are different:
1914
-
1915
- * `value` current value to be displayed
1916
- * `sourceData` array of items for current input (e.g. dropdown items)
1917
- * `response` server response (if display called after ajax submit), since 1.4.0
1918
-
1919
- To get currently selected items use `$.fn.editableutils.itemsByValue(value, sourceData)`.
1920
-
1921
- @property display
1922
- @type function|boolean
1923
- @default null
1924
- @since 1.2.0
1925
- @example
1926
- display: function(value, sourceData) {
1927
- //display checklist as comma-separated values
1928
- var html = [],
1929
- checked = $.fn.editableutils.itemsByValue(value, sourceData);
1930
-
1931
- if(checked.length) {
1932
- $.each(checked, function(i, v) { html.push($.fn.editableutils.escape(v.text)); });
1933
- $(this).html(html.join(', '));
1934
- } else {
1935
- $(this).empty();
1936
- }
1937
- }
1938
- **/
1939
- display: null,
1940
- /**
1941
- Css class applied when editable text is empty.
1942
-
1943
- @property emptyclass
1944
- @type string
1945
- @since 1.4.1
1946
- @default editable-empty
1947
- **/
1948
- emptyclass: 'editable-empty',
1949
- /**
1950
- Css class applied when value was stored but not sent to server (`pk` is empty or `send = 'never'`).
1951
- You may set it to `null` if you work with editables locally and submit them together.
1952
-
1953
- @property unsavedclass
1954
- @type string
1955
- @since 1.4.1
1956
- @default editable-unsaved
1957
- **/
1958
- unsavedclass: 'editable-unsaved',
1959
- /**
1960
- If a css selector is provided, editable will be delegated to the specified targets.
1961
- Usefull for dynamically generated DOM elements.
1962
- **Please note**, that delegated targets can't use `emptytext` and `autotext` options,
1963
- as they are initialized after first click.
1964
-
1965
- @property selector
1966
- @type string
1967
- @since 1.4.1
1968
- @default null
1969
- @example
1970
- <div id="user">
1971
- <a href="#" data-name="username" data-type="text" title="Username">awesome</a>
1972
- <a href="#" data-name="group" data-type="select" data-source="/groups" data-value="1" title="Group">Operator</a>
1973
- </div>
1974
-
1975
- <script>
1976
- $('#user').editable({
1977
- selector: 'a',
1978
- url: '/post',
1979
- pk: 1
1980
- });
1981
- </script>
1982
- **/
1983
- selector: null
1984
- };
1985
-
1986
- }(window.jQuery));
1987
-
1988
- /**
1989
- AbstractInput - base class for all editable inputs.
1990
- It defines interface to be implemented by any input type.
1991
- To create your own input you can inherit from this class.
1992
-
1993
- @class abstractinput
1994
- **/
1995
- (function ($) {
1996
-
1997
- //types
1998
- $.fn.editabletypes = {};
1999
-
2000
- var AbstractInput = function () { };
2001
-
2002
- AbstractInput.prototype = {
2003
- /**
2004
- Initializes input
2005
-
2006
- @method init()
2007
- **/
2008
- init: function(type, options, defaults) {
2009
- this.type = type;
2010
- this.options = $.extend({}, defaults, options);
2011
- },
2012
-
2013
- /*
2014
- this method called before render to init $tpl that is inserted in DOM
2015
- */
2016
- prerender: function() {
2017
- this.$tpl = $(this.options.tpl); //whole tpl as jquery object
2018
- this.$input = this.$tpl; //control itself, can be changed in render method
2019
- this.$clear = null; //clear button
2020
- this.error = null; //error message, if input cannot be rendered
2021
- },
2022
-
2023
- /**
2024
- Renders input from tpl. Can return jQuery deferred object.
2025
- Can be overwritten in child objects
2026
-
2027
- @method render()
2028
- **/
2029
- render: function() {
2030
-
2031
- },
2032
-
2033
- /**
2034
- Sets element's html by value.
2035
-
2036
- @method value2html(value, element)
2037
- @param {mixed} value
2038
- @param {DOMElement} element
2039
- **/
2040
- value2html: function(value, element) {
2041
- $(element).text(value);
2042
- },
2043
-
2044
- /**
2045
- Converts element's html to value
2046
-
2047
- @method html2value(html)
2048
- @param {string} html
2049
- @returns {mixed}
2050
- **/
2051
- html2value: function(html) {
2052
- return $('<div>').html(html).text();
2053
- },
2054
-
2055
- /**
2056
- Converts value to string (for internal compare). For submitting to server used value2submit().
2057
-
2058
- @method value2str(value)
2059
- @param {mixed} value
2060
- @returns {string}
2061
- **/
2062
- value2str: function(value) {
2063
- return value;
2064
- },
2065
-
2066
- /**
2067
- Converts string received from server into value. Usually from `data-value` attribute.
2068
-
2069
- @method str2value(str)
2070
- @param {string} str
2071
- @returns {mixed}
2072
- **/
2073
- str2value: function(str) {
2074
- return str;
2075
- },
2076
-
2077
- /**
2078
- Converts value for submitting to server. Result can be string or object.
2079
-
2080
- @method value2submit(value)
2081
- @param {mixed} value
2082
- @returns {mixed}
2083
- **/
2084
- value2submit: function(value) {
2085
- return value;
2086
- },
2087
-
2088
- /**
2089
- Sets value of input.
2090
-
2091
- @method value2input(value)
2092
- @param {mixed} value
2093
- **/
2094
- value2input: function(value) {
2095
- this.$input.val(value);
2096
- },
2097
-
2098
- /**
2099
- Returns value of input. Value can be object (e.g. datepicker)
2100
-
2101
- @method input2value()
2102
- **/
2103
- input2value: function() {
2104
- return this.$input.val();
2105
- },
2106
-
2107
- /**
2108
- Activates input. For text it sets focus.
2109
-
2110
- @method activate()
2111
- **/
2112
- activate: function() {
2113
- if(this.$input.is(':visible')) {
2114
- this.$input.focus();
2115
- }
2116
- },
2117
-
2118
- /**
2119
- Creates input.
2120
-
2121
- @method clear()
2122
- **/
2123
- clear: function() {
2124
- this.$input.val(null);
2125
- },
2126
-
2127
- /**
2128
- method to escape html.
2129
- **/
2130
- escape: function(str) {
2131
- return $('<div>').text(str).html();
2132
- },
2133
-
2134
- /**
2135
- attach handler to automatically submit form when value changed (useful when buttons not shown)
2136
- **/
2137
- autosubmit: function() {
2138
-
2139
- },
2140
-
2141
- // -------- helper functions --------
2142
- setClass: function() {
2143
- if(this.options.inputclass) {
2144
- this.$input.addClass(this.options.inputclass);
2145
- }
2146
- },
2147
-
2148
- setAttr: function(attr) {
2149
- if (this.options[attr]) {
2150
- this.$input.attr(attr, this.options[attr]);
2151
- }
2152
- },
2153
-
2154
- option: function(key, value) {
2155
- this.options[key] = value;
2156
- }
2157
-
2158
- };
2159
-
2160
- AbstractInput.defaults = {
2161
- /**
2162
- HTML template of input. Normally you should not change it.
2163
-
2164
- @property tpl
2165
- @type string
2166
- @default ''
2167
- **/
2168
- tpl: '',
2169
- /**
2170
- CSS class automatically applied to input
2171
-
2172
- @property inputclass
2173
- @type string
2174
- @default input-medium
2175
- **/
2176
- inputclass: 'input-medium'
2177
- };
2178
-
2179
- $.extend($.fn.editabletypes, {abstractinput: AbstractInput});
2180
-
2181
- }(window.jQuery));
2182
-
2183
- /**
2184
- List - abstract class for inputs that have source option loaded from js array or via ajax
2185
-
2186
- @class list
2187
- @extends abstractinput
2188
- **/
2189
- (function ($) {
2190
-
2191
- var List = function (options) {
2192
-
2193
- };
2194
-
2195
- $.fn.editableutils.inherit(List, $.fn.editabletypes.abstractinput);
2196
-
2197
- $.extend(List.prototype, {
2198
- render: function () {
2199
- var deferred = $.Deferred();
2200
-
2201
- this.error = null;
2202
- this.onSourceReady(function () {
2203
- this.renderList();
2204
- deferred.resolve();
2205
- }, function () {
2206
- this.error = this.options.sourceError;
2207
- deferred.resolve();
2208
- });
2209
-
2210
- return deferred.promise();
2211
- },
2212
-
2213
- html2value: function (html) {
2214
- return null; //can't set value by text
2215
- },
2216
-
2217
- value2html: function (value, element, display, response) {
2218
- var deferred = $.Deferred(),
2219
- success = function () {
2220
- if(typeof display === 'function') {
2221
- //custom display method
2222
- display.call(element, value, this.sourceData, response);
2223
- } else {
2224
- this.value2htmlFinal(value, element);
2225
- }
2226
- deferred.resolve();
2227
- };
2228
-
2229
- //for null value just call success without loading source
2230
- if(value === null) {
2231
- success.call(this);
2232
- } else {
2233
- this.onSourceReady(success, function () { deferred.resolve(); });
2234
- }
2235
-
2236
- return deferred.promise();
2237
- },
2238
-
2239
- // ------------- additional functions ------------
2240
-
2241
- onSourceReady: function (success, error) {
2242
- //if allready loaded just call success
2243
- if($.isArray(this.sourceData)) {
2244
- success.call(this);
2245
- return;
2246
- }
2247
-
2248
- // try parse json in single quotes (for double quotes jquery does automatically)
2249
- try {
2250
- this.options.source = $.fn.editableutils.tryParseJson(this.options.source, false);
2251
- } catch (e) {
2252
- error.call(this);
2253
- return;
2254
- }
2255
-
2256
- //loading from url
2257
- if (typeof this.options.source === 'string') {
2258
- //try to get from cache
2259
- if(this.options.sourceCache) {
2260
- var cacheID = this.options.source,
2261
- cache;
2262
-
2263
- if (!$(document).data(cacheID)) {
2264
- $(document).data(cacheID, {});
2265
- }
2266
- cache = $(document).data(cacheID);
2267
-
2268
- //check for cached data
2269
- if (cache.loading === false && cache.sourceData) { //take source from cache
2270
- this.sourceData = cache.sourceData;
2271
- this.doPrepend();
2272
- success.call(this);
2273
- return;
2274
- } else if (cache.loading === true) { //cache is loading, put callback in stack to be called later
2275
- cache.callbacks.push($.proxy(function () {
2276
- this.sourceData = cache.sourceData;
2277
- this.doPrepend();
2278
- success.call(this);
2279
- }, this));
2280
-
2281
- //also collecting error callbacks
2282
- cache.err_callbacks.push($.proxy(error, this));
2283
- return;
2284
- } else { //no cache yet, activate it
2285
- cache.loading = true;
2286
- cache.callbacks = [];
2287
- cache.err_callbacks = [];
2288
- }
2289
- }
2290
-
2291
- //loading sourceData from server
2292
- $.ajax({
2293
- url: this.options.source,
2294
- type: 'get',
2295
- cache: false,
2296
- dataType: 'json',
2297
- success: $.proxy(function (data) {
2298
- if(cache) {
2299
- cache.loading = false;
2300
- }
2301
- this.sourceData = this.makeArray(data);
2302
- if($.isArray(this.sourceData)) {
2303
- if(cache) {
2304
- //store result in cache
2305
- cache.sourceData = this.sourceData;
2306
- //run success callbacks for other fields waiting for this source
2307
- $.each(cache.callbacks, function () { this.call(); });
2308
- }
2309
- this.doPrepend();
2310
- success.call(this);
2311
- } else {
2312
- error.call(this);
2313
- if(cache) {
2314
- //run error callbacks for other fields waiting for this source
2315
- $.each(cache.err_callbacks, function () { this.call(); });
2316
- }
2317
- }
2318
- }, this),
2319
- error: $.proxy(function () {
2320
- error.call(this);
2321
- if(cache) {
2322
- cache.loading = false;
2323
- //run error callbacks for other fields
2324
- $.each(cache.err_callbacks, function () { this.call(); });
2325
- }
2326
- }, this)
2327
- });
2328
- } else { //options as json/array/function
2329
- if ($.isFunction(this.options.source)) {
2330
- this.sourceData = this.makeArray(this.options.source());
2331
- } else {
2332
- this.sourceData = this.makeArray(this.options.source);
2333
- }
2334
-
2335
- if($.isArray(this.sourceData)) {
2336
- this.doPrepend();
2337
- success.call(this);
2338
- } else {
2339
- error.call(this);
2340
- }
2341
- }
2342
- },
2343
-
2344
- doPrepend: function () {
2345
- if(this.options.prepend === null || this.options.prepend === undefined) {
2346
- return;
2347
- }
2348
-
2349
- if(!$.isArray(this.prependData)) {
2350
- //try parse json in single quotes
2351
- this.options.prepend = $.fn.editableutils.tryParseJson(this.options.prepend, true);
2352
- if (typeof this.options.prepend === 'string') {
2353
- this.options.prepend = {'': this.options.prepend};
2354
- }
2355
- if (typeof this.options.prepend === 'function') {
2356
- this.prependData = this.makeArray(this.options.prepend());
2357
- } else {
2358
- this.prependData = this.makeArray(this.options.prepend);
2359
- }
2360
- }
2361
-
2362
- if($.isArray(this.prependData) && $.isArray(this.sourceData)) {
2363
- this.sourceData = this.prependData.concat(this.sourceData);
2364
- }
2365
- },
2366
-
2367
- /*
2368
- renders input list
2369
- */
2370
- renderList: function() {
2371
- // this method should be overwritten in child class
2372
- },
2373
-
2374
- /*
2375
- set element's html by value
2376
- */
2377
- value2htmlFinal: function(value, element) {
2378
- // this method should be overwritten in child class
2379
- },
2380
-
2381
- /**
2382
- * convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}]
2383
- */
2384
- makeArray: function(data) {
2385
- var count, obj, result = [], item, iterateItem;
2386
- if(!data || typeof data === 'string') {
2387
- return null;
2388
- }
2389
-
2390
- if($.isArray(data)) { //array
2391
- /*
2392
- function to iterate inside item of array if item is object.
2393
- Caclulates count of keys in item and store in obj.
2394
- */
2395
- iterateItem = function (k, v) {
2396
- obj = {value: k, text: v};
2397
- if(count++ >= 2) {
2398
- return false;// exit from `each` if item has more than one key.
2399
- }
2400
- };
2401
-
2402
- for(var i = 0; i < data.length; i++) {
2403
- item = data[i];
2404
- if(typeof item === 'object') {
2405
- count = 0; //count of keys inside item
2406
- $.each(item, iterateItem);
2407
- //case: [{val1: 'text1'}, {val2: 'text2} ...]
2408
- if(count === 1) {
2409
- result.push(obj);
2410
- //case: [{value: 1, text: 'text1'}, {value: 2, text: 'text2'}, ...]
2411
- } else if(count > 1) {
2412
- //removed check of existance: item.hasOwnProperty('value') && item.hasOwnProperty('text')
2413
- if(item.children) {
2414
- item.children = this.makeArray(item.children);
2415
- }
2416
- result.push(item);
2417
- }
2418
- } else {
2419
- //case: ['text1', 'text2' ...]
2420
- result.push({value: item, text: item});
2421
- }
2422
- }
2423
- } else { //case: {val1: 'text1', val2: 'text2, ...}
2424
- $.each(data, function (k, v) {
2425
- result.push({value: k, text: v});
2426
- });
2427
- }
2428
- return result;
2429
- },
2430
-
2431
- option: function(key, value) {
2432
- this.options[key] = value;
2433
- if(key === 'source') {
2434
- this.sourceData = null;
2435
- }
2436
- if(key === 'prepend') {
2437
- this.prependData = null;
2438
- }
2439
- }
2440
-
2441
- });
2442
-
2443
- List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
2444
- /**
2445
- Source data for list.
2446
- If **array** - it should be in format: `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`
2447
- For compability, object format is also supported: `{"1": "text1", "2": "text2" ...}` but it does not guarantee elements order.
2448
-
2449
- If **string** - considered ajax url to load items. In that case results will be cached for fields with the same source and name. See also `sourceCache` option.
2450
-
2451
- If **function**, it should return data in format above (since 1.4.0).
2452
-
2453
- Since 1.4.1 key `children` supported to render OPTGROUP (for **select** input only).
2454
- `[{text: "group1", children: [{value: 1, text: "text1"}, {value: 2, text: "text2"}]}, ...]`
2455
-
2456
-
2457
- @property source
2458
- @type string | array | object | function
2459
- @default null
2460
- **/
2461
- source: null,
2462
- /**
2463
- Data automatically prepended to the beginning of dropdown list.
2464
-
2465
- @property prepend
2466
- @type string | array | object | function
2467
- @default false
2468
- **/
2469
- prepend: false,
2470
- /**
2471
- Error message when list cannot be loaded (e.g. ajax error)
2472
-
2473
- @property sourceError
2474
- @type string
2475
- @default Error when loading list
2476
- **/
2477
- sourceError: 'Error when loading list',
2478
- /**
2479
- if <code>true</code> and source is **string url** - results will be cached for fields with the same source.
2480
- Usefull for editable column in grid to prevent extra requests.
2481
-
2482
- @property sourceCache
2483
- @type boolean
2484
- @default true
2485
- @since 1.2.0
2486
- **/
2487
- sourceCache: true
2488
- });
2489
-
2490
- $.fn.editabletypes.list = List;
2491
-
2492
- }(window.jQuery));
2493
-
2494
- /**
2495
- Text input
2496
-
2497
- @class text
2498
- @extends abstractinput
2499
- @final
2500
- @example
2501
- <a href="#" id="username" data-type="text" data-pk="1">awesome</a>
2502
- <script>
2503
- $(function(){
2504
- $('#username').editable({
2505
- url: '/post',
2506
- title: 'Enter username'
2507
- });
2508
- });
2509
- </script>
2510
- **/
2511
- (function ($) {
2512
- var Text = function (options) {
2513
- this.init('text', options, Text.defaults);
2514
- };
2515
-
2516
- $.fn.editableutils.inherit(Text, $.fn.editabletypes.abstractinput);
2517
-
2518
- $.extend(Text.prototype, {
2519
- render: function() {
2520
- this.renderClear();
2521
- this.setClass();
2522
- this.setAttr('placeholder');
2523
- },
2524
-
2525
- activate: function() {
2526
- if(this.$input.is(':visible')) {
2527
- this.$input.focus();
2528
- $.fn.editableutils.setCursorPosition(this.$input.get(0), this.$input.val().length);
2529
- if(this.toggleClear) {
2530
- this.toggleClear();
2531
- }
2532
- }
2533
- },
2534
-
2535
- //render clear button
2536
- renderClear: function() {
2537
- if (this.options.clear) {
2538
- this.$clear = $('<span class="editable-clear-x"></span>');
2539
- this.$input.after(this.$clear)
2540
- .css('padding-right', 20)
2541
- .keyup($.proxy(this.toggleClear, this))
2542
- .parent().css('position', 'relative');
2543
-
2544
- this.$clear.click($.proxy(this.clear, this));
2545
- }
2546
- },
2547
-
2548
- postrender: function() {
2549
- if(this.$clear) {
2550
- //can position clear button only here, when form is shown and height can be calculated
2551
- var h = this.$input.outerHeight() || 20,
2552
- delta = (h - this.$clear.height()) / 2;
2553
-
2554
- //workaround for plain-popup
2555
- if(delta < 3) {
2556
- delta = 3;
2557
- }
2558
-
2559
- this.$clear.css({top: delta, right: delta});
2560
- }
2561
- },
2562
-
2563
- //show / hide clear button
2564
- toggleClear: function() {
2565
- if(!this.$clear) {
2566
- return;
2567
- }
2568
-
2569
- if(this.$input.val().length) {
2570
- this.$clear.show();
2571
- } else {
2572
- this.$clear.hide();
2573
- }
2574
- },
2575
-
2576
- clear: function() {
2577
- this.$clear.hide();
2578
- this.$input.val('').focus();
2579
- }
2580
- });
2581
-
2582
- Text.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
2583
- /**
2584
- @property tpl
2585
- @default <input type="text">
2586
- **/
2587
- tpl: '<input type="text">',
2588
- /**
2589
- Placeholder attribute of input. Shown when input is empty.
2590
-
2591
- @property placeholder
2592
- @type string
2593
- @default null
2594
- **/
2595
- placeholder: null,
2596
-
2597
- /**
2598
- Whether to show `clear` button
2599
-
2600
- @property clear
2601
- @type boolean
2602
- @default true
2603
- **/
2604
- clear: true
2605
- });
2606
-
2607
- $.fn.editabletypes.text = Text;
2608
-
2609
- }(window.jQuery));
2610
-
2611
- /**
2612
- Textarea input
2613
-
2614
- @class textarea
2615
- @extends abstractinput
2616
- @final
2617
- @example
2618
- <a href="#" id="comments" data-type="textarea" data-pk="1">awesome comment!</a>
2619
- <script>
2620
- $(function(){
2621
- $('#comments').editable({
2622
- url: '/post',
2623
- title: 'Enter comments',
2624
- rows: 10
2625
- });
2626
- });
2627
- </script>
2628
- **/
2629
- (function ($) {
2630
-
2631
- var Textarea = function (options) {
2632
- this.init('textarea', options, Textarea.defaults);
2633
- };
2634
-
2635
- $.fn.editableutils.inherit(Textarea, $.fn.editabletypes.abstractinput);
2636
-
2637
- $.extend(Textarea.prototype, {
2638
- render: function () {
2639
- this.setClass();
2640
- this.setAttr('placeholder');
2641
- this.setAttr('rows');
2642
-
2643
- //ctrl + enter
2644
- this.$input.keydown(function (e) {
2645
- if (e.ctrlKey && e.which === 13) {
2646
- $(this).closest('form').submit();
2647
- }
2648
- });
2649
- },
2650
-
2651
- value2html: function(value, element) {
2652
- var html = '', lines;
2653
- if(value) {
2654
- lines = value.split("\n");
2655
- for (var i = 0; i < lines.length; i++) {
2656
- lines[i] = $('<div>').text(lines[i]).html();
2657
- }
2658
- html = lines.join('<br>');
2659
- }
2660
- $(element).html(html);
2661
- },
2662
-
2663
- html2value: function(html) {
2664
- if(!html) {
2665
- return '';
2666
- }
2667
-
2668
- var regex = new RegExp(String.fromCharCode(10), 'g');
2669
- var lines = html.split(/<br\s*\/?>/i);
2670
- for (var i = 0; i < lines.length; i++) {
2671
- var text = $('<div>').html(lines[i]).text();
2672
-
2673
- // Remove newline characters (\n) to avoid them being converted by value2html() method
2674
- // thus adding extra <br> tags
2675
- text = text.replace(regex, '');
2676
-
2677
- lines[i] = text;
2678
- }
2679
- return lines.join("\n");
2680
- },
2681
-
2682
- activate: function() {
2683
- $.fn.editabletypes.text.prototype.activate.call(this);
2684
- }
2685
- });
2686
-
2687
- Textarea.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
2688
- /**
2689
- @property tpl
2690
- @default <textarea></textarea>
2691
- **/
2692
- tpl:'<textarea></textarea>',
2693
- /**
2694
- @property inputclass
2695
- @default input-large
2696
- **/
2697
- inputclass: 'input-large',
2698
- /**
2699
- Placeholder attribute of input. Shown when input is empty.
2700
-
2701
- @property placeholder
2702
- @type string
2703
- @default null
2704
- **/
2705
- placeholder: null,
2706
- /**
2707
- Number of rows in textarea
2708
-
2709
- @property rows
2710
- @type integer
2711
- @default 7
2712
- **/
2713
- rows: 7
2714
- });
2715
-
2716
- $.fn.editabletypes.textarea = Textarea;
2717
-
2718
- }(window.jQuery));
2719
-
2720
- /**
2721
- Select (dropdown)
2722
-
2723
- @class select
2724
- @extends list
2725
- @final
2726
- @example
2727
- <a href="#" id="status" data-type="select" data-pk="1" data-url="/post" data-original-title="Select status"></a>
2728
- <script>
2729
- $(function(){
2730
- $('#status').editable({
2731
- value: 2,
2732
- source: [
2733
- {value: 1, text: 'Active'},
2734
- {value: 2, text: 'Blocked'},
2735
- {value: 3, text: 'Deleted'}
2736
- ]
2737
- }
2738
- });
2739
- });
2740
- </script>
2741
- **/
2742
- (function ($) {
2743
-
2744
- var Select = function (options) {
2745
- this.init('select', options, Select.defaults);
2746
- };
2747
-
2748
- $.fn.editableutils.inherit(Select, $.fn.editabletypes.list);
2749
-
2750
- $.extend(Select.prototype, {
2751
- renderList: function() {
2752
- this.$input.empty();
2753
-
2754
- var fillItems = function($el, data) {
2755
- if($.isArray(data)) {
2756
- for(var i=0; i<data.length; i++) {
2757
- if(data[i].children) {
2758
- $el.append(fillItems($('<optgroup>', {label: data[i].text}), data[i].children));
2759
- } else {
2760
- $el.append($('<option>', {value: data[i].value}).text(data[i].text));
2761
- }
2762
- }
2763
- }
2764
- return $el;
2765
- };
2766
-
2767
- fillItems(this.$input, this.sourceData);
2768
-
2769
- this.setClass();
2770
-
2771
- //enter submit
2772
- this.$input.on('keydown.editable', function (e) {
2773
- if (e.which === 13) {
2774
- $(this).closest('form').submit();
2775
- }
2776
- });
2777
- },
2778
-
2779
- value2htmlFinal: function(value, element) {
2780
- var text = '',
2781
- items = $.fn.editableutils.itemsByValue(value, this.sourceData);
2782
-
2783
- if(items.length) {
2784
- text = items[0].text;
2785
- }
2786
-
2787
- $(element).text(text);
2788
- },
2789
-
2790
- autosubmit: function() {
2791
- this.$input.off('keydown.editable').on('change.editable', function(){
2792
- $(this).closest('form').submit();
2793
- });
2794
- }
2795
- });
2796
-
2797
- Select.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
2798
- /**
2799
- @property tpl
2800
- @default <select></select>
2801
- **/
2802
- tpl:'<select></select>'
2803
- });
2804
-
2805
- $.fn.editabletypes.select = Select;
2806
-
2807
- }(window.jQuery));
2808
- /**
2809
- List of checkboxes.
2810
- Internally value stored as javascript array of values.
2811
-
2812
- @class checklist
2813
- @extends list
2814
- @final
2815
- @example
2816
- <a href="#" id="options" data-type="checklist" data-pk="1" data-url="/post" data-original-title="Select options"></a>
2817
- <script>
2818
- $(function(){
2819
- $('#options').editable({
2820
- value: [2, 3],
2821
- source: [
2822
- {value: 1, text: 'option1'},
2823
- {value: 2, text: 'option2'},
2824
- {value: 3, text: 'option3'}
2825
- ]
2826
- }
2827
- });
2828
- });
2829
- </script>
2830
- **/
2831
- (function ($) {
2832
-
2833
- var Checklist = function (options) {
2834
- this.init('checklist', options, Checklist.defaults);
2835
- };
2836
-
2837
- $.fn.editableutils.inherit(Checklist, $.fn.editabletypes.list);
2838
-
2839
- $.extend(Checklist.prototype, {
2840
- renderList: function() {
2841
- var $label, $div;
2842
-
2843
- this.$tpl.empty();
2844
-
2845
- if(!$.isArray(this.sourceData)) {
2846
- return;
2847
- }
2848
-
2849
- for(var i=0; i<this.sourceData.length; i++) {
2850
- $label = $('<label>').append($('<input>', {
2851
- type: 'checkbox',
2852
- value: this.sourceData[i].value
2853
- }))
2854
- .append($('<span>').text(' '+this.sourceData[i].text));
2855
-
2856
- $('<div>').append($label).appendTo(this.$tpl);
2857
- }
2858
-
2859
- this.$input = this.$tpl.find('input[type="checkbox"]');
2860
- this.setClass();
2861
- },
2862
-
2863
- value2str: function(value) {
2864
- return $.isArray(value) ? value.sort().join($.trim(this.options.separator)) : '';
2865
- },
2866
-
2867
- //parse separated string
2868
- str2value: function(str) {
2869
- var reg, value = null;
2870
- if(typeof str === 'string' && str.length) {
2871
- reg = new RegExp('\\s*'+$.trim(this.options.separator)+'\\s*');
2872
- value = str.split(reg);
2873
- } else if($.isArray(str)) {
2874
- value = str;
2875
- }
2876
- return value;
2877
- },
2878
-
2879
- //set checked on required checkboxes
2880
- value2input: function(value) {
2881
- this.$input.prop('checked', false);
2882
- if($.isArray(value) && value.length) {
2883
- this.$input.each(function(i, el) {
2884
- var $el = $(el);
2885
- // cannot use $.inArray as it performs strict comparison
2886
- $.each(value, function(j, val){
2887
- /*jslint eqeq: true*/
2888
- if($el.val() == val) {
2889
- /*jslint eqeq: false*/
2890
- $el.prop('checked', true);
2891
- }
2892
- });
2893
- });
2894
- }
2895
- },
2896
-
2897
- input2value: function() {
2898
- var checked = [];
2899
- this.$input.filter(':checked').each(function(i, el) {
2900
- checked.push($(el).val());
2901
- });
2902
- return checked;
2903
- },
2904
-
2905
- //collect text of checked boxes
2906
- value2htmlFinal: function(value, element) {
2907
- var html = [],
2908
- checked = $.fn.editableutils.itemsByValue(value, this.sourceData);
2909
-
2910
- if(checked.length) {
2911
- $.each(checked, function(i, v) { html.push($.fn.editableutils.escape(v.text)); });
2912
- $(element).html(html.join('<br>'));
2913
- } else {
2914
- $(element).empty();
2915
- }
2916
- },
2917
-
2918
- activate: function() {
2919
- this.$input.first().focus();
2920
- },
2921
-
2922
- autosubmit: function() {
2923
- this.$input.on('keydown', function(e){
2924
- if (e.which === 13) {
2925
- $(this).closest('form').submit();
2926
- }
2927
- });
2928
- }
2929
- });
2930
-
2931
- Checklist.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
2932
- /**
2933
- @property tpl
2934
- @default <div></div>
2935
- **/
2936
- tpl:'<div class="editable-checklist"></div>',
2937
-
2938
- /**
2939
- @property inputclass
2940
- @type string
2941
- @default null
2942
- **/
2943
- inputclass: null,
2944
-
2945
- /**
2946
- Separator of values when reading from `data-value` attribute
2947
-
2948
- @property separator
2949
- @type string
2950
- @default ','
2951
- **/
2952
- separator: ','
2953
- });
2954
-
2955
- $.fn.editabletypes.checklist = Checklist;
2956
-
2957
- }(window.jQuery));
2958
-
2959
- /**
2960
- HTML5 input types.
2961
- Following types are supported:
2962
-
2963
- * password
2964
- * email
2965
- * url
2966
- * tel
2967
- * number
2968
- * range
2969
-
2970
- Learn more about html5 inputs:
2971
- http://www.w3.org/wiki/HTML5_form_additions
2972
- To check browser compatibility please see:
2973
- https://developer.mozilla.org/en-US/docs/HTML/Element/Input
2974
-
2975
- @class html5types
2976
- @extends text
2977
- @final
2978
- @since 1.3.0
2979
- @example
2980
- <a href="#" id="email" data-type="email" data-pk="1">admin@example.com</a>
2981
- <script>
2982
- $(function(){
2983
- $('#email').editable({
2984
- url: '/post',
2985
- title: 'Enter email'
2986
- });
2987
- });
2988
- </script>
2989
- **/
2990
-
2991
- /**
2992
- @property tpl
2993
- @default depends on type
2994
- **/
2995
-
2996
- /*
2997
- Password
2998
- */
2999
- (function ($) {
3000
- var Password = function (options) {
3001
- this.init('password', options, Password.defaults);
3002
- };
3003
- $.fn.editableutils.inherit(Password, $.fn.editabletypes.text);
3004
- $.extend(Password.prototype, {
3005
- //do not display password, show '[hidden]' instead
3006
- value2html: function(value, element) {
3007
- if(value) {
3008
- $(element).text('[hidden]');
3009
- } else {
3010
- $(element).empty();
3011
- }
3012
- },
3013
- //as password not displayed, should not set value by html
3014
- html2value: function(html) {
3015
- return null;
3016
- }
3017
- });
3018
- Password.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
3019
- tpl: '<input type="password">'
3020
- });
3021
- $.fn.editabletypes.password = Password;
3022
- }(window.jQuery));
3023
-
3024
-
3025
- /*
3026
- Email
3027
- */
3028
- (function ($) {
3029
- var Email = function (options) {
3030
- this.init('email', options, Email.defaults);
3031
- };
3032
- $.fn.editableutils.inherit(Email, $.fn.editabletypes.text);
3033
- Email.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
3034
- tpl: '<input type="email">'
3035
- });
3036
- $.fn.editabletypes.email = Email;
3037
- }(window.jQuery));
3038
-
3039
-
3040
- /*
3041
- Url
3042
- */
3043
- (function ($) {
3044
- var Url = function (options) {
3045
- this.init('url', options, Url.defaults);
3046
- };
3047
- $.fn.editableutils.inherit(Url, $.fn.editabletypes.text);
3048
- Url.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
3049
- tpl: '<input type="url">'
3050
- });
3051
- $.fn.editabletypes.url = Url;
3052
- }(window.jQuery));
3053
-
3054
-
3055
- /*
3056
- Tel
3057
- */
3058
- (function ($) {
3059
- var Tel = function (options) {
3060
- this.init('tel', options, Tel.defaults);
3061
- };
3062
- $.fn.editableutils.inherit(Tel, $.fn.editabletypes.text);
3063
- Tel.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
3064
- tpl: '<input type="tel">'
3065
- });
3066
- $.fn.editabletypes.tel = Tel;
3067
- }(window.jQuery));
3068
-
3069
-
3070
- /*
3071
- Number
3072
- */
3073
- (function ($) {
3074
- var NumberInput = function (options) {
3075
- this.init('number', options, NumberInput.defaults);
3076
- };
3077
- $.fn.editableutils.inherit(NumberInput, $.fn.editabletypes.text);
3078
- $.extend(NumberInput.prototype, {
3079
- render: function () {
3080
- NumberInput.superclass.render.call(this);
3081
- this.setAttr('min');
3082
- this.setAttr('max');
3083
- this.setAttr('step');
3084
- }
3085
- });
3086
- NumberInput.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
3087
- tpl: '<input type="number">',
3088
- inputclass: 'input-mini',
3089
- min: null,
3090
- max: null,
3091
- step: null
3092
- });
3093
- $.fn.editabletypes.number = NumberInput;
3094
- }(window.jQuery));
3095
-
3096
-
3097
- /*
3098
- Range (inherit from number)
3099
- */
3100
- (function ($) {
3101
- var Range = function (options) {
3102
- this.init('range', options, Range.defaults);
3103
- };
3104
- $.fn.editableutils.inherit(Range, $.fn.editabletypes.number);
3105
- $.extend(Range.prototype, {
3106
- render: function () {
3107
- this.$input = this.$tpl.filter('input');
3108
-
3109
- this.setClass();
3110
- this.setAttr('min');
3111
- this.setAttr('max');
3112
- this.setAttr('step');
3113
-
3114
- this.$input.on('input', function(){
3115
- $(this).siblings('output').text($(this).val());
3116
- });
3117
- },
3118
- activate: function() {
3119
- this.$input.focus();
3120
- }
3121
- });
3122
- Range.defaults = $.extend({}, $.fn.editabletypes.number.defaults, {
3123
- tpl: '<input type="range"><output style="width: 30px; display: inline-block"></output>',
3124
- inputclass: 'input-medium'
3125
- });
3126
- $.fn.editabletypes.range = Range;
3127
- }(window.jQuery));
3128
- /**
3129
- Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2.
3130
- Please see [original docs](http://ivaynberg.github.com/select2) for detailed description and options.
3131
- You should manually include select2 distributive:
3132
-
3133
- <link href="select2/select2.css" rel="stylesheet" type="text/css"></link>
3134
- <script src="select2/select2.js"></script>
3135
-
3136
- @class select2
3137
- @extends abstractinput
3138
- @since 1.4.1
3139
- @final
3140
- @example
3141
- <a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-original-title="Select country"></a>
3142
- <script>
3143
- $(function(){
3144
- $('#country').editable({
3145
- source: [
3146
- {id: 'gb', text: 'Great Britain'},
3147
- {id: 'us', text: 'United States'},
3148
- {id: 'ru', text: 'Russia'}
3149
- ],
3150
- select2: {
3151
- multiple: true
3152
- }
3153
- });
3154
- });
3155
- </script>
3156
- **/
3157
- (function ($) {
3158
-
3159
- var Constructor = function (options) {
3160
- this.init('select2', options, Constructor.defaults);
3161
-
3162
- options.select2 = options.select2 || {};
3163
-
3164
- var that = this,
3165
- mixin = {
3166
- placeholder: options.placeholder
3167
- };
3168
-
3169
- //detect whether it is multi-valued
3170
- this.isMultiple = options.select2.tags || options.select2.multiple;
3171
-
3172
- //if not `tags` mode, we need define init set data from source
3173
- if(!options.select2.tags) {
3174
- if(options.source) {
3175
- mixin.data = options.source;
3176
- }
3177
-
3178
- //this function can be defaulted in seletc2. See https://github.com/ivaynberg/select2/issues/710
3179
- mixin.initSelection = function (element, callback) {
3180
- var val = that.str2value(element.val()),
3181
- data = $.fn.editableutils.itemsByValue(val, mixin.data, 'id');
3182
-
3183
- //for single-valued mode should not use array. Take first element instead.
3184
- if($.isArray(data) && data.length && !that.isMultiple) {
3185
- data = data[0];
3186
- }
3187
-
3188
- callback(data);
3189
- };
3190
- }
3191
-
3192
- //overriding objects in config (as by default jQuery extend() is not recursive)
3193
- this.options.select2 = $.extend({}, Constructor.defaults.select2, mixin, options.select2);
3194
- };
3195
-
3196
- $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
3197
-
3198
- $.extend(Constructor.prototype, {
3199
- render: function() {
3200
- this.setClass();
3201
- //apply select2
3202
- this.$input.select2(this.options.select2);
3203
-
3204
- //trigger resize of editableform to re-position container in multi-valued mode
3205
- if(this.isMultiple) {
3206
- this.$input.on('change', function() {
3207
- $(this).closest('form').parent().triggerHandler('resize');
3208
- });
3209
- }
3210
-
3211
- },
3212
-
3213
- value2html: function(value, element) {
3214
- var text = '', data;
3215
- if(this.$input) { //when submitting form
3216
- data = this.$input.select2('data');
3217
- } else { //on init (autotext)
3218
- //here select2 instance not created yet and data may be even not loaded.
3219
- //we can check data/tags property of select config and if exist lookup text
3220
- if(this.options.select2.tags) {
3221
- data = value;
3222
- } else if(this.options.select2.data) {
3223
- data = $.fn.editableutils.itemsByValue(value, this.options.select2.data, 'id');
3224
- }
3225
- }
3226
-
3227
- if($.isArray(data)) {
3228
- //collect selected data and show with separator
3229
- text = [];
3230
- $.each(data, function(k, v){
3231
- text.push(v && typeof v === 'object' ? v.text : v);
3232
- });
3233
- } else if(data) {
3234
- text = data.text;
3235
- }
3236
-
3237
- text = $.isArray(text) ? text.join(this.options.viewseparator) : text;
3238
-
3239
- $(element).text(text);
3240
- },
3241
-
3242
- html2value: function(html) {
3243
- return this.options.select2.tags ? this.str2value(html, this.options.viewseparator) : null;
3244
- },
3245
-
3246
- value2input: function(value) {
3247
- this.$input.val(value).trigger('change');
3248
- },
3249
-
3250
- input2value: function() {
3251
- return this.$input.select2('val');
3252
- },
3253
-
3254
- str2value: function(str, separator) {
3255
- if(typeof str !== 'string' || !this.isMultiple) {
3256
- return str;
3257
- }
3258
-
3259
- separator = separator || this.options.select2.separator || $.fn.select2.defaults.separator;
3260
-
3261
- var val, i, l;
3262
-
3263
- if (str === null || str.length < 1) {
3264
- return null;
3265
- }
3266
- val = str.split(separator);
3267
- for (i = 0, l = val.length; i < l; i = i + 1) {
3268
- val[i] = $.trim(val[i]);
3269
- }
3270
-
3271
- return val;
3272
- }
3273
-
3274
- });
3275
-
3276
- Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
3277
- /**
3278
- @property tpl
3279
- @default <input type="hidden">
3280
- **/
3281
- tpl:'<input type="hidden">',
3282
- /**
3283
- Configuration of select2. [Full list of options](http://ivaynberg.github.com/select2).
3284
-
3285
- @property select2
3286
- @type object
3287
- @default null
3288
- **/
3289
- select2: null,
3290
- /**
3291
- Placeholder attribute of select
3292
-
3293
- @property placeholder
3294
- @type string
3295
- @default null
3296
- **/
3297
- placeholder: null,
3298
- /**
3299
- Source data for select. It will be assigned to select2 `data` property and kept here just for convenience.
3300
- Please note, that format is different from simple `select` input: use 'id' instead of 'value'.
3301
- E.g. `[{id: 1, text: "text1"}, {id: 2, text: "text2"}, ...]`.
3302
-
3303
- @property source
3304
- @type array
3305
- @default null
3306
- **/
3307
- source: null,
3308
- /**
3309
- Separator used to display tags.
3310
-
3311
- @property viewseparator
3312
- @type string
3313
- @default ', '
3314
- **/
3315
- viewseparator: ', '
3316
- });
3317
-
3318
- $.fn.editabletypes.select2 = Constructor;
3319
-
3320
- }(window.jQuery));
3321
- /**
3322
- * Combodate - 1.0.1
3323
- * Dropdown date and time picker.
3324
- * Converts text input into dropdowns to pick day, month, year, hour, minute and second.
3325
- * Uses momentjs as datetime library http://momentjs.com.
3326
- * For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang
3327
- *
3328
- * Author: Vitaliy Potapov
3329
- * Project page: http://github.com/vitalets/combodate
3330
- * Copyright (c) 2012 Vitaliy Potapov. Released under MIT License.
3331
- **/
3332
- (function ($) {
3333
-
3334
- var Combodate = function (element, options) {
3335
- this.$element = $(element);
3336
- if(!this.$element.is('input')) {
3337
- $.error('Combodate should be applied to INPUT element');
3338
- return;
3339
- }
3340
- this.options = $.extend({}, $.fn.combodate.defaults, options, this.$element.data());
3341
- this.init();
3342
- };
3343
-
3344
- Combodate.prototype = {
3345
- constructor: Combodate,
3346
- init: function () {
3347
- this.map = {
3348
- //key regexp moment.method
3349
- day: ['D', 'date'],
3350
- month: ['M', 'month'],
3351
- year: ['Y', 'year'],
3352
- hour: ['[Hh]', 'hours'],
3353
- minute: ['m', 'minutes'],
3354
- second: ['s', 'seconds'],
3355
- ampm: ['[Aa]', '']
3356
- };
3357
-
3358
- this.$widget = $('<span class="combodate"></span>').html(this.getTemplate());
3359
-
3360
- this.initCombos();
3361
-
3362
- //update original input on change
3363
- this.$widget.on('change', 'select', $.proxy(function(){
3364
- this.$element.val(this.getValue());
3365
- }, this));
3366
-
3367
- this.$widget.find('select').css('width', 'auto');
3368
-
3369
- //hide original input and insert widget
3370
- this.$element.hide().after(this.$widget);
3371
-
3372
- //set initial value
3373
- this.setValue(this.$element.val() || this.options.value);
3374
- },
3375
-
3376
- /*
3377
- Replace tokens in template with <select> elements
3378
- */
3379
- getTemplate: function() {
3380
- var tpl = this.options.template;
3381
-
3382
- //first pass
3383
- $.each(this.map, function(k, v) {
3384
- v = v[0];
3385
- var r = new RegExp(v+'+'),
3386
- token = v.length > 1 ? v.substring(1, 2) : v;
3387
-
3388
- tpl = tpl.replace(r, '{'+token+'}');
3389
- });
3390
-
3391
- //replace spaces with &nbsp;
3392
- tpl = tpl.replace(/ /g, '&nbsp;');
3393
-
3394
- //second pass
3395
- $.each(this.map, function(k, v) {
3396
- v = v[0];
3397
- var token = v.length > 1 ? v.substring(1, 2) : v;
3398
-
3399
- tpl = tpl.replace('{'+token+'}', '<select class="'+k+'"></select>');
3400
- });
3401
-
3402
- return tpl;
3403
- },
3404
-
3405
- /*
3406
- Initialize combos that presents in template
3407
- */
3408
- initCombos: function() {
3409
- var that = this;
3410
- $.each(this.map, function(k, v) {
3411
- var $c = that.$widget.find('.'+k), f, items;
3412
- if($c.length) {
3413
- that['$'+k] = $c; //set properties like this.$day, this.$month etc.
3414
- f = 'fill' + k.charAt(0).toUpperCase() + k.slice(1); //define method name to fill items, e.g `fillDays`
3415
- items = that[f]();
3416
- that['$'+k].html(that.renderItems(items));
3417
- }
3418
- });
3419
- },
3420
-
3421
- /*
3422
- Initialize items of combos. Handles `firstItem` option
3423
- */
3424
- initItems: function(key) {
3425
- var values = [];
3426
- if(this.options.firstItem === 'name') {
3427
- var header = typeof moment.relativeTime[key] === 'function' ? moment.relativeTime[key](1, true, key, false) : moment.relativeTime[key];
3428
- //take last entry (see momentjs lang files structure)
3429
- header = header.split(' ').reverse()[0];
3430
- values.push(['', header]);
3431
- } else if(this.options.firstItem === 'empty') {
3432
- values.push(['', '']);
3433
- }
3434
- return values;
3435
- },
3436
-
3437
- /*
3438
- render items to string of <option> tags
3439
- */
3440
- renderItems: function(items) {
3441
- var str = [];
3442
- for(var i=0; i<items.length; i++) {
3443
- str.push('<option value="'+items[i][0]+'">'+items[i][1]+'</option>');
3444
- }
3445
- return str.join("\n");
3446
- },
3447
-
3448
- /*
3449
- fill day
3450
- */
3451
- fillDay: function() {
3452
- var items = this.initItems('d'), name, i,
3453
- twoDigit = this.options.template.indexOf('DD') !== -1;
3454
-
3455
- for(i=1; i<=31; i++) {
3456
- name = twoDigit ? this.leadZero(i) : i;
3457
- items.push([i, name]);
3458
- }
3459
- return items;
3460
- },
3461
-
3462
- /*
3463
- fill month
3464
- */
3465
- fillMonth: function() {
3466
- var items = this.initItems('M'), name, i,
3467
- longNames = this.options.template.indexOf('MMMM') !== -1,
3468
- shortNames = this.options.template.indexOf('MMM') !== -1,
3469
- twoDigit = this.options.template.indexOf('MM') !== -1;
3470
-
3471
- for(i=0; i<=11; i++) {
3472
- if(longNames) {
3473
- name = moment.months[i];
3474
- } else if(shortNames) {
3475
- name = moment.monthsShort[i];
3476
- } else if(twoDigit) {
3477
- name = this.leadZero(i+1);
3478
- } else {
3479
- name = i+1;
3480
- }
3481
- items.push([i, name]);
3482
- }
3483
- return items;
3484
- },
3485
-
3486
- /*
3487
- fill year
3488
- */
3489
- fillYear: function() {
3490
- var items = this.initItems('y'), name, i,
3491
- longNames = this.options.template.indexOf('YYYY') !== -1;
3492
-
3493
- for(i=this.options.maxYear; i>=this.options.minYear; i--) {
3494
- name = longNames ? i : (i+'').substring(2);
3495
- items.push([i, name]);
3496
- }
3497
- return items;
3498
- },
3499
-
3500
- /*
3501
- fill hour
3502
- */
3503
- fillHour: function() {
3504
- var items = this.initItems('h'), name, i,
3505
- h12 = this.options.template.indexOf('h') !== -1,
3506
- h24 = this.options.template.indexOf('H') !== -1,
3507
- twoDigit = this.options.template.toLowerCase().indexOf('hh') !== -1,
3508
- max = h12 ? 12 : 23;
3509
-
3510
- for(i=0; i<=max; i++) {
3511
- name = twoDigit ? this.leadZero(i) : i;
3512
- items.push([i, name]);
3513
- }
3514
- return items;
3515
- },
3516
-
3517
- /*
3518
- fill minute
3519
- */
3520
- fillMinute: function() {
3521
- var items = this.initItems('m'), name, i,
3522
- twoDigit = this.options.template.indexOf('mm') !== -1;
3523
-
3524
- for(i=0; i<=59; i+= this.options.minuteStep) {
3525
- name = twoDigit ? this.leadZero(i) : i;
3526
- items.push([i, name]);
3527
- }
3528
- return items;
3529
- },
3530
-
3531
- /*
3532
- fill second
3533
- */
3534
- fillSecond: function() {
3535
- var items = this.initItems('s'), name, i,
3536
- twoDigit = this.options.template.indexOf('ss') !== -1;
3537
-
3538
- for(i=0; i<=59; i+= this.options.secondStep) {
3539
- name = twoDigit ? this.leadZero(i) : i;
3540
- items.push([i, name]);
3541
- }
3542
- return items;
3543
- },
3544
-
3545
- /*
3546
- fill ampm
3547
- */
3548
- fillAmpm: function() {
3549
- var ampmL = this.options.template.indexOf('a') !== -1,
3550
- ampmU = this.options.template.indexOf('A') !== -1,
3551
- items = [
3552
- ['am', ampmL ? 'am' : 'AM'],
3553
- ['pm', ampmL ? 'pm' : 'PM']
3554
- ];
3555
- return items;
3556
- },
3557
-
3558
- /*
3559
- Returns current date value.
3560
- If format not specified - `options.format` used.
3561
- If format = `null` - Moment object returned.
3562
- */
3563
- getValue: function(format) {
3564
- var dt, values = {},
3565
- that = this,
3566
- notSelected = false;
3567
-
3568
- //getting selected values
3569
- $.each(this.map, function(k, v) {
3570
- if(k === 'ampm') {
3571
- return;
3572
- }
3573
- var def = k === 'day' ? 1 : 0;
3574
-
3575
- values[k] = that['$'+k] ? parseInt(that['$'+k].val(), 10) : def;
3576
-
3577
- if(isNaN(values[k])) {
3578
- notSelected = true;
3579
- return false;
3580
- }
3581
- });
3582
-
3583
- //if at least one visible combo not selected - return empty string
3584
- if(notSelected) {
3585
- return '';
3586
- }
3587
-
3588
- //convert hours if 12h format
3589
- if(this.$ampm) {
3590
- values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12;
3591
- if(values.hour === 24) {
3592
- values.hour = 0;
3593
- }
3594
- }
3595
-
3596
- dt = moment([values.year, values.month, values.day, values.hour, values.minute, values.second]);
3597
-
3598
- //highlight invalid date
3599
- this.highlight(dt);
3600
-
3601
- format = format === undefined ? this.options.format : format;
3602
- if(format === null) {
3603
- return dt.isValid() ? dt : null;
3604
- } else {
3605
- return dt.isValid() ? dt.format(format) : '';
3606
- }
3607
- },
3608
-
3609
- setValue: function(value) {
3610
- if(!value) {
3611
- return;
3612
- }
3613
-
3614
- var dt = typeof value === 'string' ? moment(value, this.options.format) : moment(value),
3615
- that = this,
3616
- values = {};
3617
-
3618
- if(dt.isValid()) {
3619
- //read values from date object
3620
- $.each(this.map, function(k, v) {
3621
- if(k === 'ampm') {
3622
- return;
3623
- }
3624
- values[k] = dt[v[1]]();
3625
- });
3626
-
3627
- if(this.$ampm) {
3628
- if(values.hour > 12) {
3629
- values.hour -= 12;
3630
- values.ampm = 'pm';
3631
- } else {
3632
- values.ampm = 'am';
3633
- }
3634
- }
3635
-
3636
- $.each(values, function(k, v) {
3637
- if(that['$'+k]) {
3638
- that['$'+k].val(v);
3639
- }
3640
- });
3641
-
3642
- this.$element.val(dt.format(this.options.format));
3643
- }
3644
- },
3645
-
3646
- /*
3647
- highlight combos if date is invalid
3648
- */
3649
- highlight: function(dt) {
3650
- if(!dt.isValid()) {
3651
- if(this.options.errorClass) {
3652
- this.$widget.addClass(this.options.errorClass);
3653
- } else {
3654
- //store original border color
3655
- if(!this.borderColor) {
3656
- this.borderColor = this.$widget.find('select').css('border-color');
3657
- }
3658
- this.$widget.find('select').css('border-color', 'red');
3659
- }
3660
- } else {
3661
- if(this.options.errorClass) {
3662
- this.$widget.removeClass(this.options.errorClass);
3663
- } else {
3664
- this.$widget.find('select').css('border-color', this.borderColor);
3665
- }
3666
- }
3667
- },
3668
-
3669
- leadZero: function(v) {
3670
- return v <= 9 ? '0' + v : v;
3671
- },
3672
-
3673
- destroy: function() {
3674
- this.$widget.remove();
3675
- this.$element.removeData('combodate').show();
3676
- }
3677
-
3678
- //todo: clear method
3679
- };
3680
-
3681
- $.fn.combodate = function ( option ) {
3682
- var d, args = Array.apply(null, arguments);
3683
- args.shift();
3684
-
3685
- //getValue returns date as string / object (not jQuery object)
3686
- if(option === 'getValue' && this.length && (d = this.eq(0).data('combodate'))) {
3687
- return d.getValue.apply(d, args);
3688
- }
3689
-
3690
- return this.each(function () {
3691
- var $this = $(this),
3692
- data = $this.data('combodate'),
3693
- options = typeof option == 'object' && option;
3694
- if (!data) {
3695
- $this.data('combodate', (data = new Combodate(this, options)));
3696
- }
3697
- if (typeof option == 'string' && typeof data[option] == 'function') {
3698
- data[option].apply(data, args);
3699
- }
3700
- });
3701
- };
3702
-
3703
- $.fn.combodate.defaults = {
3704
- //in this format value stored in original input
3705
- format: 'DD-MM-YYYY HH:mm',
3706
- //in this format items in dropdowns are displayed
3707
- template: 'D / MMM / YYYY H : mm',
3708
- //initial value, can be `new Date()`
3709
- value: null,
3710
- minYear: 1970,
3711
- maxYear: 2015,
3712
- minuteStep: 5,
3713
- secondStep: 1,
3714
- firstItem: 'empty', //'name', 'empty', 'none'
3715
- errorClass: null
3716
- };
3717
-
3718
- }(window.jQuery));
3719
- /**
3720
- Combodate input - dropdown date and time picker.
3721
- Based on [combodate](http://vitalets.github.com/combodate) plugin. To use it you should manually include [momentjs](http://momentjs.com).
3722
-
3723
- <script src="js/moment.min.js"></script>
3724
-
3725
- Allows to input:
3726
-
3727
- * only date
3728
- * only time
3729
- * both date and time
3730
-
3731
- Please note, that format is taken from momentjs and **not compatible** with bootstrap-datepicker / jquery UI datepicker.
3732
- Internally value stored as `momentjs` object.
3733
-
3734
- @class combodate
3735
- @extends abstractinput
3736
- @final
3737
- @since 1.4.0
3738
- @example
3739
- <a href="#" id="dob" data-type="combodate" data-pk="1" data-url="/post" data-value="1984-05-15" data-original-title="Select date"></a>
3740
- <script>
3741
- $(function(){
3742
- $('#dob').editable({
3743
- format: 'YYYY-MM-DD',
3744
- viewformat: 'DD.MM.YYYY',
3745
- template: 'D / MMMM / YYYY',
3746
- combodate: {
3747
- minYear: 2000,
3748
- maxYear: 2015,
3749
- minuteStep: 1
3750
- }
3751
- }
3752
- });
3753
- });
3754
- </script>
3755
- **/
3756
-
3757
- /*global moment*/
3758
-
3759
- (function ($) {
3760
-
3761
- var Constructor = function (options) {
3762
- this.init('combodate', options, Constructor.defaults);
3763
-
3764
- //by default viewformat equals to format
3765
- if(!this.options.viewformat) {
3766
- this.options.viewformat = this.options.format;
3767
- }
3768
-
3769
- //overriding combodate config (as by default jQuery extend() is not recursive)
3770
- this.options.combodate = $.extend({}, Constructor.defaults.combodate, options.combodate, {
3771
- format: this.options.format,
3772
- template: this.options.template
3773
- });
3774
- };
3775
-
3776
- $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
3777
-
3778
- $.extend(Constructor.prototype, {
3779
- render: function () {
3780
- this.$input.combodate(this.options.combodate);
3781
-
3782
- //"clear" link
3783
- /*
3784
- if(this.options.clear) {
3785
- this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
3786
- e.preventDefault();
3787
- e.stopPropagation();
3788
- this.clear();
3789
- }, this));
3790
-
3791
- this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));
3792
- }
3793
- */
3794
- },
3795
-
3796
- value2html: function(value, element) {
3797
- var text = value ? value.format(this.options.viewformat) : '';
3798
- $(element).text(text);
3799
- },
3800
-
3801
- html2value: function(html) {
3802
- return html ? moment(html, this.options.viewformat) : null;
3803
- },
3804
-
3805
- value2str: function(value) {
3806
- return value ? value.format(this.options.format) : '';
3807
- },
3808
-
3809
- str2value: function(str) {
3810
- return str ? moment(str, this.options.format) : null;
3811
- },
3812
-
3813
- value2submit: function(value) {
3814
- return this.value2str(value);
3815
- },
3816
-
3817
- value2input: function(value) {
3818
- this.$input.combodate('setValue', value);
3819
- },
3820
-
3821
- input2value: function() {
3822
- return this.$input.combodate('getValue', null);
3823
- },
3824
-
3825
- activate: function() {
3826
- this.$input.siblings('.combodate').find('select').eq(0).focus();
3827
- },
3828
-
3829
- /*
3830
- clear: function() {
3831
- this.$input.data('datepicker').date = null;
3832
- this.$input.find('.active').removeClass('active');
3833
- },
3834
- */
3835
-
3836
- autosubmit: function() {
3837
-
3838
- }
3839
-
3840
- });
3841
-
3842
- Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
3843
- /**
3844
- @property tpl
3845
- @default <input type="text">
3846
- **/
3847
- tpl:'<input type="text">',
3848
- /**
3849
- @property inputclass
3850
- @default null
3851
- **/
3852
- inputclass: null,
3853
- /**
3854
- Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
3855
- See list of tokens in [momentjs docs](http://momentjs.com/docs/#/parsing/string-format)
3856
-
3857
- @property format
3858
- @type string
3859
- @default YYYY-MM-DD
3860
- **/
3861
- format:'YYYY-MM-DD',
3862
- /**
3863
- Format used for displaying date. Also applied when converting date from element's text on init.
3864
- If not specified equals to `format`.
3865
-
3866
- @property viewformat
3867
- @type string
3868
- @default null
3869
- **/
3870
- viewformat: null,
3871
- /**
3872
- Template used for displaying dropdowns.
3873
-
3874
- @property template
3875
- @type string
3876
- @default D / MMM / YYYY
3877
- **/
3878
- template: 'D / MMM / YYYY',
3879
- /**
3880
- Configuration of combodate.
3881
- Full list of options: http://vitalets.github.com/combodate/#docs
3882
-
3883
- @property combodate
3884
- @type object
3885
- @default null
3886
- **/
3887
- combodate: null
3888
-
3889
- /*
3890
- (not implemented yet)
3891
- Text shown as clear date button.
3892
- If <code>false</code> clear button will not be rendered.
3893
-
3894
- @property clear
3895
- @type boolean|string
3896
- @default 'x clear'
3897
- */
3898
- //clear: '&times; clear'
3899
- });
3900
-
3901
- $.fn.editabletypes.combodate = Constructor;
3902
-
3903
- }(window.jQuery));
3904
-
3905
- /*
3906
- Editableform based on Twitter Bootstrap
3907
- */
3908
- (function ($) {
3909
-
3910
- $.extend($.fn.editableform.Constructor.prototype, {
3911
- initTemplate: function() {
3912
- this.$form = $($.fn.editableform.template);
3913
- this.$form.find('.editable-error-block').addClass('help-block');
3914
- }
3915
- });
3916
-
3917
- //buttons
3918
- $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary editable-submit"><i class="icon-ok icon-white"></i></button>'+
3919
- '<button type="button" class="btn editable-cancel"><i class="icon-remove"></i></button>';
3920
-
3921
- //error classes
3922
- $.fn.editableform.errorGroupClass = 'error';
3923
- $.fn.editableform.errorBlockClass = null;
3924
-
3925
- }(window.jQuery));
3926
- /**
3927
- * Editable Popover
3928
- * ---------------------
3929
- * requires bootstrap-popover.js
3930
- */
3931
- (function ($) {
3932
-
3933
- //extend methods
3934
- $.extend($.fn.editableContainer.Popup.prototype, {
3935
- containerName: 'popover',
3936
- //for compatibility with bootstrap <= 2.2.1 (content inserted into <p> instead of directly .popover-content)
3937
- innerCss: $($.fn.popover.defaults.template).find('p').length ? '.popover-content p' : '.popover-content',
3938
-
3939
- initContainer: function(){
3940
- $.extend(this.containerOptions, {
3941
- trigger: 'manual',
3942
- selector: false,
3943
- content: ' ',
3944
- template: $.fn.popover.defaults.template
3945
- });
3946
-
3947
- //as template property is used in inputs, hide it from popover
3948
- var t;
3949
- if(this.$element.data('template')) {
3950
- t = this.$element.data('template');
3951
- this.$element.removeData('template');
3952
- }
3953
-
3954
- this.call(this.containerOptions);
3955
-
3956
- if(t) {
3957
- //restore data('template')
3958
- this.$element.data('template', t);
3959
- }
3960
- },
3961
-
3962
- /* show */
3963
- innerShow: function () {
3964
- this.call('show');
3965
- },
3966
-
3967
- /* hide */
3968
- innerHide: function () {
3969
- this.call('hide');
3970
- },
3971
-
3972
- /* destroy */
3973
- innerDestroy: function() {
3974
- this.call('destroy');
3975
- },
3976
-
3977
- setContainerOption: function(key, value) {
3978
- this.container().options[key] = value;
3979
- },
3980
-
3981
- /**
3982
- * move popover to new position. This function mainly copied from bootstrap-popover.
3983
- */
3984
- /*jshint laxcomma: true*/
3985
- setPosition: function () {
3986
-
3987
- (function() {
3988
- var $tip = this.tip()
3989
- , inside
3990
- , pos
3991
- , actualWidth
3992
- , actualHeight
3993
- , placement
3994
- , tp;
3995
-
3996
- placement = typeof this.options.placement === 'function' ?
3997
- this.options.placement.call(this, $tip[0], this.$element[0]) :
3998
- this.options.placement;
3999
-
4000
- inside = /in/.test(placement);
4001
-
4002
- $tip
4003
- // .detach()
4004
- //vitalets: remove any placement class because otherwise they dont influence on re-positioning of visible popover
4005
- .removeClass('top right bottom left')
4006
- .css({ top: 0, left: 0, display: 'block' });
4007
- // .insertAfter(this.$element);
4008
-
4009
- pos = this.getPosition(inside);
4010
-
4011
- actualWidth = $tip[0].offsetWidth;
4012
- actualHeight = $tip[0].offsetHeight;
4013
-
4014
- switch (inside ? placement.split(' ')[1] : placement) {
4015
- case 'bottom':
4016
- tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2};
4017
- break;
4018
- case 'top':
4019
- tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2};
4020
- break;
4021
- case 'left':
4022
- tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth};
4023
- break;
4024
- case 'right':
4025
- tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width};
4026
- break;
4027
- }
4028
-
4029
- $tip
4030
- .offset(tp)
4031
- .addClass(placement)
4032
- .addClass('in');
4033
-
4034
- }).call(this.container());
4035
- /*jshint laxcomma: false*/
4036
- }
4037
- });
4038
-
4039
- }(window.jQuery));
4040
- /**
4041
- Bootstrap-datepicker.
4042
- Description and examples: https://github.com/eternicode/bootstrap-datepicker.
4043
- For **i18n** you should include js file from here: https://github.com/eternicode/bootstrap-datepicker/tree/master/js/locales
4044
- and set `language` option.
4045
- Since 1.4.0 date has different appearance in **popup** and **inline** modes.
4046
-
4047
- @class date
4048
- @extends abstractinput
4049
- @final
4050
- @example
4051
- <a href="#" id="dob" data-type="date" data-pk="1" data-url="/post" data-original-title="Select date">15/05/1984</a>
4052
- <script>
4053
- $(function(){
4054
- $('#dob').editable({
4055
- format: 'yyyy-mm-dd',
4056
- viewformat: 'dd/mm/yyyy',
4057
- datepicker: {
4058
- weekStart: 1
4059
- }
4060
- }
4061
- });
4062
- });
4063
- </script>
4064
- **/
4065
- (function ($) {
4066
-
4067
- var Date = function (options) {
4068
- this.init('date', options, Date.defaults);
4069
- this.initPicker(options, Date.defaults);
4070
- };
4071
-
4072
- $.fn.editableutils.inherit(Date, $.fn.editabletypes.abstractinput);
4073
-
4074
- $.extend(Date.prototype, {
4075
- initPicker: function(options, defaults) {
4076
- //'format' is set directly from settings or data-* attributes
4077
-
4078
- //by default viewformat equals to format
4079
- if(!this.options.viewformat) {
4080
- this.options.viewformat = this.options.format;
4081
- }
4082
-
4083
- //overriding datepicker config (as by default jQuery extend() is not recursive)
4084
- //since 1.4 datepicker internally uses viewformat instead of format. Format is for submit only
4085
- this.options.datepicker = $.extend({}, defaults.datepicker, options.datepicker, {
4086
- format: this.options.viewformat
4087
- });
4088
-
4089
- //language
4090
- this.options.datepicker.language = this.options.datepicker.language || 'en';
4091
-
4092
- //store DPglobal
4093
- this.dpg = $.fn.datepicker.DPGlobal;
4094
-
4095
- //store parsed formats
4096
- this.parsedFormat = this.dpg.parseFormat(this.options.format);
4097
- this.parsedViewFormat = this.dpg.parseFormat(this.options.viewformat);
4098
- },
4099
-
4100
- render: function () {
4101
- this.$input.datepicker(this.options.datepicker);
4102
-
4103
- //"clear" link
4104
- if(this.options.clear) {
4105
- this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
4106
- e.preventDefault();
4107
- e.stopPropagation();
4108
- this.clear();
4109
- }, this));
4110
-
4111
- this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));
4112
- }
4113
- },
4114
-
4115
- value2html: function(value, element) {
4116
- var text = value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '';
4117
- Date.superclass.value2html(text, element);
4118
- },
4119
-
4120
- html2value: function(html) {
4121
- return html ? this.dpg.parseDate(html, this.parsedViewFormat, this.options.datepicker.language) : null;
4122
- },
4123
-
4124
- value2str: function(value) {
4125
- return value ? this.dpg.formatDate(value, this.parsedFormat, this.options.datepicker.language) : '';
4126
- },
4127
-
4128
- str2value: function(str) {
4129
- return str ? this.dpg.parseDate(str, this.parsedFormat, this.options.datepicker.language) : null;
4130
- },
4131
-
4132
- value2submit: function(value) {
4133
- return this.value2str(value);
4134
- },
4135
-
4136
- value2input: function(value) {
4137
- this.$input.datepicker('update', value);
4138
- },
4139
-
4140
- input2value: function() {
4141
- return this.$input.data('datepicker').date;
4142
- },
4143
-
4144
- activate: function() {
4145
- },
4146
-
4147
- clear: function() {
4148
- this.$input.data('datepicker').date = null;
4149
- this.$input.find('.active').removeClass('active');
4150
- },
4151
-
4152
- autosubmit: function() {
4153
- this.$input.on('changeDate', function(e){
4154
- var $form = $(this).closest('form');
4155
- setTimeout(function() {
4156
- $form.submit();
4157
- }, 200);
4158
- });
4159
- }
4160
-
4161
- });
4162
-
4163
- Date.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
4164
- /**
4165
- @property tpl
4166
- @default <div></div>
4167
- **/
4168
- tpl:'<div class="editable-date well"></div>',
4169
- /**
4170
- @property inputclass
4171
- @default null
4172
- **/
4173
- inputclass: null,
4174
- /**
4175
- Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
4176
- Possible tokens are: <code>d, dd, m, mm, yy, yyyy</code>
4177
-
4178
- @property format
4179
- @type string
4180
- @default yyyy-mm-dd
4181
- **/
4182
- format:'yyyy-mm-dd',
4183
- /**
4184
- Format used for displaying date. Also applied when converting date from element's text on init.
4185
- If not specified equals to <code>format</code>
4186
-
4187
- @property viewformat
4188
- @type string
4189
- @default null
4190
- **/
4191
- viewformat: null,
4192
- /**
4193
- Configuration of datepicker.
4194
- Full list of options: http://vitalets.github.com/bootstrap-datepicker
4195
-
4196
- @property datepicker
4197
- @type object
4198
- @default {
4199
- weekStart: 0,
4200
- startView: 0,
4201
- autoclose: false
4202
- }
4203
- **/
4204
- datepicker:{
4205
- weekStart: 0,
4206
- startView: 0,
4207
- autoclose: false
4208
- },
4209
- /**
4210
- Text shown as clear date button.
4211
- If <code>false</code> clear button will not be rendered.
4212
-
4213
- @property clear
4214
- @type boolean|string
4215
- @default 'x clear'
4216
- **/
4217
- clear: '&times; clear'
4218
- });
4219
-
4220
- $.fn.editabletypes.date = Date;
4221
-
4222
- }(window.jQuery));
4223
-
4224
- /**
4225
- Bootstrap datefield input - modification for inline mode.
4226
- Shows normal <input type="text"> and binds popup datepicker.
4227
- Automatically shown in inline mode.
4228
-
4229
- @class datefield
4230
- @extends date
4231
-
4232
- @since 1.4.0
4233
- **/
4234
- (function ($) {
4235
-
4236
- var DateField = function (options) {
4237
- this.init('datefield', options, DateField.defaults);
4238
- this.initPicker(options, DateField.defaults);
4239
- };
4240
-
4241
- $.fn.editableutils.inherit(DateField, $.fn.editabletypes.date);
4242
-
4243
- $.extend(DateField.prototype, {
4244
- render: function () {
4245
- this.$input = this.$tpl.find('input');
4246
- this.setClass();
4247
- this.setAttr('placeholder');
4248
-
4249
- this.$tpl.datepicker(this.options.datepicker);
4250
-
4251
- //need to disable original event handlers
4252
- this.$input.off('focus keydown');
4253
-
4254
- //update value of datepicker
4255
- this.$input.keyup($.proxy(function(){
4256
- this.$tpl.removeData('date');
4257
- this.$tpl.datepicker('update');
4258
- }, this));
4259
-
4260
- },
4261
-
4262
- value2input: function(value) {
4263
- this.$input.val(value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '');
4264
- this.$tpl.datepicker('update');
4265
- },
4266
-
4267
- input2value: function() {
4268
- return this.html2value(this.$input.val());
4269
- },
4270
-
4271
- activate: function() {
4272
- $.fn.editabletypes.text.prototype.activate.call(this);
4273
- },
4274
-
4275
- autosubmit: function() {
4276
- //reset autosubmit to empty
4277
- }
4278
- });
4279
-
4280
- DateField.defaults = $.extend({}, $.fn.editabletypes.date.defaults, {
4281
- /**
4282
- @property tpl
4283
- **/
4284
- tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',
4285
- /**
4286
- @property inputclass
4287
- @default 'input-small'
4288
- **/
4289
- inputclass: 'input-small',
4290
-
4291
- /* datepicker config */
4292
- datepicker: {
4293
- weekStart: 0,
4294
- startView: 0,
4295
- autoclose: true
4296
- }
4297
- });
4298
-
4299
- $.fn.editabletypes.datefield = DateField;
4300
-
4301
- }(window.jQuery));
4302
-
4303
- /**
4304
- Typeahead input (bootstrap only). Based on Twitter Bootstrap [typeahead](http://twitter.github.com/bootstrap/javascript.html#typeahead).
4305
- Depending on `source` format typeahead operates in two modes:
4306
-
4307
- * **strings**:
4308
- When `source` defined as array of strings, e.g. `['text1', 'text2', 'text3' ...]`.
4309
- User can submit one of these strings or any text entered in input (even if it is not matching source).
4310
-
4311
- * **objects**:
4312
- When `source` defined as array of objects, e.g. `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`.
4313
- User can submit only values that are in source (otherwise `null` is submitted). This is more like *dropdown* behavior.
4314
-
4315
- @class typeahead
4316
- @extends list
4317
- @since 1.4.1
4318
- @final
4319
- @example
4320
- <a href="#" id="country" data-type="typeahead" data-pk="1" data-url="/post" data-original-title="Input country"></a>
4321
- <script>
4322
- $(function(){
4323
- $('#country').editable({
4324
- value: 'ru',
4325
- source: [
4326
- {value: 'gb', text: 'Great Britain'},
4327
- {value: 'us', text: 'United States'},
4328
- {value: 'ru', text: 'Russia'}
4329
- ]
4330
- }
4331
- });
4332
- });
4333
- </script>
4334
- **/
4335
- (function ($) {
4336
-
4337
- var Constructor = function (options) {
4338
- this.init('typeahead', options, Constructor.defaults);
4339
-
4340
- //overriding objects in config (as by default jQuery extend() is not recursive)
4341
- this.options.typeahead = $.extend({}, Constructor.defaults.typeahead, {
4342
- //set default methods for typeahead to work with objects
4343
- matcher: this.matcher,
4344
- sorter: this.sorter,
4345
- highlighter: this.highlighter,
4346
- updater: this.updater
4347
- }, options.typeahead);
4348
- };
4349
-
4350
- $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.list);
4351
-
4352
- $.extend(Constructor.prototype, {
4353
- renderList: function() {
4354
- this.$input = this.$tpl.is('input') ? this.$tpl : this.$tpl.find('input[type="text"]');
4355
-
4356
- //set source of typeahead
4357
- this.options.typeahead.source = this.sourceData;
4358
-
4359
- //apply typeahead
4360
- this.$input.typeahead(this.options.typeahead);
4361
-
4362
- //attach own render method
4363
- this.$input.data('typeahead').render = $.proxy(this.typeaheadRender, this.$input.data('typeahead'));
4364
-
4365
- this.renderClear();
4366
- this.setClass();
4367
- this.setAttr('placeholder');
4368
- },
4369
-
4370
- value2htmlFinal: function(value, element) {
4371
- if(this.getIsObjects()) {
4372
- var items = $.fn.editableutils.itemsByValue(value, this.sourceData);
4373
- $(element).text(items.length ? items[0].text : '');
4374
- } else {
4375
- $(element).text(value);
4376
- }
4377
- },
4378
-
4379
- html2value: function (html) {
4380
- return html ? html : null;
4381
- },
4382
-
4383
- value2input: function(value) {
4384
- if(this.getIsObjects()) {
4385
- var items = $.fn.editableutils.itemsByValue(value, this.sourceData);
4386
- this.$input.data('value', value).val(items.length ? items[0].text : '');
4387
- } else {
4388
- this.$input.val(value);
4389
- }
4390
- },
4391
-
4392
- input2value: function() {
4393
- if(this.getIsObjects()) {
4394
- var value = this.$input.data('value'),
4395
- items = $.fn.editableutils.itemsByValue(value, this.sourceData);
4396
-
4397
- if(items.length && items[0].text.toLowerCase() === this.$input.val().toLowerCase()) {
4398
- return value;
4399
- } else {
4400
- return null; //entered string not found in source
4401
- }
4402
- } else {
4403
- return this.$input.val();
4404
- }
4405
- },
4406
-
4407
- /*
4408
- if in sourceData values <> texts, typeahead in "objects" mode:
4409
- user must pick some value from list, otherwise `null` returned.
4410
- if all values == texts put typeahead in "strings" mode:
4411
- anything what entered is submited.
4412
- */
4413
- getIsObjects: function() {
4414
- if(this.isObjects === undefined) {
4415
- this.isObjects = false;
4416
- for(var i=0; i<this.sourceData.length; i++) {
4417
- if(this.sourceData[i].value !== this.sourceData[i].text) {
4418
- this.isObjects = true;
4419
- break;
4420
- }
4421
- }
4422
- }
4423
- return this.isObjects;
4424
- },
4425
-
4426
- /*
4427
- Methods borrowed from text input
4428
- */
4429
- activate: $.fn.editabletypes.text.prototype.activate,
4430
- renderClear: $.fn.editabletypes.text.prototype.renderClear,
4431
- postrender: $.fn.editabletypes.text.prototype.postrender,
4432
- toggleClear: $.fn.editabletypes.text.prototype.toggleClear,
4433
- clear: function() {
4434
- $.fn.editabletypes.text.prototype.clear.call(this);
4435
- this.$input.data('value', '');
4436
- },
4437
-
4438
-
4439
- /*
4440
- Typeahead option methods used as defaults
4441
- */
4442
- /*jshint eqeqeq:false, curly: false, laxcomma: true*/
4443
- matcher: function (item) {
4444
- return $.fn.typeahead.Constructor.prototype.matcher.call(this, item.text);
4445
- },
4446
- sorter: function (items) {
4447
- var beginswith = []
4448
- , caseSensitive = []
4449
- , caseInsensitive = []
4450
- , item
4451
- , text;
4452
-
4453
- while (item = items.shift()) {
4454
- text = item.text;
4455
- if (!text.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item);
4456
- else if (~text.indexOf(this.query)) caseSensitive.push(item);
4457
- else caseInsensitive.push(item);
4458
- }
4459
-
4460
- return beginswith.concat(caseSensitive, caseInsensitive);
4461
- },
4462
- highlighter: function (item) {
4463
- return $.fn.typeahead.Constructor.prototype.highlighter.call(this, item.text);
4464
- },
4465
- updater: function (item) {
4466
- item = this.$menu.find('.active').data('item');
4467
- this.$element.data('value', item.value);
4468
- return item.text;
4469
- },
4470
-
4471
-
4472
- /*
4473
- Overwrite typeahead's render method to store objects.
4474
- There are a lot of disscussion in bootstrap repo on this point and still no result.
4475
- See https://github.com/twitter/bootstrap/issues/5967
4476
-
4477
- This function just store item in via jQuery data() method instead of attr('data-value')
4478
- */
4479
- typeaheadRender: function (items) {
4480
- var that = this;
4481
-
4482
- items = $(items).map(function (i, item) {
4483
- // i = $(that.options.item).attr('data-value', item)
4484
- i = $(that.options.item).data('item', item);
4485
- i.find('a').html(that.highlighter(item));
4486
- return i[0];
4487
- });
4488
-
4489
- items.first().addClass('active');
4490
- this.$menu.html(items);
4491
- return this;
4492
- }
4493
- /*jshint eqeqeq: true, curly: true, laxcomma: false*/
4494
-
4495
- });
4496
-
4497
- Constructor.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
4498
- /**
4499
- @property tpl
4500
- @default <input type="text">
4501
- **/
4502
- tpl:'<input type="text">',
4503
- /**
4504
- Configuration of typeahead. [Full list of options](http://twitter.github.com/bootstrap/javascript.html#typeahead).
4505
-
4506
- @property typeahead
4507
- @type object
4508
- @default null
4509
- **/
4510
- typeahead: null,
4511
- /**
4512
- Whether to show `clear` button
4513
-
4514
- @property clear
4515
- @type boolean
4516
- @default true
4517
- **/
4518
- clear: true
4519
- });
4520
-
4521
- $.fn.editabletypes.typeahead = Constructor;
4522
-
4523
- }(window.jQuery));