PageRenderTime 77ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/beancounter/static/js/tags.js

https://bitbucket.org/xamox/beancounter
JavaScript | 231 lines | 175 code | 33 blank | 23 comment | 8 complexity | f83466d427fc566406a84bf935138210 MD5 | raw file
Possible License(s): 0BSD
  1. // vim: fdm=marker
  2. /*
  3. * Copyright (c) 2011-2012 Audrius KaĹžukauskas
  4. *
  5. * This file is part of BeanCounter and is released under
  6. * the ISC license, see LICENSE for more details.
  7. */
  8. /*jslint indent: 4, browser: true, nomen: true, sloppy: true */
  9. /*global $, _, Backbone, B, DATA */
  10. B.Tags = {};
  11. // Tag resource URL.
  12. B.Tags.URL = '/api/tags';
  13. B.Tags.Model = Backbone.Model.extend({ // {{{
  14. defaults: {
  15. checked: false
  16. },
  17. validate: function (attrs) {
  18. if (attrs.hasOwnProperty('name')) {
  19. var name = $.trim(attrs.name);
  20. if (!name) {
  21. return 'Tag name is required';
  22. }
  23. }
  24. },
  25. toggleChecked: function () {
  26. this.set({checked: !this.get('checked')});
  27. }
  28. }); // }}}
  29. B.Tags.Collection = B.Collection.extend({ // {{{
  30. model: B.Tags.Model,
  31. url: B.Tags.URL,
  32. parse: function (resp) {
  33. return resp.tags;
  34. }
  35. }); // }}}
  36. B.Tags.Views = {};
  37. B.Tags.Views.Row = B.Views.Row.extend({ // {{{
  38. // Cache template function.
  39. template: _.template($('#tag-row-template').html()),
  40. formFactory: function () {
  41. return B.Tags.Views.EditForm;
  42. }
  43. }); // }}}
  44. // Base view for tag forms.
  45. B.Tags.Views.Form = B.Views.Form.extend({ // {{{
  46. // Cache template function.
  47. template: _.template($('#tag-form-template').html()),
  48. render: function () {
  49. var tag;
  50. if (this.model) {
  51. tag = this.model.toJSON();
  52. } else {
  53. tag = {name: ''};
  54. }
  55. this.$el.html(this.template(tag));
  56. return this;
  57. }
  58. }); // }}}
  59. // Add tag form view.
  60. B.Tags.Views.AddForm = B.Tags.Views.Form.extend({ // {{{
  61. remove: function () {
  62. this.$el.remove();
  63. this.trigger('edit:end');
  64. },
  65. saveChanges: function () {
  66. this.collection.create(this.getValues(), {
  67. wait: true,
  68. success: _.bind(function (model, resp) {
  69. this.remove();
  70. $.sticky('Added new tag');
  71. }, this),
  72. error: function (model, resp) {
  73. if (typeof resp === 'object') {
  74. resp = JSON.parse(resp.responseText).error;
  75. }
  76. $.sticky(resp, {category: 'error'});
  77. }
  78. });
  79. }
  80. }); // }}}
  81. // Edit tag form view.
  82. B.Tags.Views.EditForm = B.Tags.Views.Form.extend({ // {{{
  83. initialize: function () {
  84. // Uncheck tag before editing.
  85. this.model.set({checked: false});
  86. },
  87. remove: function () {
  88. var row = new B.Tags.Views.Row({model: this.model});
  89. this.$el.after(row.render().el).remove();
  90. this.model.trigger('edit:end');
  91. },
  92. saveChanges: function () {
  93. var name = this.model.get('name');
  94. this.model.save(this.getValues(), {
  95. wait: true,
  96. success: _.bind(function (model, resp) {
  97. this.remove();
  98. model.trigger('sort');
  99. $.sticky('Changed a tag');
  100. }, this),
  101. error: function (model, resp) {
  102. if (typeof resp === 'object') {
  103. // Restore previous tag name.
  104. model.set({name: name});
  105. resp = JSON.parse(resp.responseText).error;
  106. }
  107. $.sticky(resp, {category: 'error'});
  108. }
  109. });
  110. }
  111. }); // }}}
  112. B.Tags.Views.Table = B.Views.Table.extend({ // {{{
  113. initialize: function () {
  114. B.Views.Table.prototype.initialize.call(this);
  115. this.setElement($('#tags table'));
  116. },
  117. rowFactory: function () {
  118. return B.Tags.Views.Row;
  119. }
  120. }); // }}}
  121. B.Tags.Views.Tab = Backbone.View.extend({ // {{{
  122. events: {
  123. 'click #show-add-tag': 'showAddForm',
  124. 'click #delete-tags': 'deleteTags'
  125. },
  126. initialize: function () {
  127. this.setElement($('#tags'));
  128. this.collection = new B.Tags.Collection();
  129. this.table = new B.Tags.Views.Table({
  130. collection: this.collection
  131. });
  132. // Populate collection with tags.
  133. this.collection.reset(DATA.tags);
  134. // Make functions for enabling/disabling input elements.
  135. this.enable = _.bind(this.setDisabled, this, false);
  136. this.disable = _.bind(this.setDisabled, this, true);
  137. this.collection.on('change:checked', this.toggleDeleteTags, this);
  138. this.collection.on('change:checked', this.toggleCheckAll, this);
  139. this.collection.on('edit:start', this.disable);
  140. this.collection.on('edit:end', this.enable);
  141. this.collection.on('reset', this.enable);
  142. },
  143. // Disable input elements while displaying add/edit form.
  144. setDisabled: function (disable) {
  145. this.$('#show-add-tag').disabled(disable);
  146. this.$('table input[type=checkbox]').prop('disabled', disable);
  147. this.$('tbody div.edit').prop('disabled', disable);
  148. if (!disable) {
  149. disable = _.isEmpty(this.collection.checked());
  150. }
  151. this.$('#delete-tags').disabled(disable);
  152. },
  153. showAddForm: function () {
  154. var form = new B.Tags.Views.AddForm({
  155. collection: this.collection
  156. });
  157. form.on('edit:end', function () {
  158. if (this.collection.isEmpty()) {
  159. this.$('tr.empty-msg').show();
  160. }
  161. this.enable();
  162. }, this);
  163. this.disable();
  164. this.$('tr.empty-msg').hide();
  165. this.$('tbody').prepend(form.render().el);
  166. form.focus();
  167. },
  168. toggleCheckAll: function () {
  169. this.$('input.check-all').prop('checked',
  170. this.collection.length === this.collection.checked().length);
  171. },
  172. toggleDeleteTags: function () {
  173. this.$('#delete-tags').disabled(_.isEmpty(this.collection.checked()));
  174. },
  175. deleteTags: function () {
  176. var checkedIds = this.collection.checkedIds();
  177. $.ajax({
  178. type: 'DELETE',
  179. contentType: 'application/json',
  180. dataType: 'json',
  181. data: JSON.stringify({ids: checkedIds}),
  182. url: B.Tags.URL,
  183. success: _.bind(function () {
  184. var count = checkedIds.length;
  185. this.$('#delete-tags').disabled(true);
  186. this.$('input.check-all').prop('checked', false);
  187. this.collection.remove(checkedIds);
  188. // Trigger 'remove:multiple' event to update tag lists for
  189. // entries at once.
  190. this.collection.trigger('remove:multiple');
  191. $.sticky('Removed ' + count + (count > 1 ? ' tags' : ' tag'));
  192. }, this),
  193. error: function (xhr, textStatus, errorThrown) {
  194. // TODO: notify about error.
  195. }
  196. });
  197. }
  198. }); // }}}