source: imaps-frontend/node_modules/css-tree/lib/common/List.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 12.6 KB
Line 
1//
2// list
3// ┌──────┐
4// ┌──────────────┼─head │
5// │ │ tail─┼──────────────┐
6// │ └──────┘ │
7// ▼ ▼
8// item item item item
9// ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
10// null ◀──┼─prev │◀───┼─prev │◀───┼─prev │◀───┼─prev │
11// │ next─┼───▶│ next─┼───▶│ next─┼───▶│ next─┼──▶ null
12// ├──────┤ ├──────┤ ├──────┤ ├──────┤
13// │ data │ │ data │ │ data │ │ data │
14// └──────┘ └──────┘ └──────┘ └──────┘
15//
16
17function createItem(data) {
18 return {
19 prev: null,
20 next: null,
21 data: data
22 };
23}
24
25function allocateCursor(node, prev, next) {
26 var cursor;
27
28 if (cursors !== null) {
29 cursor = cursors;
30 cursors = cursors.cursor;
31 cursor.prev = prev;
32 cursor.next = next;
33 cursor.cursor = node.cursor;
34 } else {
35 cursor = {
36 prev: prev,
37 next: next,
38 cursor: node.cursor
39 };
40 }
41
42 node.cursor = cursor;
43
44 return cursor;
45}
46
47function releaseCursor(node) {
48 var cursor = node.cursor;
49
50 node.cursor = cursor.cursor;
51 cursor.prev = null;
52 cursor.next = null;
53 cursor.cursor = cursors;
54 cursors = cursor;
55}
56
57var cursors = null;
58var List = function() {
59 this.cursor = null;
60 this.head = null;
61 this.tail = null;
62};
63
64List.createItem = createItem;
65List.prototype.createItem = createItem;
66
67List.prototype.updateCursors = function(prevOld, prevNew, nextOld, nextNew) {
68 var cursor = this.cursor;
69
70 while (cursor !== null) {
71 if (cursor.prev === prevOld) {
72 cursor.prev = prevNew;
73 }
74
75 if (cursor.next === nextOld) {
76 cursor.next = nextNew;
77 }
78
79 cursor = cursor.cursor;
80 }
81};
82
83List.prototype.getSize = function() {
84 var size = 0;
85 var cursor = this.head;
86
87 while (cursor) {
88 size++;
89 cursor = cursor.next;
90 }
91
92 return size;
93};
94
95List.prototype.fromArray = function(array) {
96 var cursor = null;
97
98 this.head = null;
99
100 for (var i = 0; i < array.length; i++) {
101 var item = createItem(array[i]);
102
103 if (cursor !== null) {
104 cursor.next = item;
105 } else {
106 this.head = item;
107 }
108
109 item.prev = cursor;
110 cursor = item;
111 }
112
113 this.tail = cursor;
114
115 return this;
116};
117
118List.prototype.toArray = function() {
119 var cursor = this.head;
120 var result = [];
121
122 while (cursor) {
123 result.push(cursor.data);
124 cursor = cursor.next;
125 }
126
127 return result;
128};
129
130List.prototype.toJSON = List.prototype.toArray;
131
132List.prototype.isEmpty = function() {
133 return this.head === null;
134};
135
136List.prototype.first = function() {
137 return this.head && this.head.data;
138};
139
140List.prototype.last = function() {
141 return this.tail && this.tail.data;
142};
143
144List.prototype.each = function(fn, context) {
145 var item;
146
147 if (context === undefined) {
148 context = this;
149 }
150
151 // push cursor
152 var cursor = allocateCursor(this, null, this.head);
153
154 while (cursor.next !== null) {
155 item = cursor.next;
156 cursor.next = item.next;
157
158 fn.call(context, item.data, item, this);
159 }
160
161 // pop cursor
162 releaseCursor(this);
163};
164
165List.prototype.forEach = List.prototype.each;
166
167List.prototype.eachRight = function(fn, context) {
168 var item;
169
170 if (context === undefined) {
171 context = this;
172 }
173
174 // push cursor
175 var cursor = allocateCursor(this, this.tail, null);
176
177 while (cursor.prev !== null) {
178 item = cursor.prev;
179 cursor.prev = item.prev;
180
181 fn.call(context, item.data, item, this);
182 }
183
184 // pop cursor
185 releaseCursor(this);
186};
187
188List.prototype.forEachRight = List.prototype.eachRight;
189
190List.prototype.reduce = function(fn, initialValue, context) {
191 var item;
192
193 if (context === undefined) {
194 context = this;
195 }
196
197 // push cursor
198 var cursor = allocateCursor(this, null, this.head);
199 var acc = initialValue;
200
201 while (cursor.next !== null) {
202 item = cursor.next;
203 cursor.next = item.next;
204
205 acc = fn.call(context, acc, item.data, item, this);
206 }
207
208 // pop cursor
209 releaseCursor(this);
210
211 return acc;
212};
213
214List.prototype.reduceRight = function(fn, initialValue, context) {
215 var item;
216
217 if (context === undefined) {
218 context = this;
219 }
220
221 // push cursor
222 var cursor = allocateCursor(this, this.tail, null);
223 var acc = initialValue;
224
225 while (cursor.prev !== null) {
226 item = cursor.prev;
227 cursor.prev = item.prev;
228
229 acc = fn.call(context, acc, item.data, item, this);
230 }
231
232 // pop cursor
233 releaseCursor(this);
234
235 return acc;
236};
237
238List.prototype.nextUntil = function(start, fn, context) {
239 if (start === null) {
240 return;
241 }
242
243 var item;
244
245 if (context === undefined) {
246 context = this;
247 }
248
249 // push cursor
250 var cursor = allocateCursor(this, null, start);
251
252 while (cursor.next !== null) {
253 item = cursor.next;
254 cursor.next = item.next;
255
256 if (fn.call(context, item.data, item, this)) {
257 break;
258 }
259 }
260
261 // pop cursor
262 releaseCursor(this);
263};
264
265List.prototype.prevUntil = function(start, fn, context) {
266 if (start === null) {
267 return;
268 }
269
270 var item;
271
272 if (context === undefined) {
273 context = this;
274 }
275
276 // push cursor
277 var cursor = allocateCursor(this, start, null);
278
279 while (cursor.prev !== null) {
280 item = cursor.prev;
281 cursor.prev = item.prev;
282
283 if (fn.call(context, item.data, item, this)) {
284 break;
285 }
286 }
287
288 // pop cursor
289 releaseCursor(this);
290};
291
292List.prototype.some = function(fn, context) {
293 var cursor = this.head;
294
295 if (context === undefined) {
296 context = this;
297 }
298
299 while (cursor !== null) {
300 if (fn.call(context, cursor.data, cursor, this)) {
301 return true;
302 }
303
304 cursor = cursor.next;
305 }
306
307 return false;
308};
309
310List.prototype.map = function(fn, context) {
311 var result = new List();
312 var cursor = this.head;
313
314 if (context === undefined) {
315 context = this;
316 }
317
318 while (cursor !== null) {
319 result.appendData(fn.call(context, cursor.data, cursor, this));
320 cursor = cursor.next;
321 }
322
323 return result;
324};
325
326List.prototype.filter = function(fn, context) {
327 var result = new List();
328 var cursor = this.head;
329
330 if (context === undefined) {
331 context = this;
332 }
333
334 while (cursor !== null) {
335 if (fn.call(context, cursor.data, cursor, this)) {
336 result.appendData(cursor.data);
337 }
338 cursor = cursor.next;
339 }
340
341 return result;
342};
343
344List.prototype.clear = function() {
345 this.head = null;
346 this.tail = null;
347};
348
349List.prototype.copy = function() {
350 var result = new List();
351 var cursor = this.head;
352
353 while (cursor !== null) {
354 result.insert(createItem(cursor.data));
355 cursor = cursor.next;
356 }
357
358 return result;
359};
360
361List.prototype.prepend = function(item) {
362 // head
363 // ^
364 // item
365 this.updateCursors(null, item, this.head, item);
366
367 // insert to the beginning of the list
368 if (this.head !== null) {
369 // new item <- first item
370 this.head.prev = item;
371
372 // new item -> first item
373 item.next = this.head;
374 } else {
375 // if list has no head, then it also has no tail
376 // in this case tail points to the new item
377 this.tail = item;
378 }
379
380 // head always points to new item
381 this.head = item;
382
383 return this;
384};
385
386List.prototype.prependData = function(data) {
387 return this.prepend(createItem(data));
388};
389
390List.prototype.append = function(item) {
391 return this.insert(item);
392};
393
394List.prototype.appendData = function(data) {
395 return this.insert(createItem(data));
396};
397
398List.prototype.insert = function(item, before) {
399 if (before !== undefined && before !== null) {
400 // prev before
401 // ^
402 // item
403 this.updateCursors(before.prev, item, before, item);
404
405 if (before.prev === null) {
406 // insert to the beginning of list
407 if (this.head !== before) {
408 throw new Error('before doesn\'t belong to list');
409 }
410
411 // since head points to before therefore list doesn't empty
412 // no need to check tail
413 this.head = item;
414 before.prev = item;
415 item.next = before;
416
417 this.updateCursors(null, item);
418 } else {
419
420 // insert between two items
421 before.prev.next = item;
422 item.prev = before.prev;
423
424 before.prev = item;
425 item.next = before;
426 }
427 } else {
428 // tail
429 // ^
430 // item
431 this.updateCursors(this.tail, item, null, item);
432
433 // insert to the ending of the list
434 if (this.tail !== null) {
435 // last item -> new item
436 this.tail.next = item;
437
438 // last item <- new item
439 item.prev = this.tail;
440 } else {
441 // if list has no tail, then it also has no head
442 // in this case head points to new item
443 this.head = item;
444 }
445
446 // tail always points to new item
447 this.tail = item;
448 }
449
450 return this;
451};
452
453List.prototype.insertData = function(data, before) {
454 return this.insert(createItem(data), before);
455};
456
457List.prototype.remove = function(item) {
458 // item
459 // ^
460 // prev next
461 this.updateCursors(item, item.prev, item, item.next);
462
463 if (item.prev !== null) {
464 item.prev.next = item.next;
465 } else {
466 if (this.head !== item) {
467 throw new Error('item doesn\'t belong to list');
468 }
469
470 this.head = item.next;
471 }
472
473 if (item.next !== null) {
474 item.next.prev = item.prev;
475 } else {
476 if (this.tail !== item) {
477 throw new Error('item doesn\'t belong to list');
478 }
479
480 this.tail = item.prev;
481 }
482
483 item.prev = null;
484 item.next = null;
485
486 return item;
487};
488
489List.prototype.push = function(data) {
490 this.insert(createItem(data));
491};
492
493List.prototype.pop = function() {
494 if (this.tail !== null) {
495 return this.remove(this.tail);
496 }
497};
498
499List.prototype.unshift = function(data) {
500 this.prepend(createItem(data));
501};
502
503List.prototype.shift = function() {
504 if (this.head !== null) {
505 return this.remove(this.head);
506 }
507};
508
509List.prototype.prependList = function(list) {
510 return this.insertList(list, this.head);
511};
512
513List.prototype.appendList = function(list) {
514 return this.insertList(list);
515};
516
517List.prototype.insertList = function(list, before) {
518 // ignore empty lists
519 if (list.head === null) {
520 return this;
521 }
522
523 if (before !== undefined && before !== null) {
524 this.updateCursors(before.prev, list.tail, before, list.head);
525
526 // insert in the middle of dist list
527 if (before.prev !== null) {
528 // before.prev <-> list.head
529 before.prev.next = list.head;
530 list.head.prev = before.prev;
531 } else {
532 this.head = list.head;
533 }
534
535 before.prev = list.tail;
536 list.tail.next = before;
537 } else {
538 this.updateCursors(this.tail, list.tail, null, list.head);
539
540 // insert to end of the list
541 if (this.tail !== null) {
542 // if destination list has a tail, then it also has a head,
543 // but head doesn't change
544
545 // dest tail -> source head
546 this.tail.next = list.head;
547
548 // dest tail <- source head
549 list.head.prev = this.tail;
550 } else {
551 // if list has no a tail, then it also has no a head
552 // in this case points head to new item
553 this.head = list.head;
554 }
555
556 // tail always start point to new item
557 this.tail = list.tail;
558 }
559
560 list.head = null;
561 list.tail = null;
562
563 return this;
564};
565
566List.prototype.replace = function(oldItem, newItemOrList) {
567 if ('head' in newItemOrList) {
568 this.insertList(newItemOrList, oldItem);
569 } else {
570 this.insert(newItemOrList, oldItem);
571 }
572
573 this.remove(oldItem);
574};
575
576module.exports = List;
Note: See TracBrowser for help on using the repository browser.