/src/js/cilantro/models/filters.js

https://github.com/cbmi/cilantro · JavaScript · 150 lines · 97 code · 30 blank · 23 comment · 16 complexity · 3e75ee404ec8255c5f249a9646794f38 MD5 · raw file

  1. /* global define */
  2. define([
  3. 'jquery',
  4. 'underscore',
  5. 'backbone'
  6. ], function($, _, Backbone) {
  7. var generateId = function(attrs) {
  8. var id = '';
  9. if (attrs.concept) id += 'c' + attrs.concept;
  10. if (attrs.field) id += 'f' + attrs.field;
  11. if (!id) throw new Error('Cannot generate id for filter');
  12. return id;
  13. };
  14. // A filter encapsulates a single context node. The attributes must be tagged
  15. // with an identifier, either a `concept` id, `field` id or both.
  16. var Filter = Backbone.Model.extend({
  17. constructor: function(attrs, options) {
  18. if (attrs && !attrs.id) {
  19. attrs[this.idAttribute] = generateId(attrs);
  20. }
  21. Backbone.Model.prototype.constructor.call(this, attrs, options);
  22. },
  23. // This coincides with a possible feature in Backbone 1.1.3 that enables
  24. // dynamically generating ids rather than relying on a static id as defined
  25. // by idAttribute
  26. generateId: function(attrs) {
  27. return generateId(attrs);
  28. },
  29. clear: function(options) {
  30. var attrs = _.clone(this.attributes);
  31. // Do not unset required fields
  32. delete attrs.id;
  33. delete attrs.field;
  34. delete attrs.concept;
  35. // Void out the value for each key being unset otherwise
  36. // the change will not be recorded in Backbone
  37. _.each(attrs, function(value, key) {
  38. attrs[key] = undefined;
  39. });
  40. this.set(attrs, _.defaults({unset: true}, options));
  41. },
  42. // No syncing, i.e. no request, sync, error
  43. sync: function() {},
  44. apply: function(options) {
  45. this.trigger('apply', this, options);
  46. },
  47. unapply: function(options) {
  48. this.trigger('unapply', this, options);
  49. },
  50. // Performs a deep copy to prevent any shared referenced in nested
  51. // structures. Remove the id attribute by default
  52. toJSON: function(options) {
  53. options = _.defaults(options || {}, {id: false});
  54. var attrs = $.extend(true, {}, this.attributes);
  55. if (!options.id) delete attrs[this.idAttribute];
  56. return attrs;
  57. },
  58. // Convenience method for checking if the filter is enabled. If the
  59. // attribute is not set, this also implies it is enabled.
  60. isEnabled: function() {
  61. return this.get('enabled') !== false;
  62. },
  63. // Toggles if the filter is enabled/disabled
  64. toggleEnabled: function() {
  65. this.set('enabled', !this.isEnabled());
  66. }
  67. });
  68. // Collection of "filters" which correspond to an arbitrarily complex context node
  69. var Filters = Backbone.Collection.extend({
  70. model: Filter,
  71. constructor: function(models, options) {
  72. options = _.defaults({parse: true}, options);
  73. Backbone.Collection.prototype.constructor.call(this, models, options);
  74. },
  75. sync: function() {},
  76. apply: function(options) {
  77. this.trigger('apply', this, options);
  78. },
  79. unapply: function(options) {
  80. this.trigger('unapply', this, options);
  81. },
  82. // Handles both a normal array of model attributes and a context tree.
  83. // IDs are generated at this stage so attrs/models can be properly merged
  84. // into the existing collection.
  85. parse: function(models) {
  86. if (_.isArray(models)) {
  87. _.each(models, function(attrs) {
  88. if (!attrs.id) {
  89. attrs.id = this.model.prototype.generateId(attrs);
  90. }
  91. });
  92. } else {
  93. var attrs = models || {};
  94. models = [];
  95. if (attrs.field) {
  96. var id = this.model.prototype.generateId(attrs);
  97. models.push(_.extend({id: id}, attrs));
  98. }
  99. // Bare branch or standalone concept; traverse children.
  100. // Note: concept-level filters are not yet supported, so they
  101. // need to be deconstructed to their containing fields. The
  102. // will be augmented on the child if present.
  103. else if (attrs.children) {
  104. var concept = attrs.concept;
  105. _.each(attrs.children, function(child) {
  106. if (concept !== undefined) {
  107. child = _.extend({concept: concept}, child);
  108. }
  109. models = models.concat(this.parse(child));
  110. }, this);
  111. }
  112. }
  113. return models;
  114. }
  115. });
  116. return {
  117. Filter: Filter,
  118. Filters: Filters,
  119. };
  120. });