src/backbone_modules/view.js   A
last analyzed

Complexity

Total Complexity 25
Complexity/F 1.56

Size

Lines of Code 199
Function Count 16

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 0
c 3
b 0
f 0
nc 4
dl 0
loc 199
rs 10
wmc 25
mnd 2
bc 25
fnc 16
bpm 1.5625
cpm 1.5625
noi 4

16 Functions

Rating   Name   Duplication   Size   Complexity  
A _.extend._removeElement 0 3 1
A _.extend.render 0 3 1
A _.extend.initialize 0 1 1
A _.extend._setElement 0 4 2
A _.extend.remove 0 5 1
A _.extend.preinitialize 0 1 1
A _.extend.$ 0 3 1
A _.extend.setElement 0 6 1
A _.extend.undelegateEvents 0 6 2
A _.extend._createElement 0 3 1
A view.js ➔ View 0 7 1
A _.extend._ensureElement 0 17 4
A _.extend._setAttributes 0 3 1
B _.extend.delegateEvents 0 19 5
A _.extend.undelegate 0 6 1
A _.extend.delegate 0 5 1
1
import $ from 'jquery';
0 ignored issues
show
introduced by
Definition for rule 'keyword-spacing' was not found
Loading history...
2
import _ from 'underscore';
3
import {
4
  Backbone
5
} from './core.js';
6
import {
7
  Events
8
} from './events.js';
9
10
// Backbone.View
11
// -------------
12
13
// Backbone Views are almost more convention than they are actual code. A View
14
// is simply a JavaScript object that represents a logical chunk of UI in the
15
// DOM. This might be a single item, an entire list, a sidebar or panel, or
16
// even the surrounding frame which wraps your whole app. Defining a chunk of
17
// UI as a **View** allows you to define your DOM events declaratively, without
18
// having to worry about render order ... and makes it easy for the view to
19
// react to specific changes in the state of your models.
20
21
// Cached regex to split keys for `delegate`.
22
var delegateEventSplitter = /^(\S+)\s*(.*)$/;
23
24
// List of view options to be set as properties.
25
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes',
26
  'className', 'tagName', 'events'
