• 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
59Yallist.prototype.unshiftNode = function (node) {
60  if (node === this.head) {
61    return
62  }
63
64  if (node.list) {
65    node.list.removeNode(node)
66  }
67
68  var head = this.head
69  node.list = this
70  node.next = head
71  if (head) {
72    head.prev = node
73  }
74
75  this.head = node
76  if (!this.tail) {
77    this.tail = node
78  }
79  this.length++
80}
81
82Yallist.prototype.pushNode = function (node) {
83  if (node === this.tail) {
84    return
85  }
86
87  if (node.list) {
88    node.list.removeNode(node)
89  }
90
91  var tail = this.tail
92  node.list = this
93  node.prev = tail
94  if (tail) {
95    tail.next = node
96  }
97
98  this.tail = node
99  if (!this.head) {
100    this.head = node
101  }
102  this.length++
103}
104
105Yallist.prototype.push = function () {
106  for (var i = 0, l = arguments.length; i < l; i++) {
107    push(this, arguments[i])
108  }
109  return this.length
110}
111
112Yallist.prototype.unshift = function () {
113  for (var i = 0, l = arguments.length; i < l; i++) {
114    unshift(this, arguments[i])
115  }
116  return this.length
117}
118
119Yallist.prototype.pop = function () {
120  if (!this.tail) {
121    return undefined
122  }
123
124  var res = this.tail.value
125  this.tail = this.tail.prev
126  if (this.tail) {
127    this.tail.next = null
128  } else {
129    this.head = null
130  }
131  this.length--
132  return res
133}
134
135Yallist.prototype.shift = function () {
136  if (!this.head) {
137    return undefined
138  }
139
140  var res = this.head.value
141  this.head = this.head.next
142  if (this.head) {
143    this.head.prev = null
144  } else {
145    this.tail = null
146  }
147  this.length--
148  return res
149}
150
151Yallist.prototype.forEach = function (fn, thisp) {
152  thisp = thisp || this
153  for (var walker = this.head, i = 0; walker !== null; i++) {
154    fn.call(thisp, walker.value, i, this)
155    walker = walker.next
156  }
157}
158
159Yallist.prototype.forEachReverse = function (fn, thisp) {
160  thisp = thisp || this
161  for (var walker = this.tail, i = this.length - 1; walker !== null; i--) {
162    fn.call(thisp, walker.value, i, this)
163    walker = walker.prev
164  }
165}
166
167Yallist.prototype.get = function (n) {
168  for (var i = 0, walker = this.head; walker !== null && i < n; i++) {
169    // abort out of the list early if we hit a cycle
170    walker = walker.next
171  }
172  if (i === n && walker !== null) {
173    return walker.value
174  }
175}
176
177Yallist.prototype.getReverse = function (n) {
178  for (var i = 0, walker = this.tail; walker !== null && i < n; i++) {
179    // abort out of the list early if we hit a cycle
180    walker = walker.prev
181  }
182  if (i === n && walker !== null) {
183    return walker.value
184  }
185}
186
187Yallist.prototype.map = function (fn, thisp) {
188  thisp = thisp || this
189  var res = new Yallist()
190  for (var walker = this.head; walker !== null;) {
191    res.push(fn.call(thisp, walker.value, this))
192    walker = walker.next
193  }
194  return res
195}
196
197Yallist.prototype.mapReverse = function (fn, thisp) {
198  thisp = thisp || this
199  var res = new Yallist()
200  for (var walker = this.tail; walker !== null;) {
201    res.push(fn.call(thisp, walker.value, this))
202    walker = walker.prev
203  }
204  return res
205}
206
207Yallist.prototype.reduce = function (fn, initial) {
208  var acc
209  var walker = this.head
210  if (arguments.length > 1) {
211    acc = initial
212  } else if (this.head) {
213    walker = this.head.next
214    acc = this.head.value
215  } else {
216    throw new TypeError('Reduce of empty list with no initial value')
217  }
218
219  for (var i = 0; walker !== null; i++) {
220    acc = fn(acc, walker.value, i)
221    walker = walker.next
222  }
223
224  return acc
225}
226
227Yallist.prototype.reduceReverse = function (fn, initial) {
228  var acc
229  var walker = this.tail
230  if (arguments.length > 1) {
231    acc = initial
232  } else if (this.tail) {
233    walker = this.tail.prev
234    acc = this.tail.value
235  } else {
236    throw new TypeError('Reduce of empty list with no initial value')
237  }
238
239  for (var i = this.length - 1; walker !== null; i--) {
240    acc = fn(acc, walker.value, i)
241    walker = walker.prev
242  }
243
244  return acc
245}
246
247Yallist.prototype.toArray = function () {
248  var arr = new Array(this.length)
249  for (var i = 0, walker = this.head; walker !== null; i++) {
250    arr[i] = walker.value
251    walker = walker.next
252  }
253  return arr
254}
255
256Yallist.prototype.toArrayReverse = function () {
257  var arr = new Array(this.length)
258  for (var i = 0, walker = this.tail; walker !== null; i++) {
259    arr[i] = walker.value
260    walker = walker.prev
261  }
262  return arr
263}
264
265Yallist.prototype.slice = function (from, to) {
266  to = to || this.length
267  if (to < 0) {
268    to += this.length
269  }
270  from = from || 0
271  if (from < 0) {
272    from += this.length
273  }
274  var ret = new Yallist()
275  if (to < from || to < 0) {
276    return ret
277  }
278  if (from < 0) {
279    from = 0
280  }
281  if (to > this.length) {
282    to = this.length
283  }
284  for (var i = 0, walker = this.head; walker !== null && i < from; i++) {
285    walker = walker.next
286  }
287  for (; walker !== null && i < to; i++, walker = walker.next) {
288    ret.push(walker.value)
289  }
290  return ret
291}
292
293Yallist.prototype.sliceReverse = function (from, to) {
294  to = to || this.length
295  if (to < 0) {
296    to += this.length
297  }
298  from = from || 0
299  if (from < 0) {
300    from += this.length
301  }
302  var ret = new Yallist()
303  if (to < from || to < 0) {
304    return ret
305  }
306  if (from < 0) {
307    from = 0
308  }
309  if (to > this.length) {
310    to = this.length
311  }
312  for (var i = this.length, walker = this.tail; walker !== null && i > to; i--) {
313    walker = walker.prev
314  }
315  for (; walker !== null && i > from; i--, walker = walker.prev) {
316    ret.push(walker.value)
317  }
318  return ret
319}
320
321Yallist.prototype.reverse = function () {
322  var head = this.head
323  var tail = this.tail
324  for (var walker = head; walker !== null; walker = walker.prev) {
325    var p = walker.prev
326    walker.prev = walker.next
327    walker.next = p
328  }
329  this.head = tail
330  this.tail = head
331  return this
332}
333
334function push (self, item) {
335  self.tail = new Node(item, self.tail, null, self)
336  if (!self.head) {
337    self.head = self.tail
338  }
339  self.length++
340}
341
342function unshift (self, item) {
343  self.head = new Node(item, null, self.head, self)
344  if (!self.tail) {
345    self.tail = self.head
346  }
347  self.length++
348}
349
350function Node (value, prev, next, list) {
351  if (!(this instanceof Node)) {
352    return new Node(value, prev, next, list)
353  }
354
355  this.list = list
356  this.value = value
357
358  if (prev) {
359    prev.next = this
360    this.prev = prev
361  } else {
362    this.prev = null
363  }
364
365  if (next) {
366    next.prev = this
367    this.next = next
368  } else {
369    this.next = null
370  }
371}
372
373try {
374  // add if support for Symbol.iterator is present
375  require('./iterator.js')(Yallist)
376} catch (er) {}
377