Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | */ |
michael@0 | 5 | "use strict"; |
michael@0 | 6 | |
michael@0 | 7 | module.metadata = { |
michael@0 | 8 | "stability": "experimental" |
michael@0 | 9 | }; |
michael@0 | 10 | |
michael@0 | 11 | // Disclamer: |
michael@0 | 12 | // In this module we'll have some common argument / variable names |
michael@0 | 13 | // to hint their type or behavior. |
michael@0 | 14 | // |
michael@0 | 15 | // - `f` stands for "function" that is intended to be side effect |
michael@0 | 16 | // free. |
michael@0 | 17 | // - `p` stands for "predicate" that is function which returns logical |
michael@0 | 18 | // true or false and is intended to be side effect free. |
michael@0 | 19 | // - `x` / `y` single item of the sequence. |
michael@0 | 20 | // - `xs` / `ys` sequence of `x` / `y` items where `x` / `y` signifies |
michael@0 | 21 | // type of the items in sequence, so sequence is not of the same item. |
michael@0 | 22 | // - `_` used for argument(s) or variable(s) who's values are ignored. |
michael@0 | 23 | |
michael@0 | 24 | const { complement, flip, identity } = require("../lang/functional"); |
michael@0 | 25 | const { iteratorSymbol } = require("../util/iteration"); |
michael@0 | 26 | const { isArray, isArguments, isMap, isSet, |
michael@0 | 27 | isString, isBoolean, isNumber } = require("../lang/type"); |
michael@0 | 28 | |
michael@0 | 29 | const Sequence = function Sequence(iterator) { |
michael@0 | 30 | if (iterator.isGenerator && iterator.isGenerator()) |
michael@0 | 31 | this[iteratorSymbol] = iterator; |
michael@0 | 32 | else |
michael@0 | 33 | throw TypeError("Expected generator argument"); |
michael@0 | 34 | }; |
michael@0 | 35 | exports.Sequence = Sequence; |
michael@0 | 36 | |
michael@0 | 37 | const polymorphic = dispatch => x => |
michael@0 | 38 | x === null ? dispatch.null(null) : |
michael@0 | 39 | x === void(0) ? dispatch.void(void(0)) : |
michael@0 | 40 | isArray(x) ? (dispatch.array || dispatch.indexed)(x) : |
michael@0 | 41 | isString(x) ? (dispatch.string || dispatch.indexed)(x) : |
michael@0 | 42 | isArguments(x) ? (dispatch.arguments || dispatch.indexed)(x) : |
michael@0 | 43 | isMap(x) ? dispatch.map(x) : |
michael@0 | 44 | isSet(x) ? dispatch.set(x) : |
michael@0 | 45 | isNumber(x) ? dispatch.number(x) : |
michael@0 | 46 | isBoolean(x) ? dispatch.boolean(x) : |
michael@0 | 47 | dispatch.default(x); |
michael@0 | 48 | |
michael@0 | 49 | const nogen = function*() {}; |
michael@0 | 50 | const empty = () => new Sequence(nogen); |
michael@0 | 51 | exports.empty = empty; |
michael@0 | 52 | |
michael@0 | 53 | const seq = polymorphic({ |
michael@0 | 54 | null: empty, |
michael@0 | 55 | void: empty, |
michael@0 | 56 | array: identity, |
michael@0 | 57 | string: identity, |
michael@0 | 58 | arguments: identity, |
michael@0 | 59 | map: identity, |
michael@0 | 60 | set: identity, |
michael@0 | 61 | default: x => x instanceof Sequence ? x : new Sequence(x) |
michael@0 | 62 | }); |
michael@0 | 63 | exports.seq = seq; |
michael@0 | 64 | |
michael@0 | 65 | |
michael@0 | 66 | |
michael@0 | 67 | |
michael@0 | 68 | // Function to cast seq to string. |
michael@0 | 69 | const string = (...etc) => "".concat(...etc); |
michael@0 | 70 | exports.string = string; |
michael@0 | 71 | |
michael@0 | 72 | // Function for casting seq to plain object. |
michael@0 | 73 | const object = (...pairs) => { |
michael@0 | 74 | let result = {}; |
michael@0 | 75 | for (let [key, value] of pairs) |
michael@0 | 76 | result[key] = value; |
michael@0 | 77 | |
michael@0 | 78 | return result; |
michael@0 | 79 | }; |
michael@0 | 80 | exports.object = object; |
michael@0 | 81 | |
michael@0 | 82 | // Takes `getEnumerator` function that returns `nsISimpleEnumerator` |
michael@0 | 83 | // and creates lazy sequence of it's items. Note that function does |
michael@0 | 84 | // not take `nsISimpleEnumerator` itslef because that would allow |
michael@0 | 85 | // single iteration, which would not be consistent with rest of the |
michael@0 | 86 | // lazy sequences. |
michael@0 | 87 | const fromEnumerator = getEnumerator => seq(function* () { |
michael@0 | 88 | const enumerator = getEnumerator(); |
michael@0 | 89 | while (enumerator.hasMoreElements()) |
michael@0 | 90 | yield enumerator.getNext(); |
michael@0 | 91 | }); |
michael@0 | 92 | exports.fromEnumerator = fromEnumerator; |
michael@0 | 93 | |
michael@0 | 94 | // Takes `object` and returns lazy sequence of own `[key, value]` |
michael@0 | 95 | // pairs (does not include inherited and non enumerable keys). |
michael@0 | 96 | const pairs = polymorphic({ |
michael@0 | 97 | null: empty, |
michael@0 | 98 | void: empty, |
michael@0 | 99 | map: identity, |
michael@0 | 100 | indexed: indexed => seq(function* () { |
michael@0 | 101 | const count = indexed.length; |
michael@0 | 102 | let index = 0; |
michael@0 | 103 | while (index < count) { |
michael@0 | 104 | yield [index, indexed[index]]; |
michael@0 | 105 | index = index + 1; |
michael@0 | 106 | } |
michael@0 | 107 | }), |
michael@0 | 108 | default: object => seq(function* () { |
michael@0 | 109 | for (let key of Object.keys(object)) |
michael@0 | 110 | yield [key, object[key]]; |
michael@0 | 111 | }) |
michael@0 | 112 | }); |
michael@0 | 113 | exports.pairs = pairs; |
michael@0 | 114 | |
michael@0 | 115 | |
michael@0 | 116 | const keys = polymorphic({ |
michael@0 | 117 | null: empty, |
michael@0 | 118 | void: empty, |
michael@0 | 119 | indexed: indexed => seq(function* () { |
michael@0 | 120 | const count = indexed.length; |
michael@0 | 121 | let index = 0; |
michael@0 | 122 | while (index < count) { |
michael@0 | 123 | yield index; |
michael@0 | 124 | index = index + 1; |
michael@0 | 125 | } |
michael@0 | 126 | }), |
michael@0 | 127 | map: map => seq(function* () { |
michael@0 | 128 | for (let [key, _] of map) |
michael@0 | 129 | yield key; |
michael@0 | 130 | }), |
michael@0 | 131 | default: object => seq(function* () { |
michael@0 | 132 | for (let key of Object.keys(object)) |
michael@0 | 133 | yield key; |
michael@0 | 134 | }) |
michael@0 | 135 | }); |
michael@0 | 136 | exports.keys = keys; |
michael@0 | 137 | |
michael@0 | 138 | |
michael@0 | 139 | const values = polymorphic({ |
michael@0 | 140 | null: empty, |
michael@0 | 141 | void: empty, |
michael@0 | 142 | set: identity, |
michael@0 | 143 | indexed: indexed => seq(function* () { |
michael@0 | 144 | const count = indexed.length; |
michael@0 | 145 | let index = 0; |
michael@0 | 146 | while (index < count) { |
michael@0 | 147 | yield indexed[index]; |
michael@0 | 148 | index = index + 1; |
michael@0 | 149 | } |
michael@0 | 150 | }), |
michael@0 | 151 | map: map => seq(function* () { |
michael@0 | 152 | for (let [_, value] of map) yield value; |
michael@0 | 153 | }), |
michael@0 | 154 | default: object => seq(function* () { |
michael@0 | 155 | for (let key of Object.keys(object)) yield object[key]; |
michael@0 | 156 | }) |
michael@0 | 157 | }); |
michael@0 | 158 | exports.values = values; |
michael@0 | 159 | |
michael@0 | 160 | |
michael@0 | 161 | |
michael@0 | 162 | // Returns a lazy sequence of `x`, `f(x)`, `f(f(x))` etc. |
michael@0 | 163 | // `f` must be free of side-effects. Note that returned |
michael@0 | 164 | // sequence is infinite so it must be consumed partially. |
michael@0 | 165 | // |
michael@0 | 166 | // Implements clojure iterate: |
michael@0 | 167 | // http://clojuredocs.org/clojure_core/clojure.core/iterate |
michael@0 | 168 | const iterate = (f, x) => seq(function* () { |
michael@0 | 169 | let state = x; |
michael@0 | 170 | while (true) { |
michael@0 | 171 | yield state; |
michael@0 | 172 | state = f(state); |
michael@0 | 173 | } |
michael@0 | 174 | }); |
michael@0 | 175 | exports.iterate = iterate; |
michael@0 | 176 | |
michael@0 | 177 | // Returns a lazy sequence of the items in sequence for which `p(item)` |
michael@0 | 178 | // returns `true`. `p` must be free of side-effects. |
michael@0 | 179 | // |
michael@0 | 180 | // Implements clojure filter: |
michael@0 | 181 | // http://clojuredocs.org/clojure_core/clojure.core/filter |
michael@0 | 182 | const filter = (p, sequence) => seq(function* () { |
michael@0 | 183 | if (sequence !== null && sequence !== void(0)) { |
michael@0 | 184 | for (let item of sequence) { |
michael@0 | 185 | if (p(item)) |
michael@0 | 186 | yield item; |
michael@0 | 187 | } |
michael@0 | 188 | } |
michael@0 | 189 | }); |
michael@0 | 190 | exports.filter = filter; |
michael@0 | 191 | |
michael@0 | 192 | // Returns a lazy sequence consisting of the result of applying `f` to the |
michael@0 | 193 | // set of first items of each sequence, followed by applying f to the set |
michael@0 | 194 | // of second items in each sequence, until any one of the sequences is |
michael@0 | 195 | // exhausted. Any remaining items in other sequences are ignored. Function |
michael@0 | 196 | // `f` should accept number-of-sequences arguments. |
michael@0 | 197 | // |
michael@0 | 198 | // Implements clojure map: |
michael@0 | 199 | // http://clojuredocs.org/clojure_core/clojure.core/map |
michael@0 | 200 | const map = (f, ...sequences) => seq(function* () { |
michael@0 | 201 | const count = sequences.length; |
michael@0 | 202 | // Optimize a single sequence case |
michael@0 | 203 | if (count === 1) { |
michael@0 | 204 | let [sequence] = sequences; |
michael@0 | 205 | if (sequence !== null && sequence !== void(0)) { |
michael@0 | 206 | for (let item of sequence) |
michael@0 | 207 | yield f(item); |
michael@0 | 208 | } |
michael@0 | 209 | } |
michael@0 | 210 | else { |
michael@0 | 211 | // define args array that will be recycled on each |
michael@0 | 212 | // step to aggregate arguments to be passed to `f`. |
michael@0 | 213 | let args = []; |
michael@0 | 214 | // define inputs to contain started generators. |
michael@0 | 215 | let inputs = []; |
michael@0 | 216 | |
michael@0 | 217 | let index = 0; |
michael@0 | 218 | while (index < count) { |
michael@0 | 219 | inputs[index] = sequences[index][iteratorSymbol](); |
michael@0 | 220 | index = index + 1; |
michael@0 | 221 | } |
michael@0 | 222 | |
michael@0 | 223 | // Run loop yielding of applying `f` to the set of |
michael@0 | 224 | // items at each step until one of the `inputs` is |
michael@0 | 225 | // exhausted. |
michael@0 | 226 | let done = false; |
michael@0 | 227 | while (!done) { |
michael@0 | 228 | let index = 0; |
michael@0 | 229 | let value = void(0); |
michael@0 | 230 | while (index < count && !done) { |
michael@0 | 231 | ({ done, value }) = inputs[index].next(); |
michael@0 | 232 | |
michael@0 | 233 | // If input is not exhausted yet store value in args. |
michael@0 | 234 | if (!done) { |
michael@0 | 235 | args[index] = value; |
michael@0 | 236 | index = index + 1; |
michael@0 | 237 | } |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | // If none of the inputs is exhasted yet, `args` contain items |
michael@0 | 241 | // from each input so we yield application of `f` over them. |
michael@0 | 242 | if (!done) |
michael@0 | 243 | yield f(...args); |
michael@0 | 244 | } |
michael@0 | 245 | } |
michael@0 | 246 | }); |
michael@0 | 247 | exports.map = map; |
michael@0 | 248 | |
michael@0 | 249 | // Returns a lazy sequence of the intermediate values of the reduction (as |
michael@0 | 250 | // per reduce) of sequence by `f`, starting with `initial` value if provided. |
michael@0 | 251 | // |
michael@0 | 252 | // Implements clojure reductions: |
michael@0 | 253 | // http://clojuredocs.org/clojure_core/clojure.core/reductions |
michael@0 | 254 | const reductions = (...params) => { |
michael@0 | 255 | const count = params.length; |
michael@0 | 256 | let hasInitial = false; |
michael@0 | 257 | let f, initial, source; |
michael@0 | 258 | if (count === 2) { |
michael@0 | 259 | ([f, source]) = params; |
michael@0 | 260 | } |
michael@0 | 261 | else if (count === 3) { |
michael@0 | 262 | ([f, initial, source]) = params; |
michael@0 | 263 | hasInitial = true; |
michael@0 | 264 | } |
michael@0 | 265 | else { |
michael@0 | 266 | throw Error("Invoked with wrong number of arguments: " + count); |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | const sequence = seq(source); |
michael@0 | 270 | |
michael@0 | 271 | return seq(function* () { |
michael@0 | 272 | let started = hasInitial; |
michael@0 | 273 | let result = void(0); |
michael@0 | 274 | |
michael@0 | 275 | // If initial is present yield it. |
michael@0 | 276 | if (hasInitial) |
michael@0 | 277 | yield (result = initial); |
michael@0 | 278 | |
michael@0 | 279 | // For each item of the sequence accumulate new result. |
michael@0 | 280 | for (let item of sequence) { |
michael@0 | 281 | // If nothing has being yield yet set result to first |
michael@0 | 282 | // item and yield it. |
michael@0 | 283 | if (!started) { |
michael@0 | 284 | started = true; |
michael@0 | 285 | yield (result = item); |
michael@0 | 286 | } |
michael@0 | 287 | // Otherwise accumulate new result and yield it. |
michael@0 | 288 | else { |
michael@0 | 289 | yield (result = f(result, item)); |
michael@0 | 290 | } |
michael@0 | 291 | } |
michael@0 | 292 | |
michael@0 | 293 | // If nothing has being yield yet it's empty sequence and no |
michael@0 | 294 | // `initial` was provided in which case we need to yield `f()`. |
michael@0 | 295 | if (!started) |
michael@0 | 296 | yield f(); |
michael@0 | 297 | }); |
michael@0 | 298 | }; |
michael@0 | 299 | exports.reductions = reductions; |
michael@0 | 300 | |
michael@0 | 301 | // `f` should be a function of 2 arguments. If `initial` is not supplied, |
michael@0 | 302 | // returns the result of applying `f` to the first 2 items in sequence, then |
michael@0 | 303 | // applying `f` to that result and the 3rd item, etc. If sequence contains no |
michael@0 | 304 | // items, `f` must accept no arguments as well, and reduce returns the |
michael@0 | 305 | // result of calling f with no arguments. If sequence has only 1 item, it |
michael@0 | 306 | // is returned and `f` is not called. If `initial` is supplied, returns the |
michael@0 | 307 | // result of applying `f` to `initial` and the first item in sequence, then |
michael@0 | 308 | // applying `f` to that result and the 2nd item, etc. If sequence contains no |
michael@0 | 309 | // items, returns `initial` and `f` is not called. |
michael@0 | 310 | // |
michael@0 | 311 | // Implements clojure reduce: |
michael@0 | 312 | // http://clojuredocs.org/clojure_core/clojure.core/reduce |
michael@0 | 313 | const reduce = (...args) => { |
michael@0 | 314 | const xs = reductions(...args); |
michael@0 | 315 | let x; |
michael@0 | 316 | for (x of xs) void(0); |
michael@0 | 317 | return x; |
michael@0 | 318 | }; |
michael@0 | 319 | exports.reduce = reduce; |
michael@0 | 320 | |
michael@0 | 321 | const each = (f, sequence) => { |
michael@0 | 322 | for (let x of seq(sequence)) void(f(x)); |
michael@0 | 323 | }; |
michael@0 | 324 | exports.each = each; |
michael@0 | 325 | |
michael@0 | 326 | |
michael@0 | 327 | const inc = x => x + 1; |
michael@0 | 328 | // Returns the number of items in the sequence. `count(null)` && `count()` |
michael@0 | 329 | // returns `0`. Also works on strings, arrays, Maps & Sets. |
michael@0 | 330 | |
michael@0 | 331 | // Implements clojure count: |
michael@0 | 332 | // http://clojuredocs.org/clojure_core/clojure.core/count |
michael@0 | 333 | const count = polymorphic({ |
michael@0 | 334 | null: _ => 0, |
michael@0 | 335 | void: _ => 0, |
michael@0 | 336 | indexed: indexed => indexed.length, |
michael@0 | 337 | map: map => map.size, |
michael@0 | 338 | set: set => set.size, |
michael@0 | 339 | default: xs => reduce(inc, 0, xs) |
michael@0 | 340 | }); |
michael@0 | 341 | exports.count = count; |
michael@0 | 342 | |
michael@0 | 343 | // Returns `true` if sequence has no items. |
michael@0 | 344 | |
michael@0 | 345 | // Implements clojure empty?: |
michael@0 | 346 | // http://clojuredocs.org/clojure_core/clojure.core/empty_q |
michael@0 | 347 | const isEmpty = sequence => { |
michael@0 | 348 | // Treat `null` and `undefined` as empty sequences. |
michael@0 | 349 | if (sequence === null || sequence === void(0)) |
michael@0 | 350 | return true; |
michael@0 | 351 | |
michael@0 | 352 | // If contains any item non empty so return `false`. |
michael@0 | 353 | for (let _ of sequence) |
michael@0 | 354 | return false; |
michael@0 | 355 | |
michael@0 | 356 | // If has not returned yet, there was nothing to iterate |
michael@0 | 357 | // so it's empty. |
michael@0 | 358 | return true; |
michael@0 | 359 | }; |
michael@0 | 360 | exports.isEmpty = isEmpty; |
michael@0 | 361 | |
michael@0 | 362 | const and = (a, b) => a && b; |
michael@0 | 363 | |
michael@0 | 364 | // Returns true if `p(x)` is logical `true` for every `x` in sequence, else |
michael@0 | 365 | // `false`. |
michael@0 | 366 | // |
michael@0 | 367 | // Implements clojure every?: |
michael@0 | 368 | // http://clojuredocs.org/clojure_core/clojure.core/every_q |
michael@0 | 369 | const isEvery = (p, sequence) => { |
michael@0 | 370 | if (sequence !== null && sequence !== void(0)) { |
michael@0 | 371 | for (let item of sequence) { |
michael@0 | 372 | if (!p(item)) |
michael@0 | 373 | return false; |
michael@0 | 374 | } |
michael@0 | 375 | } |
michael@0 | 376 | return true; |
michael@0 | 377 | }; |
michael@0 | 378 | exports.isEvery = isEvery; |
michael@0 | 379 | |
michael@0 | 380 | // Returns the first logical true value of (p x) for any x in sequence, |
michael@0 | 381 | // else `null`. |
michael@0 | 382 | // |
michael@0 | 383 | // Implements clojure some: |
michael@0 | 384 | // http://clojuredocs.org/clojure_core/clojure.core/some |
michael@0 | 385 | const some = (p, sequence) => { |
michael@0 | 386 | if (sequence !== null && sequence !== void(0)) { |
michael@0 | 387 | for (let item of sequence) { |
michael@0 | 388 | if (p(item)) |
michael@0 | 389 | return true; |
michael@0 | 390 | } |
michael@0 | 391 | } |
michael@0 | 392 | return null; |
michael@0 | 393 | }; |
michael@0 | 394 | exports.some = some; |
michael@0 | 395 | |
michael@0 | 396 | // Returns a lazy sequence of the first `n` items in sequence, or all items if |
michael@0 | 397 | // there are fewer than `n`. |
michael@0 | 398 | // |
michael@0 | 399 | // Implements clojure take: |
michael@0 | 400 | // http://clojuredocs.org/clojure_core/clojure.core/take |
michael@0 | 401 | const take = (n, sequence) => n <= 0 ? empty() : seq(function* () { |
michael@0 | 402 | let count = n; |
michael@0 | 403 | for (let item of sequence) { |
michael@0 | 404 | yield item; |
michael@0 | 405 | count = count - 1; |
michael@0 | 406 | if (count === 0) break; |
michael@0 | 407 | } |
michael@0 | 408 | }); |
michael@0 | 409 | exports.take = take; |
michael@0 | 410 | |
michael@0 | 411 | // Returns a lazy sequence of successive items from sequence while |
michael@0 | 412 | // `p(item)` returns `true`. `p` must be free of side-effects. |
michael@0 | 413 | // |
michael@0 | 414 | // Implements clojure take-while: |
michael@0 | 415 | // http://clojuredocs.org/clojure_core/clojure.core/take-while |
michael@0 | 416 | const takeWhile = (p, sequence) => seq(function* () { |
michael@0 | 417 | for (let item of sequence) { |
michael@0 | 418 | if (!p(item)) |
michael@0 | 419 | break; |
michael@0 | 420 | |
michael@0 | 421 | yield item; |
michael@0 | 422 | } |
michael@0 | 423 | }); |
michael@0 | 424 | exports.takeWhile = takeWhile; |
michael@0 | 425 | |
michael@0 | 426 | // Returns a lazy sequence of all but the first `n` items in |
michael@0 | 427 | // sequence. |
michael@0 | 428 | // |
michael@0 | 429 | // Implements clojure drop: |
michael@0 | 430 | // http://clojuredocs.org/clojure_core/clojure.core/drop |
michael@0 | 431 | const drop = (n, sequence) => seq(function* () { |
michael@0 | 432 | if (sequence !== null && sequence !== void(0)) { |
michael@0 | 433 | let count = n; |
michael@0 | 434 | for (let item of sequence) { |
michael@0 | 435 | if (count > 0) |
michael@0 | 436 | count = count - 1; |
michael@0 | 437 | else |
michael@0 | 438 | yield item; |
michael@0 | 439 | } |
michael@0 | 440 | } |
michael@0 | 441 | }); |
michael@0 | 442 | exports.drop = drop; |
michael@0 | 443 | |
michael@0 | 444 | // Returns a lazy sequence of the items in sequence starting from the |
michael@0 | 445 | // first item for which `p(item)` returns falsy value. |
michael@0 | 446 | // |
michael@0 | 447 | // Implements clojure drop-while: |
michael@0 | 448 | // http://clojuredocs.org/clojure_core/clojure.core/drop-while |
michael@0 | 449 | const dropWhile = (p, sequence) => seq(function* () { |
michael@0 | 450 | let keep = false; |
michael@0 | 451 | for (let item of sequence) { |
michael@0 | 452 | keep = keep || !p(item); |
michael@0 | 453 | if (keep) yield item; |
michael@0 | 454 | } |
michael@0 | 455 | }); |
michael@0 | 456 | exports.dropWhile = dropWhile; |
michael@0 | 457 | |
michael@0 | 458 | // Returns a lazy sequence representing the concatenation of the |
michael@0 | 459 | // suplied sequences. |
michael@0 | 460 | // |
michael@0 | 461 | // Implements clojure conact: |
michael@0 | 462 | // http://clojuredocs.org/clojure_core/clojure.core/concat |
michael@0 | 463 | const concat = (...sequences) => seq(function* () { |
michael@0 | 464 | for (let sequence of sequences) |
michael@0 | 465 | for (let item of sequence) |
michael@0 | 466 | yield item; |
michael@0 | 467 | }); |
michael@0 | 468 | exports.concat = concat; |
michael@0 | 469 | |
michael@0 | 470 | // Returns the first item in the sequence. |
michael@0 | 471 | // |
michael@0 | 472 | // Implements clojure first: |
michael@0 | 473 | // http://clojuredocs.org/clojure_core/clojure.core/first |
michael@0 | 474 | const first = sequence => { |
michael@0 | 475 | if (sequence !== null && sequence !== void(0)) { |
michael@0 | 476 | for (let item of sequence) |
michael@0 | 477 | return item; |
michael@0 | 478 | } |
michael@0 | 479 | return null; |
michael@0 | 480 | }; |
michael@0 | 481 | exports.first = first; |
michael@0 | 482 | |
michael@0 | 483 | // Returns a possibly empty sequence of the items after the first. |
michael@0 | 484 | // |
michael@0 | 485 | // Implements clojure rest: |
michael@0 | 486 | // http://clojuredocs.org/clojure_core/clojure.core/rest |
michael@0 | 487 | const rest = sequence => drop(1, sequence); |
michael@0 | 488 | exports.rest = rest; |
michael@0 | 489 | |
michael@0 | 490 | // Returns the value at the index. Returns `notFound` or `undefined` |
michael@0 | 491 | // if index is out of bounds. |
michael@0 | 492 | const nth = (xs, n, notFound) => { |
michael@0 | 493 | if (n >= 0) { |
michael@0 | 494 | if (isArray(xs) || isArguments(xs) || isString(xs)) { |
michael@0 | 495 | return n < xs.length ? xs[n] : notFound; |
michael@0 | 496 | } |
michael@0 | 497 | else if (xs !== null && xs !== void(0)) { |
michael@0 | 498 | let count = n; |
michael@0 | 499 | for (let x of xs) { |
michael@0 | 500 | if (count <= 0) |
michael@0 | 501 | return x; |
michael@0 | 502 | |
michael@0 | 503 | count = count - 1; |
michael@0 | 504 | } |
michael@0 | 505 | } |
michael@0 | 506 | } |
michael@0 | 507 | return notFound; |
michael@0 | 508 | }; |
michael@0 | 509 | exports.nth = nth; |
michael@0 | 510 | |
michael@0 | 511 | // Return the last item in sequence, in linear time. |
michael@0 | 512 | // If `sequence` is an array or string or arguments |
michael@0 | 513 | // returns in constant time. |
michael@0 | 514 | // Implements clojure last: |
michael@0 | 515 | // http://clojuredocs.org/clojure_core/clojure.core/last |
michael@0 | 516 | const last = polymorphic({ |
michael@0 | 517 | null: _ => null, |
michael@0 | 518 | void: _ => null, |
michael@0 | 519 | indexed: indexed => indexed[indexed.length - 1], |
michael@0 | 520 | map: xs => reduce((_, x) => x, xs), |
michael@0 | 521 | set: xs => reduce((_, x) => x, xs), |
michael@0 | 522 | default: xs => reduce((_, x) => x, xs) |
michael@0 | 523 | }); |
michael@0 | 524 | exports.last = last; |
michael@0 | 525 | |
michael@0 | 526 | // Return a lazy sequence of all but the last `n` (default 1) items |
michael@0 | 527 | // from the give `xs`. |
michael@0 | 528 | // |
michael@0 | 529 | // Implements clojure drop-last: |
michael@0 | 530 | // http://clojuredocs.org/clojure_core/clojure.core/drop-last |
michael@0 | 531 | const dropLast = flip((xs, n=1) => seq(function* () { |
michael@0 | 532 | let ys = []; |
michael@0 | 533 | for (let x of xs) { |
michael@0 | 534 | ys.push(x); |
michael@0 | 535 | if (ys.length > n) |
michael@0 | 536 | yield ys.shift(); |
michael@0 | 537 | } |
michael@0 | 538 | })); |
michael@0 | 539 | exports.dropLast = dropLast; |
michael@0 | 540 | |
michael@0 | 541 | // Returns a lazy sequence of the elements of `xs` with duplicates |
michael@0 | 542 | // removed |
michael@0 | 543 | // |
michael@0 | 544 | // Implements clojure distinct |
michael@0 | 545 | // http://clojuredocs.org/clojure_core/clojure.core/distinct |
michael@0 | 546 | const distinct = sequence => seq(function* () { |
michael@0 | 547 | let items = new Set(); |
michael@0 | 548 | for (let item of sequence) { |
michael@0 | 549 | if (!items.has(item)) { |
michael@0 | 550 | items.add(item); |
michael@0 | 551 | yield item; |
michael@0 | 552 | } |
michael@0 | 553 | } |
michael@0 | 554 | }); |
michael@0 | 555 | exports.distinct = distinct; |
michael@0 | 556 | |
michael@0 | 557 | // Returns a lazy sequence of the items in `xs` for which |
michael@0 | 558 | // `p(x)` returns false. `p` must be free of side-effects. |
michael@0 | 559 | // |
michael@0 | 560 | // Implements clojure remove |
michael@0 | 561 | // http://clojuredocs.org/clojure_core/clojure.core/remove |
michael@0 | 562 | const remove = (p, xs) => filter(complement(p), xs); |
michael@0 | 563 | exports.remove = remove; |
michael@0 | 564 | |
michael@0 | 565 | // Returns the result of applying concat to the result of |
michael@0 | 566 | // `map(f, xs)`. Thus function `f` should return a sequence. |
michael@0 | 567 | // |
michael@0 | 568 | // Implements clojure mapcat |
michael@0 | 569 | // http://clojuredocs.org/clojure_core/clojure.core/mapcat |
michael@0 | 570 | const mapcat = (f, sequence) => seq(function* () { |
michael@0 | 571 | const sequences = map(f, sequence); |
michael@0 | 572 | for (let sequence of sequences) |
michael@0 | 573 | for (let item of sequence) |
michael@0 | 574 | yield item; |
michael@0 | 575 | }); |
michael@0 | 576 | exports.mapcat = mapcat; |