acts_as_dashboard 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +54 -0
  5. data/Rakefile +23 -0
  6. data/VERSION +1 -0
  7. data/acts_as_dashboard.gemspec +166 -0
  8. data/generators/dashboard/USAGE +23 -0
  9. data/generators/dashboard/dashboard_generator.rb +105 -0
  10. data/generators/dashboard/templates/controller.erb +39 -0
  11. data/generators/dashboard/templates/dashboard.css +66 -0
  12. data/generators/dashboard/templates/dashboard.js +305 -0
  13. data/generators/dashboard/templates/jqplot-0.9.7/jquery.jqplot.min.js +14 -0
  14. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.barRenderer.js +404 -0
  15. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.barRenderer.min.js +14 -0
  16. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.canvasAxisLabelRenderer.js +200 -0
  17. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.canvasAxisLabelRenderer.min.js +14 -0
  18. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.canvasAxisTickRenderer.js +232 -0
  19. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.canvasAxisTickRenderer.min.js +14 -0
  20. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.canvasTextRenderer.js +408 -0
  21. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.canvasTextRenderer.min.js +14 -0
  22. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.categoryAxisRenderer.js +238 -0
  23. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.categoryAxisRenderer.min.js +14 -0
  24. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.cursor.js +812 -0
  25. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.cursor.min.js +14 -0
  26. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.dateAxisRenderer.js +313 -0
  27. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.dateAxisRenderer.min.js +14 -0
  28. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.dragable.js +203 -0
  29. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.dragable.min.js +14 -0
  30. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.highlighter.js +359 -0
  31. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.highlighter.min.js +14 -0
  32. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.logAxisRenderer.js +434 -0
  33. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.logAxisRenderer.min.js +14 -0
  34. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.mekkoAxisRenderer.js +595 -0
  35. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.mekkoAxisRenderer.min.js +14 -0
  36. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.mekkoRenderer.js +308 -0
  37. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.mekkoRenderer.min.js +14 -0
  38. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.ohlcRenderer.js +343 -0
  39. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.ohlcRenderer.min.js +14 -0
  40. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.pieRenderer.js +333 -0
  41. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.pieRenderer.min.js +14 -0
  42. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.pointLabels.js +307 -0
  43. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.pointLabels.js.orig +273 -0
  44. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.pointLabels.min.js +14 -0
  45. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.trendline.js +208 -0
  46. data/generators/dashboard/templates/jqplot-0.9.7/plugins/jqplot.trendline.min.js +14 -0
  47. data/generators/dashboard/templates/jquery.jqplot.min.css +1 -0
  48. data/generators/dashboard/templates/js.class-2.1.4/CHANGELOG +269 -0
  49. data/generators/dashboard/templates/js.class-2.1.4/MIT-LICENSE +30 -0
  50. data/generators/dashboard/templates/js.class-2.1.4/README +30 -0
  51. data/generators/dashboard/templates/js.class-2.1.4/min/command.js +1 -0
  52. data/generators/dashboard/templates/js.class-2.1.4/min/comparable.js +1 -0
  53. data/generators/dashboard/templates/js.class-2.1.4/min/constant_scope.js +1 -0
  54. data/generators/dashboard/templates/js.class-2.1.4/min/core.js +1 -0
  55. data/generators/dashboard/templates/js.class-2.1.4/min/decorator.js +1 -0
  56. data/generators/dashboard/templates/js.class-2.1.4/min/enumerable.js +1 -0
  57. data/generators/dashboard/templates/js.class-2.1.4/min/forwardable.js +1 -0
  58. data/generators/dashboard/templates/js.class-2.1.4/min/hash.js +1 -0
  59. data/generators/dashboard/templates/js.class-2.1.4/min/linked_list.js +1 -0
  60. data/generators/dashboard/templates/js.class-2.1.4/min/loader.js +1 -0
  61. data/generators/dashboard/templates/js.class-2.1.4/min/method_chain.js +1 -0
  62. data/generators/dashboard/templates/js.class-2.1.4/min/observable.js +1 -0
  63. data/generators/dashboard/templates/js.class-2.1.4/min/package.js +1 -0
  64. data/generators/dashboard/templates/js.class-2.1.4/min/proxy.js +1 -0
  65. data/generators/dashboard/templates/js.class-2.1.4/min/ruby.js +1 -0
  66. data/generators/dashboard/templates/js.class-2.1.4/min/set.js +1 -0
  67. data/generators/dashboard/templates/js.class-2.1.4/min/stack_trace.js +1 -0
  68. data/generators/dashboard/templates/js.class-2.1.4/min/state.js +1 -0
  69. data/generators/dashboard/templates/js.class-2.1.4/min/stdlib.js +16 -0
  70. data/generators/dashboard/templates/js.class-2.1.4/src/command.js +93 -0
  71. data/generators/dashboard/templates/js.class-2.1.4/src/comparable.js +37 -0
  72. data/generators/dashboard/templates/js.class-2.1.4/src/constant_scope.js +48 -0
  73. data/generators/dashboard/templates/js.class-2.1.4/src/core.js +1060 -0
  74. data/generators/dashboard/templates/js.class-2.1.4/src/decorator.js +50 -0
  75. data/generators/dashboard/templates/js.class-2.1.4/src/enumerable.js +505 -0
  76. data/generators/dashboard/templates/js.class-2.1.4/src/forwardable.js +22 -0
  77. data/generators/dashboard/templates/js.class-2.1.4/src/hash.js +334 -0
  78. data/generators/dashboard/templates/js.class-2.1.4/src/linked_list.js +114 -0
  79. data/generators/dashboard/templates/js.class-2.1.4/src/loader.js +458 -0
  80. data/generators/dashboard/templates/js.class-2.1.4/src/method_chain.js +172 -0
  81. data/generators/dashboard/templates/js.class-2.1.4/src/observable.js +55 -0
  82. data/generators/dashboard/templates/js.class-2.1.4/src/package.js +377 -0
  83. data/generators/dashboard/templates/js.class-2.1.4/src/proxy.js +58 -0
  84. data/generators/dashboard/templates/js.class-2.1.4/src/ruby.js +44 -0
  85. data/generators/dashboard/templates/js.class-2.1.4/src/set.js +332 -0
  86. data/generators/dashboard/templates/js.class-2.1.4/src/stack_trace.js +151 -0
  87. data/generators/dashboard/templates/js.class-2.1.4/src/state.js +95 -0
  88. data/generators/dashboard/templates/js.class-2.1.4/src/stdlib.js +2517 -0
  89. data/generators/dashboard/templates/show.html.erb +67 -0
  90. data/lib/acts_as_dashboard/app/views/dashboards/show.html.erb +67 -0
  91. data/lib/acts_as_dashboard/class_methods.rb +58 -0
  92. data/lib/acts_as_dashboard/config.rb +25 -0
  93. data/lib/acts_as_dashboard/instance_methods.rb +32 -0
  94. data/lib/acts_as_dashboard/line_graph_widget.rb +68 -0
  95. data/lib/acts_as_dashboard/public/javascripts/dashboard.js +305 -0
  96. data/lib/acts_as_dashboard/public/stylesheets/dashboard.css +66 -0
  97. data/lib/acts_as_dashboard/short_messages_widget.rb +25 -0
  98. data/lib/acts_as_dashboard/widget.rb +55 -0
  99. data/lib/acts_as_dashboard.rb +17 -0
  100. data/spec/acts_as_dashboard/class_method_specs.rb +188 -0
  101. data/spec/acts_as_dashboard/config_spec.rb +57 -0
  102. data/spec/acts_as_dashboard/instance_methods_spec.rb +134 -0
  103. data/spec/acts_as_dashboard/line_graph_widget_spec.rb +165 -0
  104. data/spec/acts_as_dashboard/short_messages_widget_spec.rb +69 -0
  105. data/spec/acts_as_dashboard/widget_spec.rb +6 -0
  106. data/spec/acts_as_dashboard_spec.rb +15 -0
  107. data/spec/shared/widget_behaviours.rb +171 -0
  108. data/spec/spec.opts +1 -0
  109. data/spec/spec_helper.rb +10 -0
  110. data/tasks/install.rake +8 -0
  111. data/tasks/install_javascript.rake +7 -0
  112. data/tasks/install_stylesheets.rake +7 -0
  113. metadata +209 -0
