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

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 11.7 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.nextUntil = function(start, fn, context) {
191 if (start === null) {
192 return;
193 }
194
195 var item;
196
197 if (context === undefined) {
198 context = this;
199 }
200
201 // push cursor
202 var cursor = allocateCursor(this, null, start);
203
204 while (cursor.next !== null) {
205 item = cursor.next;
206 cursor.next = item.next;
207
208 if (fn.call(context, item.data, item, this)) {
209 break;
210 }
211 }
212
213 // pop cursor
214 releaseCursor(this);
215};
216
217List.prototype.prevUntil = function(start, fn, context) {
218 if (start === null) {
219 return;
220 }
221
222 var item;
223
224 if (context === undefined) {
225 context = this;
226 }
227
228 // push cursor
229 var cursor = allocateCursor(this, start, null);
230
231 while (cursor.prev !== null) {
232 item = cursor.prev;
233 cursor.prev = item.prev;
234
235 if (fn.call(context, item.data, item, this)) {
236 break;
237 }
238 }
239
240 // pop cursor
241 releaseCursor(this);
242};
243
244List.prototype.some = function(fn, context) {
245 var cursor = this.head;
246
247 if (context === undefined) {
248 context = this;
249 }
250
251 while (cursor !== null) {
252 if (fn.call(context, cursor.data, cursor, this)) {
253 return true;
254 }
255
256 cursor = cursor.next;
257 }
258
259 return false;
260};
261
262List.prototype.map = function(fn, context) {
263 var result = new List();
264 var cursor = this.head;
265
266 if (context === undefined) {
267 context = this;
268 }
269
270 while (cursor !== null) {
271 result.appendData(fn.call(context, cursor.data, cursor, this));
272 cursor = cursor.next;
273 }
274
275 return result;
276};
277
278List.prototype.filter = function(fn, context) {
279 var result = new List();
280 var cursor = this.head;
281
282 if (context === undefined) {
283 context = this;
284 }
285
286 while (cursor !== null) {
287 if (fn.call(context, cursor.data, cursor, this)) {
288 result.appendData(cursor.data);
289 }
290 cursor = cursor.next;
291 }
292
293 return result;
294};
295
296List.prototype.clear = function() {
297 this.head = null;
298 this.tail = null;
299};
300
301List.prototype.copy = function() {
302 var result = new List();
303 var cursor = this.head;
304
305 while (cursor !== null) {
306 result.insert(createItem(cursor.data));
307 cursor = cursor.next;
308 }
309
310 return result;
311};
312
313List.prototype.prepend = function(item) {
314 // head
315 // ^
316 // item
317 this.updateCursors(null, item, this.head, item);
318
319 // insert to the beginning of the list
320 if (this.head !== null) {
321 // new item <- first item
322 this.head.prev = item;
323
324 // new item -> first item
325 item.next = this.head;
326 } else {
327 // if list has no head, then it also has no tail
328 // in this case tail points to the new item
329 this.tail = item;
330 }
331
332 // head always points to new item
333 this.head = item;
334
335 return this;
336};
337
338List.prototype.prependData = function(data) {
339 return this.prepend(createItem(data));
340};
341
342List.prototype.append = function(item) {
343 return this.insert(item);
344};
345
346List.prototype.appendData = function(data) {
347 return this.insert(createItem(data));
348};
349
350List.prototype.insert = function(item, before) {
351 if (before !== undefined && before !== null) {
352 // prev before
353 // ^
354 // item
355 this.updateCursors(before.prev, item, before, item);
356
357 if (before.prev === null) {
358 // insert to the beginning of list
359 if (this.head !== before) {
360 throw new Error('before doesn\'t belong to list');
361 }
362
363 // since head points to before therefore list doesn't empty
364 // no need to check tail
365 this.head = item;
366 before.prev = item;
367 item.next = before;
368
369 this.updateCursors(null, item);
370 } else {
371
372 // insert between two items
373 before.prev.next = item;
374 item.prev = before.prev;
375
376 before.prev = item;
377 item.next = before;
378 }
379 } else {
380 // tail
381 // ^
382 // item
383 this.updateCursors(this.tail, item, null, item);
384
385 // insert to the ending of the list
386 if (this.tail !== null) {
387 // last item -> new item
388 this.tail.next = item;
389
390 // last item <- new item
391 item.prev = this.tail;
392 } else {
393 // if list has no tail, then it also has no head
394 // in this case head points to new item
395 this.head = item;
396 }
397
398 // tail always points to new item
399 this.tail = item;
400 }
401
402 return this;
403};
404
405List.prototype.insertData = function(data, before) {
406 return this.insert(createItem(data), before);
407};
408
409List.prototype.remove = function(item) {
410 // item
411 // ^
412 // prev next
413 this.updateCursors(item, item.prev, item, item.next);
414
415 if (item.prev !== null) {
416 item.prev.next = item.next;
417 } else {
418 if (this.head !== item) {
419 throw new Error('item doesn\'t belong to list');
420 }
421
422 this.head = item.next;
423 }
424
425 if (item.next !== null) {
426 item.next.prev = item.prev;
427 } else {
428 if (this.tail !== item) {
429 throw new Error('item doesn\'t belong to list');
430 }
431
432 this.tail = item.prev;
433 }
434
435 item.prev = null;
436 item.next = null;
437
438 return item;
439};
440
441List.prototype.push = function(data) {
442 this.insert(createItem(data));
443};
444
445List.prototype.pop = function() {
446 if (this.tail !== null) {
447 return this.remove(this.tail);
448 }
449};
450
451List.prototype.unshift = function(data) {
452 this.prepend(createItem(data));
453};
454
455List.prototype.shift = function() {
456 if (this.head !== null) {
457 return this.remove(this.head);
458 }
459};
460
461List.prototype.prependList = function(list) {
462 return this.insertList(list, this.head);
463};
464
465List.prototype.appendList = function(list) {
466 return this.insertList(list);
467};
468
469List.prototype.insertList = function(list, before) {
470 // ignore empty lists
471 if (list.head === null) {
472 return this;
473 }
474
475 if (before !== undefined && before !== null) {
476 this.updateCursors(before.prev, list.tail, before, list.head);
477
478 // insert in the middle of dist list
479 if (before.prev !== null) {
480 // before.prev <-> list.head
481 before.prev.next = list.head;
482 list.head.prev = before.prev;
483 } else {
484 this.head = list.head;
485 }
486
487 before.prev = list.tail;
488 list.tail.next = before;
489 } else {
490 this.updateCursors(this.tail, list.tail, null, list.head);
491
492 // insert to end of the list
493 if (this.tail !== null) {
494 // if destination list has a tail, then it also has a head,
495 // but head doesn't change
496
497 // dest tail -> source head
498 this.tail.next = list.head;
499
500 // dest tail <- source head
501 list.head.prev = this.tail;
502 } else {
503 // if list has no a tail, then it also has no a head
504 // in this case points head to new item
505 this.head = list.head;
506 }
507
508 // tail always start point to new item
509 this.tail = list.tail;
510 }
511
512 list.head = null;
513 list.tail = null;
514
515 return this;
516};
517
518List.prototype.replace = function(oldItem, newItemOrList) {
519 if ('head' in newItemOrList) {
520 this.insertList(newItemOrList, oldItem);
521 } else {
522 this.insert(newItemOrList, oldItem);
523 }
524
525 this.remove(oldItem);
526};
527
528module.exports = List;
Note: See TracBrowser for help on using the repository browser.