27
];
28
29
// Creating a Backbone.View creates its initial element outside of the DOM,
30
// if an existing element is not provided...
31
var View = function (options) {
32
  this.cid = _.uniqueId('view');
33
  this.preinitialize.apply(this, arguments);
34
  _.extend(this, _.pick(options, viewOptions));
35
  this._ensureElement();
36
  this.initialize.apply(this, arguments);
37
};
38
39
// Set up all inheritable **Backbone.View** properties and methods.
40
_.extend(View.prototype, Events, {
41
42
  // The default `tagName` of a View's element is `"div"`.
43
  tagName: 'div',
44
45
  // jQuery delegate for element lookup, scoped to DOM elements within the
46
  // current view. This should be preferred to global lookups where possible.
47
  $: function (selector) {
48
    return this.$el.find(selector);
49
  },
50
51
  // preinitialize is an empty function by default. You can override it with a function
52
  // or object.  preinitialize will run before any instantiation logic is run in the View
53
  preinitialize: function () {},
54
55
  // Initialize is an empty function by default. Override it with your own
56
  // initialization logic.
57
  initialize: function () {},
58
59
  // **render** is the core function that your view should override, in order
60
  // to populate its element (`this.el`), with the appropriate HTML. The
61
  // convention is for **render** to always return `this`.
62
  render: function () {
63
    return this;
64
  },
65
66
  // Remove this view by taking the element out of the DOM, and removing any
67
  // applicable Backbone.Events listeners.
68
  remove: function () {
69
    this._removeElement();
70
    this.stopListening();
71
    return this;
72
  },
73
74
  // Remove this view's element from the document and all event listeners
75
  // attached to it. Exposed for subclasses using an alternative DOM
76
  // manipulation API.
77
  _removeElement: function () {
78
    this.$el.remove();
79
  },
80
81
  // Change the view's element (`this.el` property) and re-delegate the
82
  // view's events on the new element.
83
  setElement: function (element) {
84
    this.undelegateEvents();
85
    this._setElement(element);
86
    this.delegateEvents();
87
    return this;
88
  },
89
90
  // Creates the `this.el` and `this.$el` references for this view using the
91
  // given `el`. `el` can be a CSS selector or an HTML string, a jQuery
92
  // context or an element. Subclasses can override this to utilize an
93
  // alternative DOM manipulation API and are only required to set the
94
  // `this.el` property.
95
  _setElement: function (el) {
96
    this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
97
    this.el = this.$el[0];
98
  },
99
100
  // Set callbacks, where `this.events` is a hash of
101
  //
102
  // *{"event selector": "callback"}*
103
  //
104
  //     {
105
  //       'mousedown .title':  'edit',
106
  //       'click .button':     'save',
107
  //       'click .open':       function(e) { ... }
108
  //     }
109
  //
110
  // pairs. Callbacks will be bound to the view, with `this` set properly.
111
  // Uses event delegation for efficiency.
112
  // Omitting the selector binds the event to `this.el`.
113
  delegateEvents: function (events) {
114
    events || (events = _.result(this, 'events'));
0 ignored issues
show
introduced by
Expected an assignment or function call and instead saw an expression.
Loading history...
115
    if (!events) {
116
      return this;
117
    }
118
    this.undelegateEvents();
119
    for (var key in events) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
120
      var method = events[key];
121
      if (!_.isFunction(method)) {
122
        method = this[method];
123
      }
124
      if (!method) {
125
        continue;
126
      }
127
      var match = key.match(delegateEventSplitter);
128
      this.delegate(match[1], match[2], _.bind(method, this));
129
    }
130
    return this;
131
  },
132
133
  // Add a single event listener to the view's element (or a child element
134
  // using `selector`). This only works for delegate-able events: not `focus`,
135
  // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
136
  delegate: function (eventName, selector, listener) {
137
    this.$el.on(eventName + '.delegateEvents' + this.cid, selector,
138
      listener);
139
    return this;
140
  },
141
142
  // Clears all callbacks previously bound to the view by `delegateEvents`.
143
  // You usually don't need to use this, but may wish to if you have multiple
144
  // Backbone views attached to the same DOM element.
145
  undelegateEvents: function () {
146
    if (this.$el) {
147
      this.$el.off('.delegateEvents' + this.cid);
148
    }
149
    return this;
150
  },
151
152
  // A finer-grained `undelegateEvents` for removing a single delegated event.
153
  // `selector` and `listener` are both optional.
154
  undelegate: function (eventName, selector, listener) {
155
    this.$el.off(eventName + '.delegateEvents' + this.cid,
156
      selector,
157
      listener);
158
    return this;
159
  },
160
161
  // Produces a DOM element to be assigned to your view. Exposed for
162
  // subclasses using an alternative DOM manipulation API.
163
  _createElement: function (tagName) {
164
    return document.createElement(tagName);
165
  },
166
167
  // Ensure that the View has a DOM element to render into.
168
  // If `this.el` is a string, pass it through `$()`, take the first
169
  // matching element, and re-assign it to `el`. Otherwise, create
170
  // an element from the `id`, `className` and `tagName` properties.
171
  _ensureElement: function () {
172
    if (!this.el) {
173
      var attrs = _.extend({}, _.result(this, 'attributes'));
174
      if (this.id) {
175
        attrs.id = _.result(this, 'id');
176
      }
177
      if (this.className) {
178
        attrs['class'] = _.result(this,
0 ignored issues
show
Coding Style Compatibility introduced by
As per coding-style, please use dot notation .class instead of brackets ["class"].

The dot notation is easier to read, less verbose, and also works better with certain Javascript optimizers.

Loading history...
179
          'className');
180
      }
181
      this.setElement(this._createElement(_.result(this,
182
        'tagName')));
183
      this._setAttributes(attrs);
184
    } else {
185
      this.setElement(_.result(this, 'el'));
186
    }
187
  },
188
189
  // Set attributes from a hash on this view's element.  Exposed for
190
  // subclasses using an alternative DOM manipulation API.
191
  _setAttributes: function (attributes) {
192
    this.$el.attr(attributes);
193
  }
194
195
});
196
197
export {
198
  View
199
};
200