@@ -0,0 +1,334 @@
1
+ JS.Hash = new JS.Class('Hash', {
2
+ include: JS.Enumerable || {},
3
+
4
+ extend: {
5
+ Pair: new JS.Class({
6
+ include: JS.Comparable || {},
7
+
8
+ setKey: function(key) {
9
+ this[0] = this.key = key;
10
+ },
11
+
12
+ hasKey: function(key) {
13
+ var my = this.key;
14
+ return my.equals ? my.equals(key) : my === key;
15
+ },
16
+
17
+ setValue: function(value) {
18
+ this[1] = this.value = value;
19
+ },
20
+
21
+ hasValue: function(value) {
22
+ var my = this.value;
23
+ return my.equals ? my.equals(value) : my === value;
24
+ },
25
+
26
+ compareTo: function(other) {
27
+ return this.key.compareTo
28
+ ? this.key.compareTo(other.key)
29
+ : (this.key < other.key ? -1 : (this.key > other.key ? 1 : 0));
30
+ },
31
+
32
+ hash: function() {
33
+ var key = JS.Hash.codeFor(this.key),
34
+ value = JS.Hash.codeFor(this.value);
35
+
36
+ return [key, value].sort().join('');
37
+ }
38
+ }),
39
+
40
+ codeFor: function(object) {
41
+ if (typeof object !== 'object') return String(object);
42
+ return JS.isFn(object.hash)
43
+ ? object.hash()
44
+ : object.toString();
45
+ }
46
+ },
47
+
48
+ initialize: function(object) {
49
+ this.clear();
50
+ if (!JS.isType(object, Array)) return this.setDefault(object);
51
+ for (var i = 0, n = object.length; i < n; i += 2)
52
+ this.store(object[i], object[i+1]);
53
+ },
54
+
55
+ forEach: function(block, context) {
56
+ if (!block) return this.enumFor('forEach');
57
+ block = JS.Enumerable.toFn(block);
58
+
59
+ var hash, bucket, i;
60
+
61
+ for (hash in this._buckets) {
62
+ if (!this._buckets.hasOwnProperty(hash)) continue;
63
+ bucket = this._buckets[hash];
64
+ i = bucket.length;
65
+ while (i--) block.call(context || null, bucket[i]);
66
+ }
67
+ return this;
68
+ },
69
+
70
+ _bucketForKey: function(key, createIfAbsent) {
71
+ var hash = this.klass.codeFor(key),
72
+ bucket = this._buckets[hash];
73
+
74
+ if (!bucket && createIfAbsent)
75
+ bucket = this._buckets[hash] = [];
76
+
77
+ return bucket;
78
+ },
79
+
80
+ _indexInBucket: function(bucket, key) {
81
+ var i = bucket.length,
82
+ ident = !!this._compareByIdentity;
83
+
84
+ while (i--) {
85
+ if (ident ? (bucket[i].key === key) : bucket[i].hasKey(key))
86
+ return i;
87
+ }
88
+ return -1;
89
+ },
90
+
91
+ assoc: function(key, createIfAbsent) {
92
+ var bucket, index, pair;
93
+
94
+ bucket = this._bucketForKey(key, createIfAbsent);
95
+ if (!bucket) return null;
96
+
97
+ index = this._indexInBucket(bucket, key);
98
+ if (index > -1) return bucket[index];
99
+ if (!createIfAbsent) return null;
100
+
101
+ this.size += 1; this.length += 1;
102
+ pair = new this.klass.Pair;
103
+ pair.setKey(key);
104
+ bucket.push(pair);
105
+ return pair;
106
+ },
107
+
108
+ rassoc: function(value) {
109
+ var key = this.key(value);
110
+ return key ? this.assoc(key) : null;
111
+ },
112
+
113
+ clear: function() {
114
+ this._buckets = {};
115
+ this.length = this.size = 0;
116
+ },
117
+
118
+ compareByIdentity: function() {
119
+ this._compareByIdentity = true;
120
+ },
121
+
122
+ comparesByIdentity: function() {
123
+ return !!this._compareByIdentity;
124
+ },
125
+
126
+ setDefault: function(value) {
127
+ this._default = value;
128
+ return this;
129
+ },
130
+
131
+ getDefault: function(key) {
132
+ return JS.isFn(this._default)
133
+ ? this._default(this, key)
134
+ : (this._default || null);
135
+ },
136
+
137
+ equals: function(other) {
138
+ if (!JS.isType(other, JS.Hash) || this.length !== other.length)
139
+ return false;
140
+ var result = true;
141
+ this.forEach(function(pair) {
142
+ if (!result) return;
143
+ var otherPair = other.assoc(pair.key);
144
+ if (otherPair === null || !otherPair.hasValue(pair.value)) result = false;
145
+ });
146
+ return result;
147
+ },
148
+
149
+ hash: function() {
150
+ var hashes = [];
151
+ this.forEach(function(pair) { hashes.push(pair.hash()) });
152
+ return hashes.sort().join('');
153
+ },
154
+
155
+ fetch: function(key, defaultValue) {
156
+ var pair = this.assoc(key);
157
+ if (pair) return pair.value;
158
+
159
+ if (defaultValue === undefined) throw new Error('key not found');
160
+ if (JS.isFn(defaultValue)) return defaultValue(key);
161
+ return defaultValue;
162
+ },
163
+
164
+ forEachKey: function(block, context) {
165
+ if (!block) return this.enumFor('forEachKey');
166
+ block = JS.Enumerable.toFn(block);
167
+
168
+ this.forEach(function(pair) {
169
+ block.call(context || null, pair.key);
170
+ });
171
+ return this;
172
+ },
173
+
174
+ forEachPair: function(block, context) {
175
+ if (!block) return this.enumFor('forEachPair');
176
+ block = JS.Enumerable.toFn(block);
177
+
178
+ this.forEach(function(pair) {
179
+ block.call(context || null, pair.key, pair.value);
180
+ });
181
+ return this;
182
+ },
183
+
184
+ forEachValue: function(block, context) {
185
+ if (!block) return this.enumFor('forEachValue');
186
+ block = JS.Enumerable.toFn(block);
187
+
188
+ this.forEach(function(pair) {
189
+ block.call(context || null, pair.value);
190
+ });
191
+ return this;
192
+ },
193
+
194
+ get: function(key) {
195
+ var pair = this.assoc(key);
196
+ return pair ? pair.value : this.getDefault(key);
197
+ },
198
+
199
+ hasKey: function(key) {
200
+ return !!this.assoc(key);
201
+ },
202
+
203
+ hasValue: function(value) {
204
+ var has = false, ident = !!this._compareByIdentity;
205
+ this.forEach(function(pair) {
206
+ if ((value.equals && !ident) ? value.equals(pair.value) : value === pair.value)
207
+ has = true;
208
+ });
209
+ return has;
210
+ },
211
+
212
+ invert: function() {
213
+ var hash = new this.klass;
214
+ this.forEach(function(pair) {
215
+ hash.store(pair.value, pair.key);
216
+ });
217
+ return hash;
218
+ },
219
+
220
+ isEmpty: function() {
221
+ for (var hash in this._buckets) {
222
+ if (this._buckets.hasOwnProperty(hash) && this._buckets[hash].length > 0)
223
+ return false;
224
+ }
225
+ return true;
226
+ },
227
+
228
+ key: function(value) {
229
+ var result = null;
230
+ this.forEach(function(pair) {
231
+ if (value.equals ? value.equals(pair.value) : (value === pair.value))
232
+ result = pair.key;
233
+ });
234
+ return result;
235
+ },
236
+
237
+ keys: function() {
238
+ var keys = [];
239
+ this.forEach(function(pair) { keys.push(pair.key) });
240
+ return keys;
241
+ },
242
+
243
+ merge: function(hash, block, context) {
244
+ var newHash = new this.klass;
245
+ newHash.update(this);
246
+ newHash.update(hash, block, context);
247
+ return newHash;
248
+ },
249
+
250
+ rehash: function() {
251
+ var temp = new this.klass;
252
+ temp._buckets = this._buckets;
253
+ this.clear();
254
+ this.update(temp);
255
+ },
256
+
257
+ remove: function(key, block) {
258
+ if (block === undefined) block = null;
259
+ var bucket, index, result;
260
+
261
+ bucket = this._bucketForKey(key);
262
+ if (!bucket) return JS.isFn(block) ? this.fetch(key, block) : this.getDefault(key);
263
+
264
+ index = this._indexInBucket(bucket, key);
265
+ if (index < 0) return JS.isFn(block) ? this.fetch(key, block) : this.getDefault(key);
266
+
267
+ result = bucket[index].value;
268
+ bucket.splice(index, 1);
269
+ this.size -= 1;
270
+ this.length -= 1;
271
+
272
+ if (bucket.length === 0)
273
+ delete this._buckets[this.klass.codeFor(key)];
274
+
275
+ return result;
276
+ },
277
+
278
+ removeIf: function(block, context) {
279
+ if (!block) return this.enumFor('removeIf');
280
+ block = JS.Enumerable.toFn(block);
281
+
282
+ this.forEach(function(pair) {
283
+ if (block.call(context || null, pair))
284
+ this.remove(pair.key);
285
+ }, this);
286
+ return this;
287
+ },
288
+
289
+ replace: function(hash) {
290
+ this.clear();
291
+ this.update(hash);
292
+ },
293
+
294
+ shift: function() {
295
+ var keys = this.keys();
296
+ if (keys.length === 0) return this.getDefault();
297
+ var pair = this.assoc(keys[0]);
298
+ this.remove(pair.key);
299
+ return pair;
300
+ },
301
+
302
+ store: function(key, value) {
303
+ this.assoc(key, true).setValue(value);
304
+ return value;
305
+ },
306
+
307
+ update: function(hash, block, context) {
308
+ var blockGiven = JS.isFn(block);
309
+ hash.forEach(function(pair) {
310
+ var key = pair.key, value = pair.value;
311
+ if (blockGiven && this.hasKey(key))
312
+ value = block.call(context || null, key, this.get(key), value);
313
+ this.store(key, value);
314
+ }, this);
315
+ },
316
+
317
+ values: function() {
318
+ var values = [];
319
+ this.forEach(function(pair) { values.push(pair.value) });
320
+ return values;
321
+ },
322
+
323
+ valuesAt: function() {
324
+ var i = arguments.length, results = [];
325
+ while (i--) results.push(this.get(arguments[i]));
326
+ return results;
327
+ }
328
+ });
329
+
330
+ JS.Hash.include({
331
+ includes: JS.Hash.instanceMethod('hasKey'),
332
+ index: JS.Hash.instanceMethod('key'),
333
+ put: JS.Hash.instanceMethod('store')
334
+ }, true);
@@ -0,0 +1,114 @@
1
+ JS.LinkedList = new JS.Class('LinkedList', {
2
+ include: JS.Enumerable || {},
3
+
4
+ initialize: function(array, useNodes) {
5
+ this.length = 0;
6
+ this.first = this.last = null;
7
+ if (!array) return;
8
+ for (var i = 0, n = array.length; i < n; i++)
9
+ this.push( useNodes ? new this.klass.Node(array[i]) : array[i] );
10
+ },
11
+
12
+ forEach: function(block, context) {
13
+ if (!block) return this.enumFor('forEach');
14
+ block = JS.Enumerable.toFn(block);
15
+
16
+ var node = this.first,
17
+ next, i, n;
18
+
19
+ for (i = 0, n = this.length; i < n; i++) {
20
+ next = node.next;
21
+ block.call(context || null, node, i);
22
+ if (node === this.last) break;
23
+ node = next;
24
+ }
25
+ return this;
26
+ },
27
+
28
+ at: function(n) {
29
+ if (n < 0 || n >= this.length) return undefined;
30
+ var node = this.first;
31
+ while (n--) node = node.next;
32
+ return node;
33
+ },
34
+
35
+ pop: function() {
36
+ return this.length ? this.remove(this.last) : undefined;
37
+ },
38
+
39
+ shift: function() {
40
+ return this.length ? this.remove(this.first) : undefined;
41
+ },
42
+
43
+ // stubs - should be implemented by concrete list types
44
+ insertAfter: function() {},
45
+ push: function() {},
46
+ remove: function() {},
47
+
48
+ extend: {
49
+ Node: new JS.Class({
50
+ initialize: function(data) {
51
+ this.data = data;
52
+ this.prev = this.next = this.list = null;
53
+ }
54
+ })
55
+ }
56
+ });
57
+
58
+ JS.LinkedList.Doubly = new JS.Class('LinkedList.Doubly', JS.LinkedList, {
59
+ insertAt: function(n, newNode) {
60
+ if (n < 0 || n >= this.length) return;
61
+ this.insertBefore(this.at(n), newNode);
62
+ },
63
+
64
+ unshift: function(newNode) {
65
+ this.length > 0
66
+ ? this.insertBefore(this.first, newNode)
67
+ : this.push(newNode);
68
+ },
69
+
70
+ insertBefore: function() {}
71
+ });
72
+
73
+ JS.LinkedList.insertTemplate = function(prev, next, pos) {
74
+ return function(node, newNode) {
75
+ if (node.list !== this) return;
76
+ newNode[prev] = node;
77
+ newNode[next] = node[next];
78
+ node[next] = (node[next][prev] = newNode);
79
+ if (newNode[prev] === this[pos]) this[pos] = newNode;
80
+ newNode.list = this;
81
+ this.length++;
82
+ };
83
+ };
84
+
85
+ JS.LinkedList.Doubly.Circular = new JS.Class('LinkedList.Doubly.Circular', JS.LinkedList.Doubly, {
86
+ insertAfter: JS.LinkedList.insertTemplate('prev', 'next', 'last'),
87
+ insertBefore: JS.LinkedList.insertTemplate('next', 'prev', 'first'),
88
+
89
+ push: function(newNode) {
90
+ if (this.length)
91
+ return this.insertAfter(this.last, newNode);
92
+
93
+ this.first = this.last =
94
+ newNode.prev = newNode.next = newNode;
95
+
96
+ newNode.list = this;
97
+ this.length = 1;
98
+ },
99
+
100
+ remove: function(removed) {
101
+ if (removed.list !== this || this.length === 0) return null;
102
+ if (this.length > 1) {
103
+ removed.prev.next = removed.next;
104
+ removed.next.prev = removed.prev;
105
+ if (removed === this.first) this.first = removed.next;
106
+ if (removed === this.last) this.last = removed.prev;
107
+ } else {
108
+ this.first = this.last = null;
109
+ }
110
+ removed.prev = removed.next = removed.list = null;
111
+ this.length--;
112
+ return removed;
113
+ }
114
+ });