• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2module.exports = Yallist
3
4Yallist.Node = Node
5Yallist.create = Yallist
6
7function Yallist (list) {
8  var self = this
9  if (!(self instanceof Yallist)) {
10    self = new Yallist()
11  }
12
13  self.tail = null
14  self.head = null
15  self.length = 0
16
17  if (list && typeof list.forEach === 'function') {
18    list.forEach(function (item) {
19      self.push(item)
20    })
21  } else if (arguments.length > 0) {
22    for (var i = 0, l = arguments.length; i < l; i++) {
23      self.push(arguments[i])
24    }
25  }
26
27  return self
28}
29
30Yallist.prototype.removeNode = function (node) {
31  if (node.list !== this) {
32    throw new Error('removing node which does not belong to this list')
33  }
34
35  var next = node.next
36  var prev = node.prev
37
38  if (next) {
39    next.prev = prev
40  }
41
42  if (prev) {
43    prev.next = next
44  }
45
46  if (node === this.head) {
47    this.head = next
48  }
49  if (node === this.tail) {
50    this.tail = prev
51  }
52
53  node.list.length--
54  node.next = null
55  node.prev = null
56  node.list = null
57
58  return next
59}
60
61Yallist.prototype.unshiftNode = function (node) {
62  if (node === this.head) {
63    return
64  }
65
66  if (node.list) {
67    node.list.removeNode(node)
68  }
69
70  var head = this.head
71  node.list = this
72  node.next = head
73  if (head) {
74    head.prev = node
75  }
76
77  this.head = node
78  if (!this.tail) {
79    this.tail = node
80  }
81  this.length++
82}
83
84Yallist.prototype.pushNode = function (node) {
85  if (node === this.tail) {
86    return
87  }
88
89  if (node.list) {
90    node.list.removeNode(node)
91  }
92
93  var tail = this.tail
94  node.list = this
95  node.prev = tail
96  if (tail) {
97    tail.next = node
98  }
99
100  this.tail = node
101  if (!this.head) {
102    this.head = node
103  }
104  this.length++
105}
106
107Yallist.prototype.push = function () {
108  for (var i = 0, l = arguments.length; i < l; i++) {
109    push(this, arguments[i])
110  }
111  return this.length
112}
113
114Yallist.prototype.unshift = function () {
115  for (var i = 0, l = arguments.length; i < l; i++) {
116    unshift(this, arguments[i])
117  }
118  return this.length
119}
120
121Yallist.prototype.pop = function () {
122  if (!this.tail) {
123    return undefined
124  }
125
126  var res = this.tail.value
127  this.tail = this.tail.prev
128  if (this.tail) {
129    this.tail.next = null
130  } else {
131    this.head = null
132  }
133  this.length--
134  return res
135}
136
137Yallist.prototype.shift = function () {
138  if (!this.head) {
139    return undefined
140  }
141
142  var res = this.head.value
143  this.head = this.head.next
144  if (this.head) {
145    this.head.prev = null
146  } else {
147    this.tail = null
148  }
149  this.length--
150  return res
151}
152
153Yallist.prototype.forEach = function (fn, thisp) {
154  thisp = thisp || this
155  for (var walker = this.head, i = 0; walker !== null; i++) {
156    fn.call(thisp, walker.value, i, this)
157    walker = walker.next
158  }
159}
160
161Yallist.prototype.forEachReverse = function (fn, thisp) {
162  thisp = thisp || this
163  for (var walker = this.tail, i = this.length - 1; walker !== null; i--) {
164    fn.call(thisp, walker.value, i, this)
165    walker = walker.prev
166  }
167}
168
169Yallist.prototype.get = function (n) {
170  for (var i = 0, walker = this.head; walker !== null && i < n; i++) {
171    // abort out of the list early if we hit a cycle
172    walker = walker.next
173  }
174  if (i === n && walker !== null) {
175    return walker.value
176  }
177}
178
179Yallist.prototype.getReverse = function (n) {
180  for (var i = 0, walker = this.tail; walker !== null && i < n; i++) {
181    // abort out of the list early if we hit a cycle
182    walker = walker.prev
183  }
184  if (i === n && walker !== null) {
185    return walker.value
186  }
187}
188
189Yallist.prototype.map = function (fn, thisp) {
190  thisp = thisp || this
191  var res = new Yallist()
192  for (var walker = this.head; walker !== null;) {
193    res.push(fn.call(thisp, walker.value, this))
194    walker = walker.next
195  }
196  return res
197}
198
199Yallist.prototype.mapReverse = function (fn, thisp) {
200  thisp = thisp || this
201  var res = new Yallist()
202  for (var walker = this.tail; walker !== null;) {
203    res.push(fn.call(thisp, walker.value, this))
204    walker = walker.prev
205  }
206  return res
207}
208
209Yallist.prototype.reduce = function (fn, initial) {
210  var acc
211  var walker = this.head
212  if (arguments.length > 1) {
213    acc = initial
214  } else if (this.head) {
215    walker = this.head.next
216    acc = this.head.value
217  } else {
218    throw new TypeError('Reduce of empty list with no initial value')
219  }
220
221  for (var i = 0; walker !== null; i++) {
222    acc = fn(acc, walker.value, i)
223    walker = walker.next
224  }
225
226  return acc
227}
228
229Yallist.prototype.reduceReverse = function (fn, initial) {
230  var acc
231  var walker = this.tail
232  if (arguments.length > 1) {
233    acc = initial
234  } else if (this.tail) {
235    walker = this.tail.prev
236    acc = this.tail.value
237  } else {
238    throw new TypeError('Reduce of empty list with no initial value')
239  }
240
241  for (var i = this.length - 1; walker !== null; i--) {
242    acc = fn(acc, walker.value, i)
243    walker = walker.prev
244  }
245
246  return acc
247}
248
249Yallist.prototype.toArray = function () {
250  var arr = new Array(this.length)
251  for (var i = 0, walker = this.head; walker !== null; i++) {
252    arr[i] = walker.value
253    walker = walker.next
254  }
255  return arr
256}
257
258Yallist.prototype.toArrayReverse = function () {
259  var arr = new Array(this.length)
260  for (var i = 0, walker = this.tail; walker !== null; i++) {
261    arr[i] = walker.value
262    walker = walker.prev
263  }
264  return arr
265}
266
267Yallist.prototype.slice = function (from, to) {
268  to = to || this.length
269  if (to < 0) {
270    to += this.length
271  }
272  from = from || 0
273  if (from < 0) {
274    from += this.length
275  }
276  var ret = new Yallist()
277  if (to < from || to < 0) {
278    return ret
279  }
280  if (from < 0) {
281    from = 0
282  }
283  if (to > this.length) {
284    to = this.length
285  }
286  for (var i = 0, walker = this.head; walker !== null && i < from; i++) {
287    walker = walker.next
288  }
289  for (; walker !== null && i < to; i++, walker = walker.next) {
290    ret.push(walker.value)
291  }
292  return ret
293}
294
295Yallist.prototype.sliceReverse = function (from, to) {
296  to = to || this.length
297  if (to < 0) {
298    to += this.length
299  }
300  from = from || 0
301  if (from < 0) {
302    from += this.length
303  }
304  var ret = new Yallist()
305  if (to < from || to < 0) {
306    return ret
307  }
308  if (from < 0) {
309    from = 0
310  }
311  if (to > this.length) {
312    to = this.length
313  }
314  for (var i = this.length, walker = this.tail; walker !== null && i > to; i--) {
315    walker = walker.prev
316  }
317  for (; walker !== null && i > from; i--, walker = walker.prev) {
318    ret.push(walker.value)
319  }
320  return ret
321}
322
323Yallist.prototype.splice = function (start, deleteCount, ...nodes) {
324  if (start > this.length) {
325    start = this.length - 1
326  }
327  if (start < 0) {
328    start = this.length + start;
329  }
330
331  for (var i = 0, walker = this.head; walker !== null && i < start; i++) {
332    walker = walker.next
333  }
334
335  var ret = []
336  for (var i = 0; walker && i < deleteCount; i++) {
337    ret.push(walker.value)
338    walker = this.removeNode(walker)
339  }
340  if (walker === null) {
341    walker = this.tail
342  }
343
344  if (walker !== this.head && walker !== this.tail) {
345    walker = walker.prev
346  }
347
348  for (var i = 0; i < nodes.length; i++) {
349    walker = insert(this, walker, nodes[i])
350  }
351  return ret;
352}
353
354Yallist.prototype.reverse = function () {
355  var head = this.head
356  var tail = this.tail
357  for (var walker = head; walker !== null; walker = walker.prev) {
358    var p = walker.prev
359    walker.prev = walker.next
360    walker.next = p
361  }
362  this.head = tail
363  this.tail = head
364  return this
365}
366
367function insert (self, node, value) {
368  var inserted = node === self.head ?
369    new Node(value, null, node, self) :
370    new Node(value, node, node.next, self)
371
372  if (inserted.next === null) {
373    self.tail = inserted
374  }
375  if (inserted.prev === null) {
376    self.head = inserted
377  }
378
379  self.length++
380
381  return inserted
382}
383
384function push (self, item) {
385  self.tail = new Node(item, self.tail, null, self)
386  if (!self.head) {
387    self.head = self.tail
388  }
389  self.length++
390}
391
392function unshift (self, item) {
393  self.head = new Node(item, null, self.head, self)
394  if (!self.tail) {
395    self.tail = self.head
396  }
397  self.length++
398}
399
400function Node (value, prev, next, list) {
401  if (!(this instanceof Node)) {
402    return new Node(value, prev, next, list)
403  }
404
405  this.list = list
406  this.value = value
407
408  if (prev) {
409    prev.next = this
410    this.prev = prev
411  } else {
412    this.prev = null
413  }
414
415  if (next) {
416    next.prev = this
417    this.next = next
418  } else {
419    this.next = null
420  }
421}
422
423try {
424  // add if support for Symbol.iterator is present
425  require('./iterator.js')(Yallist)
426} catch (er) {}
427