mirror of
				https://github.com/flynx/types.js.git
				synced 2025-10-31 11:30:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2841 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			2841 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # types.js
 | |
| 
 | |
| Library of JavaScript type extensions, types and utilities.
 | |
| 
 | |
| - [types.js](#typesjs)
 | |
|   - [Installation](#installation)
 | |
|   - [Basic usage](#basic-usage)
 | |
|   - [`Object`](#object)
 | |
|     - [`Object.deepKeys(..)`](#objectdeepkeys)
 | |
|     - [`Object.copy(..)` (EXPERIMENTAL)](#objectcopy-experimental)
 | |
|     - [`Object.flatCopy(..)`](#objectflatcopy)
 | |
|     - [`Object.match(..)`](#objectmatch)
 | |
|     - [`Object.matchPartial(..)`](#objectmatchpartial)
 | |
|     - [`<object>.run(..)`](#objectrun)
 | |
|     - [`Object.sort(..)`](#objectsort)
 | |
|   - [`Array`](#array)
 | |
|     - [`<array>.first(..)` / `<array>.last(..)`](#arrayfirst--arraylast)
 | |
|     - [`<array>.rol(..)`](#arrayrol)
 | |
|     - [`<array>.compact()`](#arraycompact)
 | |
|     - [`<array>.len`](#arraylen)
 | |
|     - [`<array>.unique()` / `<array>.tailUnique()`](#arrayunique--arraytailunique)
 | |
|     - [`<array>.trim()` / `<array>.trimStart()` / `<array>.trimEnd()`](#arraytrim--arraytrimstart--arraytrimend)
 | |
|     - [`<array>.cmp(..)`](#arraycmp)
 | |
|     - [`<array>.setCmp(..)`](#arraysetcmp)
 | |
|     - [`<array>.sortAs(..)`](#arraysortas)
 | |
|     - [`<array>.inplaceSortAs(..)`](#arrayinplacesortas)
 | |
|     - [`<array>.toKeys(..)`](#arraytokeys)
 | |
|     - [`<array>.toMap(..)`](#arraytomap)
 | |
|     - [`Array.zip(..)` / `<array>.zip(..)`](#arrayzip--arrayzip)
 | |
|     - [`Array.iter(..)` / `<array>.iter()`](#arrayiter--arrayiter)
 | |
|     - [Abortable `Array` iteration](#abortable-array-iteration)
 | |
|       - [`array.STOP` / `array.STOP(..)`](#arraystop--arraystop)
 | |
|       - [`<array>.smap(..)` / `<array>.sfilter(..)` / `<array>.sreduce(..)` / `<array>.sforEach(..)`](#arraysmap--arraysfilter--arraysreduce--arraysforeach)
 | |
|     - [Large `Array` iteration (chunked)](#large-array-iteration-chunked)
 | |
|       - [`array.STOP` / `array.STOP(..)`](#arraystop--arraystop-1)
 | |
|       - [`<array>.CHUNK_SIZE`](#arraychunk_size)
 | |
|       - [`<array>.mapChunks(..)` / `<array>.filterChunks(..)` / `<array>.reduceChunks(..)`](#arraymapchunks--arrayfilterchunks--arrayreducechunks)
 | |
|   - [`Map`](#map)
 | |
|     - [`<map>.replaceKey(..)`](#mapreplacekey)
 | |
|     - [`<map>.sort(..)`](#mapsort)
 | |
|   - [`Set`](#set)
 | |
|     - [`<set>.unite(..)`](#setunite)
 | |
|     - [`<set>.intersect(..)`](#setintersect)
 | |
|     - [`<set>.subtract(..)`](#setsubtract)
 | |
|     - [`<set>.splice(..)`](#setsplice)
 | |
|     - [`<set>.replace(..)`](#setreplace)
 | |
|     - [`<set>.replaceAt(..)`](#setreplaceat)
 | |
|     - [`<set>.sort(..)`](#setsort)
 | |
|   - [`Date`](#date)
 | |
|     - [`Date.timeStamp(..)`](#datetimestamp)
 | |
|     - [`Date.fromTimeStamp(..)`](#datefromtimestamp)
 | |
|     - [`Date.str2ms(..)`](#datestr2ms)
 | |
|     - [`<date>.toShortDate(..)`](#datetoshortdate)
 | |
|     - [`<date>.getTimeStamp(..)`](#dategettimestamp)
 | |
|     - [`<date>.setTimeStamp(..)`](#datesettimestamp)
 | |
|   - [`String`](#string)
 | |
|     - [`<string>.capitalize()`](#stringcapitalize)
 | |
|     - [`<string>.indent(..)`](#stringindent)
 | |
|   - [`RegExp`](#regexp)
 | |
|     - [`RegExp.quoteRegExp(..)`](#regexpquoteregexp)
 | |
|   - [`Promise`](#promise)
 | |
|     - [Interactive promises](#interactive-promises)
 | |
|       - [`Promise.interactive(..)`](#promiseinteractive)
 | |
|       - [`<promise-inter>.send(..)`](#promise-intersend)
 | |
|       - [`<promise-inter>.then(..)`](#promise-interthen)
 | |
|     - [Cooperative promises](#cooperative-promises)
 | |
|       - [`Promise.cooperative()`](#promisecooperative)
 | |
|       - [`<promise-coop>.set(..)`](#promise-coopset)
 | |
|       - [`<promise-coop>.isSet`](#promise-coopisset)
 | |
|       - [`<promise-coop>.then(..)`](#promise-coopthen)
 | |
|     - [Promise iteration](#promise-iteration)
 | |
|       - [`Promise.iter(..)` / `promise.IterablePromise(..)`](#promiseiter--promiseiterablepromise)
 | |
|       - [`<promise>.iter()`](#promiseiter)
 | |
|       - [`<promise-iter>.iter()`](#promise-iteriter)
 | |
|       - [`<promise-iter>.map(..)` / `<promise-iter>.filter(..)` / `<promise-iter>.reduce(..)`](#promise-itermap--promise-iterfilter--promise-iterreduce)
 | |
|       - [`<promise-iter>.flat(..)`](#promise-iterflat)
 | |
|       - [`<promise-iter>.reverse()`](#promise-iterreverse)
 | |
|       - [`<promise-iter>.concat(..)`](#promise-iterconcat)
 | |
|       - [`<promise-iter>.push(..)` / `<promise-iter>.unshift(..)`](#promise-iterpush--promise-iterunshift)
 | |
|       - [`<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`](#promise-iterthen--promise-itercatch--promise-iterfinally)
 | |
|       - [Advanced handler](#advanced-handler)
 | |
|     - [Promise proxies](#promise-proxies)
 | |
|       - [`<promise>.as(..)`](#promiseas)
 | |
|       - [`<promise-proxy>.<method>(..)`](#promise-proxymethod)
 | |
|   - [Generator extensions and utilities](#generator-extensions-and-utilities)
 | |
|     - [The basics](#the-basics)
 | |
|       - [`generator.Generator`](#generatorgenerator)
 | |
|       - [`generator.iter(..)`](#generatoriter)
 | |
|       - [`generator.STOP`](#generatorstop)
 | |
|     - [Generator instance iteration](#generator-instance-iteration)
 | |
|       - [`<generator>.iter()`](#generatoriter-1)
 | |
|       - [`<generator>.map(..)` / `<generator>.filter(..)`](#generatormap--generatorfilter)
 | |
|       - [`<generator>.reduce(..)` / `<generator>.greduce(..)`](#generatorreduce--generatorgreduce)
 | |
|       - [`<generator>.slice(..)`](#generatorslice)
 | |
|       - [`<generator>.at(..)` / `<generator>.gat(..)`](#generatorat--generatorgat)
 | |
|       - [`<generator>.flat(..)`](#generatorflat)
 | |
|       - [`<generator>.shift()` / `<generator>.pop()` / `<generator>.gshift()` / `<generator>.gpop()`](#generatorshift--generatorpop--generatorgshift--generatorgpop)
 | |
|       - [`<generator>.unshift(..)` / `<generator>.push(..)`](#generatorunshift--generatorpush)
 | |
|       - [`<generator>.promise()`](#generatorpromise)
 | |
|       - [`<generator>.then(..)` / `<generator>.catch(..)` / `<generator>.finally(..)`](#generatorthen--generatorcatch--generatorfinally)
 | |
|       - [`<generator>.toArray()`](#generatortoarray)
 | |
|     - [Treating iterators the same as generators](#treating-iterators-the-same-as-generators)
 | |
|     - [Generator constructor iteration](#generator-constructor-iteration)
 | |
|       - [`<Generator>.iter(..)`](#generatoriter-2)
 | |
|       - [`<Generator>.at(..)` / `<Generator>.gat(..)`](#generatorat--generatorgat-1)
 | |
|       - [`<Generator>.shift()` / `<Generator>.pop()` / `<Generator>.gshift()` / `<Generator>.gpop()`](#generatorshift--generatorpop--generatorgshift--generatorgpop-1)
 | |
|       - [`<generator>.unshift(..)` / `<generator>.push(..)`](#generatorunshift--generatorpush-1)
 | |
|       - [`<Generator>.slice(..)`](#generatorslice-1)
 | |
|       - [`<Generator>.map(..)` / `<Generator>.filter(..)` / `<Generator>.reduce(..)` / `<Generator>.flat()`](#generatormap--generatorfilter--generatorreduce--generatorflat)
 | |
|       - [`<Generator>.toArray()`](#generatortoarray-1)
 | |
|       - [`<Generator>.then(..)` / `<Generator>.catch(..)` / `<Generator>.finally(..)`](#generatorthen--generatorcatch--generatorfinally-1)
 | |
|     - [Generator combinators](#generator-combinators)
 | |
|       - [`<Generator>.chain(..)` / `<generator>.chain(..)`](#generatorchain--generatorchain)
 | |
|       - [`<Generator>.concat(..)` / `<generator>.concat(..)`](#generatorconcat--generatorconcat)
 | |
|     - [Generator library](#generator-library)
 | |
|       - [`generator.range(..)`](#generatorrange)
 | |
|       - [`generator.repeat(..)`](#generatorrepeat)
 | |
|       - [`generator.produce(..)`](#generatorproduce)
 | |
|     - [Generator helpers](#generator-helpers)
 | |
|       - [`generator.stoppable(..)`](#generatorstoppable)
 | |
|   - [Containers](#containers)
 | |
|     - [`containers.UniqueKeyMap()` (`Map`)](#containersuniquekeymap-map)
 | |
|       - [`<unique-key-map>.set(..)`](#unique-key-mapset)
 | |
|       - [`<unique-key-map>.reset(..)`](#unique-key-mapreset)
 | |
|       - [`<unique-key-map>.rename(..)`](#unique-key-maprename)
 | |
|       - [`<unique-key-map>.orderedRename(..)`](#unique-key-maporderedrename)
 | |
|       - [`<unique-key-map>.unorderedRename(..)`](#unique-key-mapunorderedrename)
 | |
|       - [`<unique-key-map>.keysOf(..)`](#unique-key-mapkeysof)
 | |
|       - [`<unique-key-map>.originalKey(..)`](#unique-key-maporiginalkey)
 | |
|       - [`<unique-key-map>.uniqueKey(..)`](#unique-key-mapuniquekey)
 | |
|       - [`<unique-key-map>.__key_pattern__`](#unique-key-map__key_pattern__)
 | |
|       - [`<unique-key-map>.__unordered_rename__`](#unique-key-map__unordered_rename__)
 | |
|   - [Event](#event)
 | |
|     - [`event.Eventfull(..)`](#eventeventfull)
 | |
|     - [`event.Event(..)`](#eventevent)
 | |
|     - [`event.TRIGGER`](#eventtrigger)
 | |
|     - [`event.EventHandlerMixin`](#eventeventhandlermixin)
 | |
|       - [`<obj>.on(..)`](#objon)
 | |
|       - [`<obj>.one(..)`](#objone)
 | |
|       - [`<obj>.off(..)`](#objoff)
 | |
|       - [`<obj>.trigger(..)`](#objtrigger)
 | |
|     - [`event.EventDocMixin`](#eventeventdocmixin)
 | |
|       - [`<obj>.eventfull`](#objeventfull)
 | |
|       - [`<obj>.events`](#objevents)
 | |
|     - [`event.EventMixin`](#eventeventmixin)
 | |
|   - [Runner](#runner)
 | |
|     - [Micro task queue](#micro-task-queue)
 | |
|       - [`runner.STOP`](#runnerstop)
 | |
|       - [`runner.SKIP`](#runnerskip)
 | |
|       - [`Queue(..)` / `Queue.runTasks(..)`](#queue--queueruntasks)
 | |
|       - [`Queue.handle(..)`](#queuehandle)
 | |
|       - [`<queue>.state`](#queuestate)
 | |
|       - [`<queue>.start(..)`](#queuestart)
 | |
|       - [`<queue>.stop(..)`](#queuestop)
 | |
|       - [`<queue>.runTask(..)`](#queueruntask)
 | |
|       - [`<queue>.tasksAdded(..)` (event)](#queuetasksadded-event)
 | |
|       - [`<queue>.taskStarting(..)` (event)](#queuetaskstarting-event)
 | |
|       - [`<queue>.taskFailed(..)` (event)](#queuetaskfailed-event)
 | |
|       - [`<queue>.taskCompleted(..)` (event)](#queuetaskcompleted-event)
 | |
|       - [`<queue>.queueEmpty(..)` (event)](#queuequeueempty-event)
 | |
|       - [`<queue>.prioritize(..)`](#queueprioritize)
 | |
|       - [`<queue>.delay(..)`](#queuedelay)
 | |
|       - [`<queue>.add(..)`](#queueadd)
 | |
|       - [`<queue>.clear(..)`](#queueclear)
 | |
|       - [`FinalizableQueue(..)` / `FinalizableQueue.runTasks(..)` (Queue)](#finalizablequeue--finalizablequeueruntasks-queue)
 | |
|       - [`<finalizable-queue>.done(..)` (event/method)](#finalizable-queuedone-eventmethod)
 | |
|       - [`<finalizable-queue>.abort(..)` (event/method)](#finalizable-queueabort-eventmethod)
 | |
|       - [`<finalizable-queue>.promise(..)`](#finalizable-queuepromise)
 | |
|       - [`<finalizable-queue>.then(..)`](#finalizable-queuethen)
 | |
|       - [`<finalizable-queue>.catch(..)`](#finalizable-queuecatch)
 | |
|     - [Large task management](#large-task-management)
 | |
|       - [`runner.TaskManager(..)`](#runnertaskmanager)
 | |
|       - [`<task-manager>.Task(..)`](#task-managertask)
 | |
|       - [`<task-manager>.sync_start`](#task-managersync_start)
 | |
|       - [`<task-manager>.record_times`](#task-managerrecord_times)
 | |
|       - [`<task-manager>.titled(..)`](#task-managertitled)
 | |
|       - [`<task-manager>.send(..)`](#task-managersend)
 | |
|       - [`<task-manager>.stop(..)`](#task-managerstop)
 | |
|       - [`<task-manager>.done(..)` (event)](#task-managerdone-event)
 | |
|       - [`<task-manager>.error(..)` (event)](#task-managererror-event)
 | |
|       - [`<task-manager>.tasksDone(..)` (event)](#task-managertasksdone-event)
 | |
|       - [`runner.TaskTicket(..)`](#runnertaskticket)
 | |
|       - [`runner.TaskMixin(..)`](#runnertaskmixin)
 | |
|   - [License](#license)
 | |
| 
 | |
| ## Installation
 | |
| 
 | |
| ```shell
 | |
| $ npm install -s 'ig-types'
 | |
| ```
 | |
| 
 | |
| 
 | |
| ## Basic usage
 | |
| 
 | |
| To extend everything:
 | |
| ```javascript
 | |
| require('ig-types')
 | |
| ```
 | |
| 
 | |
| To have access to additional library types and utilities:
 | |
| ```javascript
 | |
| var types = require('ig-types')
 | |
| ```
 | |
| 
 | |
| `types.js` is organized so as to be able to import/extend only specific 
 | |
| sub-modules mostly independently so...
 | |
| 
 | |
| In case there is a need to only extend a specific constructor just import 
 | |
| the module dealing with that constructor (`Array` in this case):
 | |
| ```javascript
 | |
| // require `ig-types/<constructor-name>`...
 | |
| require('ig-types/Array')
 | |
| ```
 | |
| Note that type patching modules are _mostly_ independent.
 | |
| 
 | |
| And to import specific library modules only:
 | |
| ```javascript
 | |
| var containers = require('ig-types/containers')
 | |
| ```
 | |
| 
 | |
| 
 | |
| ## `Object`
 | |
| 
 | |
| ```javascript
 | |
| require('ig-types/Object')
 | |
| ```
 | |
| 
 | |
| Note that this module imports from 
 | |
| [`object.js`](https://github.com/flynx/object.js) and 
 | |
| [`object-run.js`](https://github.com/flynx/object-run.js),
 | |
| see those modules for more details.
 | |
| 
 | |
| 
 | |
| ### `Object.deepKeys(..)`
 | |
| 
 | |
| Get list of keys from all objects in the prototype chain.
 | |
| ```bnf
 | |
| Object.deepKeys(<obj>)
 | |
|     -> <keys>
 | |
| ```
 | |
| 
 | |
| This is different from `Object.keys(..)` which only gets _own_ keys from the 
 | |
| current object.
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| var a = { x: 123 }
 | |
| var b = Object.create(a)
 | |
| b.y = 321
 | |
| 
 | |
| // get own keys of b...
 | |
| Object.keys(b) // -> ['y']
 | |
| 
 | |
| // get all keys accessible from b...
 | |
| Object.deepKeys(b) // -> ['x', 'y']
 | |
| ```
 | |
| 
 | |
| For more details see:
 | |
| https://github.com/flynx/object.js#deepkeys
 | |
| 
 | |
| 
 | |
| ### `Object.copy(..)` (EXPERIMENTAL)
 | |
| 
 | |
| Create a copy of `<obj>`
 | |
| ```bnf
 | |
| Object.copy(<obj>)
 | |
|     -> <obj-copy>
 | |
| 
 | |
| Object.copy(<obj>, <constructor>)
 | |
|     -> <obj-copy>
 | |
| ```
 | |
| 
 | |
| This will:
 | |
| - create a blank `<obj-copy>`
 | |
| - link `<obj-copy>` to the same prototype chain
 | |
| - assign all _own_ keys from `<obj>` to `<obj-copy>`
 | |
| 
 | |
| This is similar to `Object.clone(..)` but instead of creating a new descendant of 
 | |
| the input object with no data this will instead create a new sibling with a copy
 | |
| of the instance data.
 | |
| 
 | |
| `<constructor>` if given is called to create the instance to be populated, 
 | |
| otherwise `Object.create(<obj>)` is used.
 | |
| 
 | |
| Note that `.assign(..)` is used to copy data, thus properties will be copied as values, to copy instance properties use `object.js`'s 
 | |
| [`.mixinFlat(..)`](https://github.com/flynx/object.js#mixinflat).
 | |
| 
 | |
| Note that this will make no attempt to clone object type, a `<constructor>` 
 | |
| should be passed manually if any instance type other that `Object` is required.
 | |
| 
 | |
| 
 | |
| ### `Object.flatCopy(..)`
 | |
| 
 | |
| Copy all attributes from the prototype chain of `<obj>` into `<new-obj>`.
 | |
| ```bnf
 | |
| Object.flatCopy(<obj>)
 | |
|     -> <new-obj>
 | |
| 
 | |
| Object.flatCopy(<obj>, <constructor>)
 | |
|     -> <new-obj>
 | |
| ```
 | |
| 
 | |
| This is different to [`.copy(..)`](#objectcopy-experimental) in that if 
 | |
| no `<constructor>` is given `<new-obj>` will _not_ be linked into the 
 | |
| prototype chain of `<obj>`, if this behavior is desired use `o => Object.create(o)`
 | |
| as the `<constructor>`.
 | |
| 
 | |
| 
 | |
| ### `Object.match(..)`
 | |
| 
 | |
| Attribute/value match two objects (non-recursive).
 | |
| ```bnf
 | |
| Object.match(<object>, <other>)
 | |
|     -> <bool>
 | |
| ```
 | |
| 
 | |
| Objects `A` and `B` match iff:
 | |
| - `A` and `B` are _identical_, i.e. `A === B`
 | |
| 
 | |
| or
 | |
| - `typeof A == typeof B` _and_,
 | |
| - `A` and `B` have the same number of attributes _and_,
 | |
| - attribute names match _and_,
 | |
| - attribute values are _identical_.
 | |
| 
 | |
| And for a less strict match:
 | |
| ```bnf
 | |
| Object.match(<object>, <other>, true)
 | |
|     -> <bool>
 | |
| ```
 | |
| Like the default case but uses _equality_ instead of _identity_ to match values.
 | |
| 
 | |
| 
 | |
| For more details see:
 | |
| https://github.com/flynx/object.js#match
 | |
| 
 | |
| <!-- 
 | |
| XXX should this test based on equality or on identity by default??? 
 | |
|     ...see: Array.cmp(..)
 | |
| -->
 | |
| 
 | |
| 
 | |
| ### `Object.matchPartial(..)`
 | |
| 
 | |
| ```bnf
 | |
| Object.matchPartial(<object>, <other>)
 | |
|     -> <bool>
 | |
| 
 | |
| Object.matchPartial(<object>, <other>, true)
 | |
|     -> <bool>
 | |
| ```
 | |
| Like `.match(..)` but will check for a _partial_ match, i.e. when `<other>` is a non-strict subset of `<object>`.
 | |
| 
 | |
| For more details see:
 | |
| https://github.com/flynx/object.js#matchpartial
 | |
| 
 | |
| <!-- 
 | |
| XXX should this test based on equality or on identity by default??? 
 | |
|     ...see: Array.cmp(..)
 | |
| -->
 | |
| 
 | |
| 
 | |
| ### `<object>.run(..)`
 | |
| 
 | |
| ```bnf
 | |
| <object>.run(<func>)
 | |
|     -> <object>
 | |
|     -> <other>
 | |
| ```
 | |
| 
 | |
| Run a function in the context of `<object>` returning either `<object>` 
 | |
| itself (if returning `undefined`) or the result.
 | |
| 
 | |
| Note that this is accessible from all JavaScript non-primitive objects, 
 | |
| i.e. everything that inherits from `Object`.
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| var L = [1, 2, 3]
 | |
|     .map(function(e){
 | |
|         return e * 2 })
 | |
|     // see if the first element is 1 and prepend 1 if it is not...
 | |
|     .run(function(){
 | |
|         if(this[0] != 1){
 | |
|             this.unshift(1) } })
 | |
| 
 | |
| console.log(L) // -> [1, 2, 6, 8]
 | |
| ```
 | |
| 
 | |
| `.run(..)` is also available standalone via:
 | |
| 
 | |
| ```shell
 | |
| $ npm install -s object-run
 | |
| ```
 | |
| 
 | |
| For more details see:  
 | |
| https://github.com/flynx/object-run.js
 | |
| 
 | |
| 
 | |
| ### `Object.sort(..)`
 | |
| 
 | |
| Sort `<obj>` attributes (similar to `Array`'s `.sort(..)`)
 | |
| ```bnf
 | |
| Object.sort(<obj>)
 | |
|     -> <obj>
 | |
| ```
 | |
| 
 | |
| Sort `<obj>` attributes via `<cmp>` function.
 | |
| ```
 | |
| Object.sort(<obj>, <cmp>)
 | |
|     -> <obj>
 | |
| ```
 | |
| 
 | |
| Sort `<obj>` attributes to the same order of `<order-list>`.
 | |
| ```bnf
 | |
| Object.sort(<obj>, <order-list>)
 | |
|     -> <obj>
 | |
| ```
 | |
| 
 | |
| Note that this rewrites all the keys of `<obj>` thus for very large
 | |
| sets of keys/attributes this may be quite expensive.
 | |
| 
 | |
| Note that some keys of `Object` may misbehave in JavaScript, currently keys
 | |
| that are string values of numbers are sorted automatically by _number value_
 | |
| and are not affected by `.sort(..)`, this affects both _Chrome_ and _Firefox_.
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| var o = {x: 0, a: 1, '100':2, '0':3, ' 27 ':4, b:5}
 | |
| 
 | |
| // notice that the order is already different to the order of attributes above...
 | |
| Object.keys(o) 
 | |
| //    -> ['0', '100', 'x', 'a', ' 27 ', 'b']
 | |
| 
 | |
| // '0' and '100' are not affected by .sort(..) while ' 27 ' is...
 | |
| Object.keys(Object.sort(o, ['x', 'a', '100'])) 
 | |
| //    -> [ '0', '100', 'x', 'a', ' 27 ', 'b' ]
 | |
| ```
 | |
| 
 | |
| This is similar to [`<map>.sort(..)`](#mapsort) and [`<ser>.sort(..)`](#setsort).
 | |
| 
 | |
| 
 | |
| ## `Array`
 | |
| 
 | |
| ```javascript
 | |
| require('ig-types/Array')
 | |
| ```
 | |
| or
 | |
| ```javascript
 | |
| var array = require('ig-types/Array')
 | |
| ```
 | |
| 
 | |
| ### `<array>.first(..)` / `<array>.last(..)`
 | |
| 
 | |
| Get the first/last items of `<array>`.
 | |
| ```bnf
 | |
| <array>.first()
 | |
|     -> <item>
 | |
| 
 | |
| <array>.last()
 | |
|     -> <item>
 | |
| ```
 | |
| 
 | |
| Set the first/last items of `<array>`.
 | |
| ```bnf
 | |
| <array>.first(<item>)
 | |
|   -> <array>
 | |
| 
 | |
| <array>.last(<item>)
 | |
|   -> <array>
 | |
| ```
 | |
| 
 | |
| Note that these do not affect `<array>` length unless setting items on 
 | |
| an empty `<array>`.
 | |
| 
 | |
| 
 | |
| ### `<array>.rol(..)`
 | |
| 
 | |
| Roll `<array>` in-place left.
 | |
| ```bnf
 | |
| <array>.rol()
 | |
| <array>.rol(1)
 | |
|     -> <array>
 | |
| 
 | |
| <array>.rol(n)
 | |
|     -> <array>
 | |
| ```
 | |
| 
 | |
| To roll _right_ pass a negative `n` to `.rol(..)`.
 | |
| 
 | |
| 
 | |
| ### `<array>.compact()`
 | |
| 
 | |
| ```bnf
 | |
| <array>.compact()
 | |
|     -> <compact-array>
 | |
| ```
 | |
| 
 | |
| Generate a compact `<array>` from a sparse `<array>`, i.e. removing all
 | |
| the empty slots.
 | |
| 
 | |
| 
 | |
| ### `<array>.len`
 | |
| 
 | |
| Number of non-empty slots/elements in `<array>`.
 | |
| 
 | |
| This is similar to:
 | |
| ```javascript
 | |
| var L = [,,, 1,, 2, 3,,]
 | |
| 
 | |
| // this is the same as L.len...
 | |
| L.compact().length 
 | |
| ```
 | |
| 
 | |
| Note that this is different from `.length` in that writing to `.len` has
 | |
| no effect.
 | |
| 
 | |
| 
 | |
| ### `<array>.unique()` / `<array>.tailUnique()`
 | |
| 
 | |
| Generate an array with all duplicate elements removed.
 | |
| ```bnf
 | |
| <array>.unique()
 | |
|     -> <array>
 | |
| 
 | |
| <array>.tailUnique()
 | |
|     -> <array>
 | |
| ```
 | |
| 
 | |
| The difference between the two versions is in that `.unique(..)` keeps the 
 | |
| first occurrence of a value while `.tailUnique(..)` keeps the last.
 | |
| 
 | |
| 
 | |
| ### `<array>.trim()` / `<array>.trimStart()` / `<array>.trimEnd()`
 | |
| 
 | |
| Copy array removing empty slots from array start, end or both.
 | |
| ```bnf
 | |
| <array>.trim()
 | |
|     -> <array>
 | |
| 
 | |
| <array>.trimStart()
 | |
|     -> <array>
 | |
|     
 | |
| <array>.trimEnd()
 | |
|     -> <array>
 | |
| ```
 | |
| 
 | |
| This is similar to `String`'s equivalent methods but removing _empty_ slots 
 | |
| instead of spaces.
 | |
| 
 | |
| 
 | |
| ### `<array>.cmp(..)`
 | |
| 
 | |
| Compare two arrays.
 | |
| ```bnf
 | |
| <array>.cmp(<other>)
 | |
|     -> <bool>
 | |
| ```
 | |
| 
 | |
| This will return `true` if:
 | |
| - `<array> === <other>` 
 | |
| 
 | |
| or
 | |
| - lengths are the same and,
 | |
| - values on the same positions are equal.
 | |
| 
 | |
| <!-- 
 | |
| XXX should this test based on equality or on identity by default??? 
 | |
|     ...see: Object.match(..)
 | |
| -->
 | |
| 
 | |
| 
 | |
| ### `<array>.setCmp(..)`
 | |
| 
 | |
| Compare to arrays ignoring element order and count.
 | |
| ```bnf
 | |
| <array>.setCmp(<other>)
 | |
|     -> <bool>
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<array>.sortAs(..)`
 | |
| 
 | |
| Sort array as a different array.
 | |
| ```bnf
 | |
| <array>.sortAs(<other>)
 | |
|     -> <array>
 | |
| ```
 | |
| 
 | |
| Elements not present in `<other>` retain their relative order and are
 | |
| placed after the sorted elements.
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| var L = [1, 2, 3, 4, 5, 6]
 | |
| var O = [5, 3, 1, 0]
 | |
| 
 | |
| L.sortAs(O) // -> [5, 3, 1, 2, 4, 6]
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<array>.inplaceSortAs(..)`
 | |
| 
 | |
| Sort array as a different array keeping positions of unsorted elements.
 | |
| ```bnf
 | |
| <array>.inplaceSortAs(<other>)
 | |
|     -> <array>
 | |
| ```
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| var L = [1, 2, 3, 4, 5, 6]
 | |
| var O = [5, 3, 1, 0]
 | |
| 
 | |
| L.inplaceSortAs(O) // -> [5, 2, 3, 4, 1, 6]
 | |
| ```
 | |
| 
 | |
| ### `<array>.toKeys(..)`
 | |
| 
 | |
| Create an object with array values as keys and index as value.
 | |
| ```bnf
 | |
| <array>.toKeys()
 | |
|     -> <object>
 | |
| ```
 | |
| 
 | |
| Normalize resulting `<object>` keys:
 | |
| ```bnf
 | |
| <array>.toKeys(<normalize>)
 | |
|     -> <object>
 | |
| 
 | |
| <normalize>(<elem>, <index>)
 | |
|     -> <key>
 | |
| ```
 | |
| 
 | |
| If `<array>` contains the same value multiple times it will be written 
 | |
| to `<object>` only once with the last occurrences' index.
 | |
| 
 | |
| Since `object` keys can only be `string`s array items that are not
 | |
| strings will be converted to strings. If this is not desired use `.toMap(..)`
 | |
| instead.
 | |
| 
 | |
| 
 | |
| ### `<array>.toMap(..)`
 | |
| 
 | |
| Create a map with array values as keys and index as value.
 | |
| ```bnf
 | |
| <array>.toMap()
 | |
|     -> <map>
 | |
| ```
 | |
| 
 | |
| Normalize resulting `<map>` keys:
 | |
| ```bnf
 | |
| <array>.toMap(<normalize>)
 | |
|     -> <map>
 | |
| 
 | |
| <normalize>(<elem>, <index>)
 | |
|     -> <key>
 | |
| ```
 | |
| 
 | |
| Note that if `<array>` contains the same value multiple times it will be used 
 | |
| as key only once and retain the last occurrences' index.
 | |
| 
 | |
| 
 | |
| ### `Array.zip(..)` / `<array>.zip(..)`
 | |
| 
 | |
| _Zip_ input array items.
 | |
| ```bnf
 | |
| Array.zip(<array>, <array>, ..)
 | |
|     -> <array>
 | |
| 
 | |
| <array>.zip(<array>, <array>, ..)
 | |
|     -> <array>
 | |
| ```
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| var A = [1, 2, 3]
 | |
| var B = ['a', 'b', 'c', 'd']
 | |
| 
 | |
| Array.zip(A, B) // -> [[1, 'a'], [2, 'b'], [3, 'c'], [, 'd']]
 | |
| ```
 | |
| 
 | |
| Array _sparseness_ is retained -- if one of the arrays has an empty slot, or is 
 | |
| not long enough, the corresponding spot in the result will be empty.
 | |
| 
 | |
| Resulting array length is strictly equal to the longest input array length.
 | |
| 
 | |
| 
 | |
| ### `Array.iter(..)` / `<array>.iter()`
 | |
| 
 | |
| Return an iterator/generator from the current array.
 | |
| 
 | |
| This is mostly useful in combination with the [Generator extensions and utilities](#generator-extensions-and-utilities)
 | |
| 
 | |
| 
 | |
| ### Abortable `Array` iteration
 | |
| 
 | |
| A an alternative to `Array`'s `.map(..)` / `.filter(..)` / .. methods with ability to
 | |
| stop the iteration process by `throw`ing `STOP` or `STOP(<value>)`.
 | |
| 
 | |
| ```javascript
 | |
| var {STOP} = require('ig-types/Array')
 | |
| ```
 | |
| 
 | |
| This can be used in two ways:
 | |
| 
 | |
| 1) `throw` as-is to simply stop...
 | |
|     ```javascript
 | |
|     ;[1,2,3,4,5]
 | |
|         .smap(function(e){ 
 | |
|             // simply abort here and now...
 | |
|             throw STOP })
 | |
|     ```
 | |
|     Since we aborted the iteration without passing any arguments to `STOP`,
 | |
|     `.smap(..)` will return `undefined`.
 | |
| 
 | |
| 2) `throw` an instance and return the argument...
 | |
|     ```javascript
 | |
|     // this will print "4" -- the value passed to STOP...
 | |
|     console.log([1,2,3,4,5]
 | |
|         .smap(function(e){ 
 | |
|             if(e > 3){
 | |
|               // NOTE: new is optional here...
 | |
|               //    ...StopIteratiom is an object.js constructor.
 | |
|               throw new STOP(e) } }))
 | |
|     ```
 | |
| 
 | |
| Note that no partial result is returned unless passed through `STOP(..)`.
 | |
| 
 | |
| 
 | |
| #### `array.STOP` / `array.STOP(..)`
 | |
| 
 | |
| An _object/constructor_ that if raised (as an exception) while iterating via 
 | |
| a supporting iterator method will abort further execution and correctly exit.
 | |
| 
 | |
| 
 | |
| #### `<array>.smap(..)` / `<array>.sfilter(..)` / `<array>.sreduce(..)` / `<array>.sforEach(..)`
 | |
| 
 | |
| Like `Array`'s `.map(..)`, `.filter(..)`, `.reduce(..)` and `.forEach(..)` but 
 | |
| with added support for aborting iteration by throwing `STOP` or `STOP(<value>)`.
 | |
| 
 | |
| 
 | |
| ### Large `Array` iteration (chunked)
 | |
| 
 | |
| Iterating over very large `Array` instances in JavaScript can block execution,
 | |
| to avoid this `types.js` implements `.map(..)`/`.filter(..)`/`.reduce(..)`
 | |
| equivalent methods that iterate the array in chunks and do it asynchronously 
 | |
| giving the runtime a chance to run in between.
 | |
| 
 | |
| In the simplest cases these are almost a drop-in replacements for the equivalent
 | |
| methods but return a promise.
 | |
| ```javascript
 | |
| var a = [1,2,3,4,5]
 | |
|     .map(function(e){ 
 | |
|         return e*2 })
 | |
| 
 | |
| var b
 | |
| ;[1,2,3,4,5]
 | |
|     .mapChunks(function(e){ 
 | |
|         return e*2 })
 | |
|     .then(function(res){
 | |
|         b = res })
 | |
| 
 | |
| // or with await...
 | |
| var c = await [1,2,3,4,5]
 | |
|     .mapChunks(function(e){ 
 | |
|         return e*2 })
 | |
| ```
 | |
| 
 | |
| These support setting the chunk size (default: `50`) as the first argument:
 | |
| ```javascript
 | |
| var c = await [1,2,3,4,5]
 | |
|     .mapChunks(2, function(e){ 
 | |
|         return e*2 })
 | |
| ```
 | |
| 
 | |
| #### `array.STOP` / `array.STOP(..)`
 | |
| 
 | |
| Like for [`<array>.smap(..)` and friends](#abortable-array-iteration) iteration 
 | |
| can be stopped by throwing a `array.STOP` / `array.STOP(<value>)` and as before 
 | |
| there are two ways to go:
 | |
| 
 | |
| 1) `throw` as-is to simply stop
 | |
|     ```javascript
 | |
|     ;[1,2,3,4,5]
 | |
|         .mapChunks(function(e){ 
 | |
|             // simply abort here and now...
 | |
|             throw STOP })
 | |
|         .catch(function(){
 | |
|             console.log('done.') })
 | |
|     ```
 | |
| 
 | |
| 2) `Throw` an instance and pass a value to `.catch(..)`
 | |
|    ```javascript
 | |
|     ;[1,2,3,4,5]
 | |
|         .mapChunks(function(e){ 
 | |
|             if(e > 3){
 | |
|               // NOTE: new is optional here...
 | |
|               //    ...StopIteratiom is an object.js constructor.
 | |
|               throw new STOP(e) } })
 | |
|         .catch(function(e){
 | |
|             console.log('first value greater than 3:', e) })
 | |
|    ```
 | |
| 
 | |
| 
 | |
| #### `<array>.CHUNK_SIZE`
 | |
| 
 | |
| The default iteration chunk size.
 | |
| 
 | |
| Note that the smaller this is the more _responsive_ the code is, especially 
 | |
| in UI applications but there is a small overhead added per chunk.
 | |
| 
 | |
| Default value: `50`
 | |
| 
 | |
| 
 | |
| #### `<array>.mapChunks(..)` / `<array>.filterChunks(..)` / `<array>.reduceChunks(..)`
 | |
| 
 | |
| The `.map(..)`, `.filter(..)` and `.reduce(..)` alternatives respectively:
 | |
| ```bnf
 | |
| <array>.mapChunks(<func>)
 | |
| <array>.mapChunks(<chunk-size>, <func>)
 | |
|   -> <promise>
 | |
| 
 | |
| <func>(<item>, <index>, <array>)
 | |
|   -> <new-item>
 | |
| ```
 | |
| 
 | |
| ```bnf
 | |
| <array>.filterChunks(<func>)
 | |
| <array>.filterChunks(<chunk-size>, <func>)
 | |
|   -> <promise>
 | |
| 
 | |
| <func>(<item>, <index>, <array>)
 | |
|   -> <bool>
 | |
| ```
 | |
| 
 | |
| ```bnf
 | |
| <array>.reduceChunks(<func>, <state>)
 | |
| <array>.mreduceChunks(<chunk-size>, <func>, <state>)
 | |
|   -> <promise>
 | |
| 
 | |
| <func>(<state>, <item>, <index>, <array>)
 | |
|   -> <state>
 | |
| ```
 | |
| <!-- XXX Example -->
 | |
| 
 | |
| 
 | |
| 
 | |
| All three support chunk handlers in the same way (illustrated on `.mapChunks(..)`):
 | |
| ```bnf
 | |
| <array>.mapChunks([<func>, <chunk-handler>])
 | |
| <array>.mapChunks(<chunk-size>, [<func>, <chunk-handler>])
 | |
|   -> <promise>
 | |
| 
 | |
| <func>(<item>, <index>, <array>)
 | |
|   -> <new-item>
 | |
| 
 | |
| <chunk-handler>(<chunk>, <result>, <offset>)
 | |
| ```
 | |
| 
 | |
| The `<chunk-handler>` gets the completed chunk of data after it is computed 
 | |
| but before the timeout.
 | |
| 
 | |
| <!-- XXX Example -->
 | |
| 
 | |
| 
 | |
| 
 | |
| ## `Map`
 | |
| 
 | |
| ```javascript
 | |
| require('ig-types/Map')
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<map>.replaceKey(..)`
 | |
| 
 | |
| Replace key in map retaining item order
 | |
| ```bnf
 | |
| <map>.replaceKey(<old>, <new>)
 | |
| <map>.replaceKey(<old>, <new>, true)
 | |
|     -> <map>
 | |
| ```
 | |
| 
 | |
| Replace the key without sorting
 | |
| ```bnf
 | |
| <map>.replaceKey(<old>, <new>, false)
 | |
|     -> <map>
 | |
| ```
 | |
| 
 | |
| Note that when sorting large maps this can get expensive.
 | |
| 
 | |
| 
 | |
| 
 | |
| ### `<map>.sort(..)`
 | |
| 
 | |
| Sort `<map>` keys in-place
 | |
| ```bnf
 | |
| <map>.sort()
 | |
|     -> <map>
 | |
| 
 | |
| <map>.sort(<cmp>)
 | |
|     -> <map>
 | |
| ```
 | |
| 
 | |
| In the general case this is similar to 
 | |
| [`<array>.sort(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 
 | |
| with the addition of the [`<array>.sortAs(..)`](#arraysortas)'s ability to sort 
 | |
| as a list
 | |
| ```bnf
 | |
| <map>.sort(<sorted-keys>)
 | |
|     -> <map>
 | |
| ```
 | |
| 
 | |
| This is similar to [`<set>.sort(..)`](#setsort) and [`Object.sort(..)`](#objectsort), 
 | |
| see the later for more info.
 | |
| 
 | |
| 
 | |
| ## `Set`
 | |
| 
 | |
| ```javascript
 | |
| require('ig-types/Set')
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<set>.unite(..)`
 | |
| 
 | |
| Unite two sets and return the resulting set
 | |
| ```bnf
 | |
| <set>.unite(<other>)
 | |
|     -> <union-set>
 | |
| ```
 | |
| 
 | |
| This is a shorthand for `new Set([...<set>, ...<other>])`
 | |
| 
 | |
| 
 | |
| ### `<set>.intersect(..)`
 | |
| 
 | |
| Intersect two sets and return the intersection set
 | |
| ```bnf
 | |
| <set>.untersect(<other>)
 | |
|     -> <intersection-set>
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<set>.subtract(..)`
 | |
| 
 | |
| Subtract `<other>` from set and return resulting set
 | |
| ```bnf
 | |
| <set>.subtract(<other>)
 | |
|     -> <sub-set>
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<set>.splice(..)`
 | |
| 
 | |
| In-place splice a set
 | |
| ```bnf
 | |
| <set>.splice(<from>)
 | |
| <set>.splice(<from>, <count>)
 | |
| <set>.splice(<from>, <count>, ...<items>)
 | |
|     -> <removed>
 | |
| ```
 | |
| 
 | |
| This is the same as 
 | |
| [`<array>.splice(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) 
 | |
| but without the ability to add more than one instance of an item.
 | |
| 
 | |
| 
 | |
| ### `<set>.replace(..)`
 | |
| 
 | |
| Replace value in set with other value retaining item order (in-place)
 | |
| ```bnf
 | |
| <set>.replace(<old>, <new>)
 | |
| <set>.replace(<old>, <new>, true)
 | |
|     -> <set>
 | |
| ```
 | |
| 
 | |
| Replace the value without sorting
 | |
| ```bnf
 | |
| <set>.replace(<old>, <new>, false)
 | |
|     -> <set>
 | |
| ```
 | |
| 
 | |
| Note that when sorting large sets this can get expensive.
 | |
| 
 | |
| 
 | |
| ### `<set>.replaceAt(..)`
 | |
| 
 | |
| Replace item at position in set retaining order (in-place)
 | |
| ```bnf
 | |
| <set>.replaceAt(<index>, <new>)
 | |
| <set>.replaceAt(<index>, <new>, true)
 | |
|     -> <set>
 | |
| ```
 | |
| 
 | |
| If `<index>` is less than `0` the `<new>` item will be prepended to `<set>`,
 | |
| if the `<index>` is greater than or equal to `<set>.size` then `<new>` is 
 | |
| appended.
 | |
| 
 | |
| Replace the value at index without sorting
 | |
| ```bnf
 | |
| <set>.replaceAt(<index>, <new>, false)
 | |
|     -> <set>
 | |
| ```
 | |
| 
 | |
| Here, if `<index>` is less than `0` or greater than or equal to `<set>.size`
 | |
| `<new>` will always be appended to `<set>`.
 | |
| 
 | |
| Note that when sorting large sets this can get expensive.
 | |
| 
 | |
| 
 | |
| ### `<set>.sort(..)`
 | |
| 
 | |
| Sort `<set>` (in-place)
 | |
| ```bnf
 | |
| <set>.sort()
 | |
|     -> <set>
 | |
| 
 | |
| <set>.sort(<cmp>)
 | |
|     -> <set>
 | |
| ```
 | |
| 
 | |
| In the general case this is similar to 
 | |
| [`<array>.sort(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 
 | |
| with the addition of the [`<array>.sortAs(..)`](#arraysortas)'s ability to sort 
 | |
| as a list
 | |
| ```bnf
 | |
| <set>.sort(<sorted-values>)
 | |
|     -> <set>
 | |
| ```
 | |
| 
 | |
| This is similar to [`<map>.sort(..)`](#mapsort) and [`Object.sort(..)`](#objectsort),
 | |
| see the later for more info.
 | |
| 
 | |
| 
 | |
| 
 | |
| ## `Date`
 | |
| 
 | |
| ```javascript
 | |
| require('ig-types/Date')
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `Date.timeStamp(..)`
 | |
| 
 | |
| Generate a timestamp (format: `'YYYYMMDDHHMMSS'`)
 | |
| ```bnf
 | |
| Date.timeStamp()
 | |
|     -> <timestamp>
 | |
| ```
 | |
| 
 | |
| Generate a full timestamp, including milliseconds (format: `'YYYYMMDDHHMMSSmmm'`)
 | |
| ```bnf
 | |
| Date.timeStamp(true)
 | |
|     -> <timestamp>
 | |
| ```
 | |
| 
 | |
| This is a shorthand to: [`(new Date()).getTimeStamp(..)`](#dategettimestamp)
 | |
| 
 | |
| The timestamp is generated from the time of call, for generating timestamps form specific `<date>` objects see:
 | |
| [`<date>.getTimeStamp(..)`](#dategettimestamp)
 | |
| 
 | |
| 
 | |
| ### `Date.fromTimeStamp(..)`
 | |
| 
 | |
| Create a `<date>` from a timestamp
 | |
| ```bnf
 | |
| Date.fromTimeStamp(<timestamp>)
 | |
|     -> <date>
 | |
| ```
 | |
| 
 | |
| This is a shorthand to: [`(new Date()).setTimeStamp(<timestamp>)`](#datesettimestamp)
 | |
| 
 | |
| 
 | |
| ### `Date.str2ms(..)`
 | |
| 
 | |
| Convert a string describing a time period into milliseconds.
 | |
| ```bnf
 | |
| Date.str2ms(<str>)
 | |
|     -> <number>
 | |
| ```
 | |
| 
 | |
| Examples:
 | |
| ```javascript
 | |
| // time units (d/h/m/s/ms) and their variants...
 | |
| var a = Date.str2ms('3 seconds') // -> 3000
 | |
| 
 | |
| var b = Date.str2ms('0.1h') // -> 360000
 | |
| 
 | |
| // time period (DD:HH:MM:SS:mmm)...
 | |
| var c = Date.str2ms('00:20:001') // -> 20001
 | |
| var d = Date.str2ms('1:3') // -> 63000
 | |
| ```
 | |
| 
 | |
| Note that time periods are seconds-based by default unless it contains three 
 | |
| digits then it defaults to milliseconds:
 | |
| ```javascript
 | |
| // least significant unit is seconds by default...
 | |
| var e = Date.str2ms(':3') // -> 3000
 | |
| 
 | |
| // when the least significant unit contains 3 digits it is read as ms...
 | |
| var f = Date.str2ms(':030') // -> 30
 | |
| ```
 | |
| 
 | |
| 
 | |
| Supported formats:
 | |
| ```bnf
 | |
| <str> ::=
 | |
|     <milliseconds>
 | |
|     | <seconds>
 | |
|     | <minutes>
 | |
|     | <hours>
 | |
|     | <days>
 | |
|     | <period>
 | |
| 
 | |
| <milliseconds> ::=
 | |
|     <number>
 | |
|     | <number>ms
 | |
|     | <number>m[illi][-]s[ec[ond[s]]]
 | |
| 
 | |
| <seconds> ::=
 | |
|     <number>s
 | |
|     | <number>s[ec[ond[s]]]
 | |
| 
 | |
| <seconds> ::=
 | |
|     <number>m
 | |
|     | <number>m[in[ute[s]]]
 | |
| 
 | |
| <seconds> ::=
 | |
|     <number>h
 | |
|     | <number>h[our[s]]
 | |
|     
 | |
| <seconds> ::=
 | |
|     <number>d
 | |
|     | <number>d[ay[s]]
 | |
| 
 | |
| <period> ::= 
 | |
|     [[[DD:]HH:]MM]:SS[:mmm]
 | |
|     | [[[[DD:]HH:]MM]:SS]:mmm
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<date>.toShortDate(..)`
 | |
| 
 | |
| Generate a short date string from `<date>` 
 | |
| (format: `'YYYY-MM-DD HH:MM:SS'`)
 | |
| ```bnf
 | |
| <date>.toShortDate()
 | |
|     -> <short-date>
 | |
| ```
 | |
| 
 | |
| Generate a short date string including milliseconds from `<date>` 
 | |
| (format: `'YYYY-MM-DD HH:MM:SS:mmm'`)
 | |
| ```bnf
 | |
| <date>.toShortDate(true)
 | |
|     -> <short-date>
 | |
| ```
 | |
| 
 | |
| Note that `<short-date>` is directly parseable by `new Date(..)`
 | |
| ```javascript
 | |
| var a = (new Date()).toShortDate(true)
 | |
| 
 | |
| // parse the <short-date> and generate a new short date from it...
 | |
| var b = (new Date(a)).toShortDate(true)
 | |
| 
 | |
| a == b // -> true
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<date>.getTimeStamp(..)`
 | |
| 
 | |
| Generate a timestamp from `<date>`
 | |
| (format `'YYYYMMDDHHMMSS'`)
 | |
| ```bnf
 | |
| <date>.getTimeStamp()
 | |
|     -> <timestamp>
 | |
| ```
 | |
| 
 | |
| Generate a timestamp from `<date>` including milliseconds 
 | |
| (format `'YYYYMMDDHHMMSSmmm'`)
 | |
| ```bnf
 | |
| <date>.getTimeStamp(true)
 | |
|     -> <timestamp>
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<date>.setTimeStamp(..)`
 | |
| 
 | |
| Update a `<date>` from a timestamp
 | |
| ```bnf
 | |
| <date>.setTimeStamp(<timestamp>)
 | |
|     -> <date>
 | |
| ```
 | |
| 
 | |
| 
 | |
| ## `String`
 | |
| 
 | |
| ```javascript
 | |
| require('ig-types/String')
 | |
| ```
 | |
| 
 | |
| ### `<string>.capitalize()`
 | |
| 
 | |
| Capitalize the first character of a string
 | |
| ```bnf
 | |
| <string>.capitalize()
 | |
|     -> <string>
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `<string>.indent(..)`
 | |
| 
 | |
| Indent each line in `<string>` by `<size>` spaces
 | |
| ```bnf
 | |
| <string>.indent(<size>)
 | |
|     -> <string>
 | |
| ```
 | |
| 
 | |
| Indent/prepend each line in `<string>` by the `<prefix>` string
 | |
| ```bnf
 | |
| <string>.indent(<prefix>)
 | |
|     -> <string>
 | |
| ```
 | |
| 
 | |
| 
 | |
| ## `RegExp`
 | |
| 
 | |
| ```javascript
 | |
| require('ig-types/RegExp')
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `RegExp.quoteRegExp(..)`
 | |
| 
 | |
| Quote regexp reserved characters in a string
 | |
| ```bnf
 | |
| RegExp.quoteRegExp(<str>)
 | |
|     -> <str>
 | |
| ```
 | |
| 
 | |
| This is mainly used to quote strings to be matched as-is within a regular expression.
 | |
| 
 | |
| 
 | |
| 
 | |
| ## `Promise`
 | |
| 
 | |
| ```javascript
 | |
| require('ig-types/Promise')
 | |
| ```
 | |
| or
 | |
| ```javascript
 | |
| var promise = require('ig-types/Promise')
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### Interactive promises
 | |
| 
 | |
| _Interactive promises_ can be sent messages and then handle them.
 | |
| 
 | |
| 
 | |
| ```javascript
 | |
| var printer = Promise.interactive(function(resolve, reject, onmessage){
 | |
|     var buf = []
 | |
|     var state = 'pending'
 | |
|     onmessage(function(type, ...args){
 | |
|         type == 'flush' ?
 | |
|             (buf = buf
 | |
|                 .filter(function([type, state, ...args]){
 | |
|                     console[type](`(${ state }):`, ...args) }))
 | |
|         : type == 'close' ?
 | |
|             (resolve(...args), 
 | |
|                 state = 'resolved')
 | |
|         : buf.push([type, state, ...args]) }) })
 | |
| 
 | |
| printer
 | |
|     .send('log', 'some message...')
 | |
|     .send('warn', 'some warning...')
 | |
|     .send('flush')
 | |
|     .send('close')
 | |
| ```
 | |
| 
 | |
| Note that message handling is independent of promise state, so in the above case 
 | |
| we can still populate the buffer and _flush_ it even if the promise is resolved
 | |
| ```javascript
 | |
| printer
 | |
|     .send('log', 'some other message...')
 | |
|     .send('flush')
 | |
| ```
 | |
| 
 | |
| If the user wants to handle messages differently (ignore for example) after the
 | |
| promise is finalized it is their responsibility 
 | |
| (see: [`<onmessage>(..)`](#promiseinteractive) for more info)
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `Promise.interactive(..)`
 | |
| 
 | |
| Create and interactive promise
 | |
| ```bnf
 | |
| Promise.interactive(<handler>)
 | |
|     -> <promise-inter>
 | |
| ```
 | |
| 
 | |
| The `<handler>` accepts one additional argument, compared to the `Promise(..)`
 | |
| handler, `<onmessage>`, used to register message handlers.
 | |
| ```bnf
 | |
| <handler>(<resolve>, <reject>, <onmessage>)
 | |
| 
 | |
| <onmessage>(<message-handler>)
 | |
| ```
 | |
| 
 | |
| Remove `<message-handler>`
 | |
| ```bnf
 | |
| <onmessage>(<message-handler>, false)
 | |
| ```
 | |
| 
 | |
| Remove all handlers
 | |
| ```bnf
 | |
| <onmessage>(false)
 | |
| ```
 | |
| 
 | |
| `<message-handler>` is called when a message is sent via 
 | |
| [`<promise-inter>.send()`](#promise-intersend).
 | |
| 
 | |
| 
 | |
| #### `<promise-inter>.send(..)`
 | |
| 
 | |
| Send a message to an interactive promise
 | |
| ```bnf
 | |
| <promise-inter>.send()
 | |
| <promise-inter>.send(...)
 | |
|     -> <promise-inter>
 | |
| ```
 | |
| 
 | |
| Sending a message triggers message handlers registered via `<onmessage>(..)` 
 | |
| passing each handler the arguments.
 | |
| 
 | |
| 
 | |
| #### `<promise-inter>.then(..)`
 | |
| 
 | |
| Extended `.then(..)` implementation.
 | |
| 
 | |
| See [`<promise-iter>.then(..)`](#promise-iterthen--promise-itercatch--promise-iterfinally) for details.
 | |
| 
 | |
| 
 | |
| 
 | |
| ### Cooperative promises
 | |
| 
 | |
| A _cooperative promise_ is one that can be finalized externally/cooperatively.
 | |
| 
 | |
| This can be useful for breaking recursive dependencies between promises or when
 | |
| it is simpler to thread the result receiver promise down the stack than building 
 | |
| a promise stack and manually threading the result up.
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| // NOTE: implementing this via Promise.any(..) would also require implementing a 
 | |
| //      way to stop the "workers" after the result is found...
 | |
| async function controller(trigger){
 | |
|     while(!trigger.isSet)
 | |
| 
 | |
|         // do things...
 | |
| 
 | |
|         trigger.isSet
 | |
|             || trigger.set(result) } }
 | |
| 
 | |
| async function controlled(trigger){
 | |
| 
 | |
|     // do things independently of trigger...
 | |
| 
 | |
|     trigger
 | |
|         .then(function(){
 | |
|             // do things after trigger...
 | |
|         }) }
 | |
| 
 | |
| 
 | |
| var t = Promise.cooperative()
 | |
| 
 | |
| // multiple cooperative controllers competing to create a result...
 | |
| controller(t)
 | |
| controller(t)
 | |
| controller(t)
 | |
| // ...
 | |
| 
 | |
| // prepare and process result...
 | |
| // NOTE: calling .then() here is completely optional and done out of role
 | |
| //      hygene -- isolating cooperative API from the client...
 | |
| controlled(t.then())
 | |
| // ...
 | |
| ```
 | |
| 
 | |
| Note that this functionally can be considered a special-case of an 
 | |
| [interactive promise](#interactive-promises), but in reality they are two 
 | |
| different implementations, the main differences are:
 | |
| - _Cooperative promise_ constructor does not need a resolver function,
 | |
| - _Cooperative promises_ do not the implement `.send(..)` API.
 | |
| 
 | |
| Note that implementing _Cooperative promises_ on top of _Interactive promises_ 
 | |
| cleanly, though feeling more _"beautiful"_, would be more complex than the 
 | |
| current standalone implementation, as it would require both implementing 
 | |
| the `.set(..)` API/logic _and_ active encapsulation of the message API.
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `Promise.cooperative()`
 | |
| 
 | |
| Create a cooperative promise
 | |
| ```bnf
 | |
| Promise.cooperative()
 | |
|     -> <promise-coop>
 | |
| ```
 | |
| 
 | |
| 
 | |
| #### `<promise-coop>.set(..)`
 | |
| 
 | |
| Resolve `<promise-coop>` with `<value>`
 | |
| ```bnf
 | |
| <promise-coop>.set(<value>)
 | |
| <promise-coop>.set(<value>, true)
 | |
|     -> <promise-coop>
 | |
| ```
 | |
| 
 | |
| If `<value>` is a promise, then `<promise-coop>` will be bound to its state, i.e.
 | |
| resolved if `<value>` is resolved and rejected if it is rejected with the same
 | |
| values.
 | |
| 
 | |
| Reject `<promise-coop>` with `<value>`
 | |
| ```bnf
 | |
| <promise-coop>.set(<value>, false)
 | |
|     -> <promise-coop>
 | |
| ```
 | |
| 
 | |
| Calling `.set(..)` will set `.isSet` to `true`.
 | |
| 
 | |
| 
 | |
| #### `<promise-coop>.isSet`
 | |
| 
 | |
| Property representing if the cooperative promise was _set_ / `.set(..)` was 
 | |
| called (value is `true`) or no (`false`).
 | |
| 
 | |
| This property is read-only.
 | |
| 
 | |
| 
 | |
| #### `<promise-coop>.then(..)`
 | |
| 
 | |
| Extended `.then(..)` implementation.
 | |
| 
 | |
| See [`<promise-iter>.then(..)`](#promise-iterthen--promise-itercatch--promise-iterfinally) for details.
 | |
| 
 | |
| 
 | |
| 
 | |
| ### Promise iteration
 | |
| 
 | |
| An _iterable promise_ is on one hand very similar to `Promise.all(..)` in that it
 | |
| generally takes a list of values each could be either an explicit value or a 
 | |
| promise, and it is similar to a _generator_ in that it allows iteration over the 
 | |
| contained values and chaining of operations but unlike `Promise.all(..)` this 
 | |
| iteration occurs depth-first instead of breadth first.
 | |
| 
 | |
| One can think of _promise iterators_ vs. _generators_ as the former being 
 | |
| internally controlled and asynchronous while the later being externally
 | |
| controlled and synchronous.
 | |
| 
 | |
| Here is a traditional example using `Promise.all(..)`:
 | |
| ```javascript
 | |
| var p = Promise.all([ .. ])
 | |
|     // this will not execute until ALL the inputs resolve...
 | |
|     .then(function(lst){
 | |
|         return lst
 | |
|           .filter(function(e){
 | |
|               // ...
 | |
|           })
 | |
|           // this will not run until ALL of lst is filtered...
 | |
|           .map(function(e){
 | |
|               // ...
 | |
|           }) })
 | |
| ```
 | |
| 
 | |
| And a _promise iterator_:
 | |
| ```javascript
 | |
| var p = Promise.iter([ .. ])
 | |
|     // each element is processed as soon as it is ready disregarding of its order
 | |
|     // in the input array...
 | |
|     .filter(function(e){
 | |
|         // ...
 | |
|     })
 | |
|     // items reach here as soon as they are returned by the filter stage handler...
 | |
|     .map(function(e){
 | |
|         // ...
 | |
|     })
 | |
|     // .then(..) explicitly waits for the whole list of inputs to resolve...
 | |
|     .then(function(lst){
 | |
|         // ...
 | |
|     })
 | |
| ```
 | |
| 
 | |
| This approach has a number of advantages:
 | |
| - items are processed as soon as they are available without waiting for the 
 | |
|   slowest promise on each level to resolve
 | |
| - simpler and more intuitive code
 | |
| 
 | |
| And some disadvantages:
 | |
| - item indexes are unknowable until all the promises resolve.
 | |
| 
 | |
| Calling each of the `<promise-iter>` methods will return a new and unresolved 
 | |
| promise, even if the original is resolved.
 | |
| 
 | |
| If all values are resolved the `<promise-iter>` will resolve on the next 
 | |
| execution frame.
 | |
| 
 | |
| <!-- 
 | |
| XXX should we support generators as input?
 | |
|     ...not sure about the control flow direction here, on one hand the generator
 | |
|     should be unwound by the client (not sure how to do this within a promise) on 
 | |
|     the other hand...
 | |
|     ...not sure if it is possible/correct to control the flow upstream in a 
 | |
|     promise chain...
 | |
|     ...might be a good idea to at least provide some means to control flow up
 | |
|     the chain, for instance to abort execution...
 | |
| XXX should we support infinite generators as input?
 | |
| -->
 | |
| 
 | |
| 
 | |
| #### `Promise.iter(..)` / `promise.IterablePromise(..)`
 | |
| 
 | |
| Create an _iterable promise_
 | |
| 
 | |
| ```bnf
 | |
| Promise.iter(<array>)
 | |
| Promise.iter(<promise>)
 | |
|     -> <promise-iter>
 | |
| ```
 | |
| 
 | |
| #### `<promise>.iter()`
 | |
| 
 | |
| Wrap a promise in an promise iterator. 
 | |
| 
 | |
| ```bnf
 | |
| <promise>.iter()
 | |
|     -> <promise-iter>
 | |
| ```
 | |
| 
 | |
| If `<promise>` resolves to a non-array value it will be treated as a single 
 | |
| element, otherwise the array will be iterated over.
 | |
| 
 | |
| 
 | |
| #### `<promise-iter>.iter()`
 | |
| 
 | |
| Return a shallow copy of the current promise iterator.
 | |
| 
 | |
| ```bnf
 | |
| <promise-iter>.iter()
 | |
|     -> <promise-iter>
 | |
| ```
 | |
| 
 | |
| 
 | |
| #### `<promise-iter>.map(..)` / `<promise-iter>.filter(..)` / `<promise-iter>.reduce(..)`
 | |
| 
 | |
| Methods similar but not fully equivalent to `Array`'s 
 | |
| [`.map(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), 
 | |
| [`.filter(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter),
 | |
| and [`.reduce(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
 | |
| ```bnf
 | |
| <promise-iter>.map(<handler>)
 | |
|     -> <promise-iter>
 | |
| 
 | |
| <handler>(<elem>)
 | |
|     -> <elem>
 | |
| ```
 | |
| 
 | |
| ```bnf
 | |
| <promise-iter>.filter(<handler>)
 | |
|     -> <promise-iter>
 | |
| 
 | |
| <handler>(<elem>)
 | |
|     -> <bool>
 | |
| ```
 | |
| 
 | |
| ```bnf
 | |
| <promise-iter>.reduce(<handler>, <state>)
 | |
|     -> <promise>
 | |
| 
 | |
| <handler>(<state>, <elem>)
 | |
|     -> <state>
 | |
| ```
 | |
| 
 | |
| Note that these are different to `Array`'s equivalents in some details: 
 | |
| - `<handler>` is _not_ called in the order of element occurrence but rather 
 | |
|     in the order of elements are resolved/ready.
 | |
| - `<handler>` does not get either the element _index_ or the _container_.  
 | |
|     this is because in _out-of-order_ and _depth-first_ execution the 
 | |
|     index is _unknowable_ and the container is a promise/black-box.
 | |
| 
 | |
| This is especially critical for `.reduce(..)` as the iteration in an order
 | |
| different from the order of elements _can_ affect actual result if this is 
 | |
| not expected.
 | |
| 
 | |
| `.reduce(..)` is also a bit different here in that it will return a basic 
 | |
| `<promise>` rather than an iterable promise object as we can't know what 
 | |
| will it will reduce to.
 | |
| 
 | |
| Note that since `.reduce(..)` handler's execution order can not be known,
 | |
| there is no point in implementing `.reduceRigth(..)`.
 | |
| 
 | |
| 
 | |
| #### `<promise-iter>.flat(..)`
 | |
| 
 | |
| ```bnf
 | |
| <promise-iter>.flat()
 | |
| <promise-iter>.flat(<depth>)
 | |
|     -> <promise-iter>
 | |
| ```
 | |
| 
 | |
| This is similar to [`<array>.flat(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) see it for more info.
 | |
| 
 | |
| 
 | |
| #### `<promise-iter>.reverse()`
 | |
| 
 | |
| ```bnf
 | |
| <promise-iter>.reverse()
 | |
|     -> <promise-iter>
 | |
| ```
 | |
| 
 | |
| This is deferent from `<array>.reverse()` in that it will _not_ reverse in-place, 
 | |
| but rather a _reversed copy_ will be created.
 | |
| 
 | |
| This is similar to [`<array>.reverse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse) see it for more info.
 | |
| 
 | |
| 
 | |
| #### `<promise-iter>.concat(..)`
 | |
| 
 | |
| ```bnf
 | |
| <promise-iter>.concat(<other>)
 | |
|     -> <promise-iter>
 | |
| ```
 | |
| 
 | |
| This is similar to [`<array>.concat(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) see it for more info.
 | |
| 
 | |
| 
 | |
| #### `<promise-iter>.push(..)` / `<promise-iter>.unshift(..)`
 | |
| 
 | |
| ```bnf
 | |
| <promise-iter>.push(<elem>)
 | |
|     -> <promise-iter>
 | |
| 
 | |
| <promise-iter>.unshift(<elem>)
 | |
|     -> <promise-iter>
 | |
| ```
 | |
| 
 | |
| These are similar to [`<array>.push(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) 
 | |
| and [`<array>.unshift(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift) 
 | |
| see them for more info.
 | |
| 
 | |
| 
 | |
| #### `<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`
 | |
| 
 | |
| An extension to
 | |
| [`<promise>.then(..)` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)
 | |
| this adds the ability to pass no arguments
 | |
| ```bnf
 | |
| <promise-iter>.then()
 | |
|     -> <promise>
 | |
| ```
 | |
| 
 | |
| This will return a generic promise wrapper passing through the results as-is. This 
 | |
| can be useful to hide the extended promise API from further code.
 | |
| 
 | |
| 
 | |
| #### Advanced handler
 | |
| 
 | |
| ```bnf
 | |
| Promise.iter(<block>, <handler>)
 | |
|     -> <iterable-promise>
 | |
| ```
 | |
| 
 | |
| The `<handler>` will get passed each resolved `<value>` of the input `<block>` 
 | |
| as soon as it's available/resolved.
 | |
| 
 | |
| The `<handler>` return value is unwrapped into the resulting array, allowing
 | |
| each call to both remove elements (i.e. returning `[]`) from the resulting 
 | |
| `<block>` as well as insert multiple items (by returning an array of items).
 | |
| 
 | |
| <!-- XXX returning promises from handler needs to be documented/tested... -->
 | |
| 
 | |
| ```bnf
 | |
| <handler>(<value>)
 | |
|     -> []
 | |
|     -> [ <elem>, .. ]
 | |
|     -> <non-array>
 | |
| ```
 | |
| 
 | |
| ```bnf
 | |
| <block> ::= 
 | |
|     []
 | |
|     | [ <elem>, .. ]
 | |
| 
 | |
| <elem> ::= 
 | |
|     <value>
 | |
|     | <promise>(<value>)
 | |
| ```
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| var p = Promise.iter(
 | |
|         [1, 2, 3, Promise.resolve(4), [5, 6]], 
 | |
|         function(elem){
 | |
|             // duplicate even numbers...
 | |
|             return elem % 2 == 0 ?
 | |
|                     [elem, elem]
 | |
|                 // return arrays as-is...
 | |
|                 : elem instanceof Array ?
 | |
|                     [elem]
 | |
|                 // remove other elements...
 | |
|                 : [] })
 | |
|     .then(function(lst){
 | |
|         console.log(lst) }) // -> [2, 2, 4, 4, [5, 6]]
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### Promise proxies
 | |
| 
 | |
| _Promise proxies_ generate a set of prototype methods returning promises that when the parent promise is resolved will resolve to a specific method call.
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| var o = {
 | |
|     method: function(...args){
 | |
|         console.log('method:', ...args)
 | |
|     },
 | |
| }
 | |
| 
 | |
| var p = Peomise.cooperative().as(o)
 | |
| 
 | |
| p.method(1, 2, 3) // returns a promise...
 | |
| 
 | |
| // ...
 | |
| 
 | |
| // resolving a promise will trigger all the proxy emthod execution, so 
 | |
| // here 'method: 1, 2, 3' will get printed...
 | |
| p.set(o)
 | |
| 
 | |
| ```
 | |
| 
 | |
| #### `<promise>.as(..)`
 | |
| 
 | |
| Create a promise proxy
 | |
| 
 | |
| ```bnf
 | |
| <promise>.as(<object>)
 | |
| <promise>.as(<constructor>)
 | |
|     -> <promise-proxy>
 | |
| ```
 | |
| 
 | |
| A proxy promise will be populated with proxy methods to all the methods of the `<object>` or `<constructor>.prototype`.
 | |
| 
 | |
| #### `<promise-proxy>.<method>(..)`
 | |
| 
 | |
| When `<promise>` resolves, call the `.<method>(..)` on the resolved value.
 | |
| 
 | |
| ```bnf
 | |
| <promise-proxy>.<method>(..)
 | |
|     -> <method-promise>
 | |
| ```
 | |
| 
 | |
| `<method-promise>` will resolve the the return value of the `<method>` when 
 | |
| the main `<promise>` is resolved.
 | |
| 
 | |
| 
 | |
| 
 | |
| ## Generator extensions and utilities
 | |
| 
 | |
| ```javascript
 | |
| var generator = require('ig-types/generator')
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### The basics
 | |
| 
 | |
| The _generator_ hierarchy in JavaScript is a bit complicated.
 | |
| 
 | |
| Consider the following:
 | |
| ```javascript
 | |
| // generator constructor function...
 | |
| var Iter = function*(L){
 | |
|     for(var e of L){
 | |
|         yield e }}
 | |
| 
 | |
| // generator instance...
 | |
| var iter = Iter([1, 2, 3])
 | |
| ```
 | |
| 
 | |
| We can test that `iter` is an instance of `Iter`:
 | |
| ```javascript
 | |
| iter instanceof Iter // -> true
 | |
| ```
 | |
| 
 | |
| Note that there is no generator constructor constructor or _meta-generator_, 
 | |
| i.e. `Iter` is created syntactically and not constructed via a `new` _constructor_.
 | |
| 
 | |
| Due to the three level structure of generators we use a slightly different 
 | |
| terminology to reference different levels of API's:
 | |
| - `Generator` - the generator meta-constructor.  
 | |
|     This is a constructor that is used to create/prototype `<Generator>`'s, i.e.
 | |
|     generator constructors.  
 | |
|     `Generator` is mainly used for `instanceof` checks, but can be used as a 
 | |
|     prototype for extending generators.
 | |
| - `<Generator>` - the generator constructor.  
 | |
|     This is the product of either a `Generator` meta-constructor or a 
 | |
|     `function*(..){ .. }` statement.  
 | |
|     In the above example `Iter` is a generator constructor.
 | |
| - `<generator>` - the generator instance.  
 | |
|     Generator instances are created by calling a `<Generator>` / generator 
 | |
|     constructor.  
 | |
|     In the above example `iter` is a generator instance.
 | |
| 
 | |
| 
 | |
| Iterators and generators are similar but not the same. Some objects like `Array`'s, 
 | |
| `Map`'s and `Set`'s provide a number of generic iterators that are not implemented 
 | |
| as generators. These objects are also extended by `ig-types/generator` to match the 
 | |
| `<generator>` object API defined below.
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `generator.Generator`
 | |
| 
 | |
| Exposes the _hidden_ JavaScript generator meta-constructor.
 | |
| 
 | |
| This is similar to the JavaScript's
 | |
| [`Function`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) constructor
 | |
| ```javascript
 | |
| var g = generator.Generator(`
 | |
|     yield 123
 | |
|     yield 321 `)
 | |
| 
 | |
| // prints 123 then 321...
 | |
| for(var e of g()){
 | |
|     console.log(e.value) }
 | |
| ```
 | |
| 
 | |
| This can be used to test if a function is a _generator constructor_
 | |
| ```javascript
 | |
| Iter instanceof generator.Generator // -> true
 | |
| ```
 | |
| 
 | |
| Note that currently in JavaScript there is no _built-in_ way to test if a 
 | |
| constructor/function, `Iter` in this case, is a _generator_ constructor.
 | |
| 
 | |
| 
 | |
| #### `generator.iter(..)`
 | |
| 
 | |
| Generic generator wrapper
 | |
| ```bnf
 | |
| generator.iter()
 | |
|     -> <generator>
 | |
| 
 | |
| generator.iter(<iterable>)
 | |
|     -> <generator>
 | |
| ```
 | |
| 
 | |
| Example:
 | |
| ```javascript
 | |
| for(var i of generator.iter([1, 2, 3])){
 | |
|     console.log(i) }
 | |
| ```
 | |
| 
 | |
| The following are equivalent:
 | |
| ```javascript
 | |
| var a = generator.iter()
 | |
| 
 | |
| var b = new generator.Generator()
 | |
| ```
 | |
| 
 | |
| But `Generator()` takes no arguments and thus can not be used as a wrapper 
 | |
| while `.iter(..)` is designed to accept an iterable value like an array object.
 | |
| 
 | |
| 
 | |
| #### `generator.STOP`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| ### Generator instance iteration
 | |
| 
 | |
| This is a set of `Array`-like iterator methods that enable chaining of 
 | |
| generators and `Promise`-like API to handle the generated results. 
 | |
| 
 | |
| Chained generators handle items depth-first, i.e. the items are passed as they 
 | |
| are yielded down the generator chain.
 | |
| 
 | |
| 
 | |
| #### `<generator>.iter()`
 | |
| 
 | |
| Iterate over the generator.
 | |
| ```bnf
 | |
| <generator>.iter()
 | |
|     -> <generator>
 | |
| ```
 | |
| 
 | |
| This is here mainly for compatibility with [`<array>`'s `.iter()`](#arrayiter--arrayiter).
 | |
| 
 | |
| 
 | |
| #### `<generator>.map(..)` / `<generator>.filter(..)` 
 | |
| 
 | |
| Equivalents to `Array`'s `.map(..)`, `.filter(..)` and `.reduce(..)` but return 
 | |
| generators that yield the handler return values.
 | |
| 
 | |
| `.map(..)` here also supports a generator as a handler
 | |
| ```javascript
 | |
| var expand = function*(n){ 
 | |
|     yield* (new Array(n)).fill(n) }
 | |
| 
 | |
| // will create: [1, 2, 2, 3, 3, 3]
 | |
| var L = [1,2,3]
 | |
|     .iter()
 | |
|         .map(expand)
 | |
|         .toArray()
 | |
| ```
 | |
| 
 | |
| Throwing `STOP` form within the handler will stop generation, throwing 
 | |
| `STOP(<value>)` will yield the `<value>` then stop.
 | |
| ```javascript
 | |
| var stopAt = function(n){
 | |
|     return function(e){
 | |
|         if(e == n){
 | |
|             // stop iteration yielding the value we are stopping at...
 | |
|             throw generator.STOP(e) } 
 | |
|         return e } }
 | |
| 
 | |
| var L = [1,2,3,4,5]
 | |
|     .iter()
 | |
|         .map(stopAt(3))
 | |
|         .toArray()
 | |
| ```
 | |
| 
 | |
| 
 | |
| #### `<generator>.reduce(..)` / `<generator>.greduce(..)`
 | |
| 
 | |
| <!--
 | |
| XXX .reduce(..) can return a non-iterable -- test and document this case...
 | |
|     ...compare with Array.prototype.reduce(..)
 | |
| -->
 | |
| 
 | |
| 
 | |
| #### `<generator>.slice(..)`
 | |
| 
 | |
| ```bnf
 | |
| <generator>.slice()
 | |
| <generator>.slice(<from>)
 | |
| <generator>.slice(<from>, <to>)
 | |
|     -> <generator>
 | |
| ```
 | |
| 
 | |
| Note that this does not support negative indexes as it not possible to know the
 | |
| generator length until it is fully done.
 | |
| 
 | |
| Otherwise this is similar to `Array`'s `.slice(..)` but will return a generator 
 | |
| instead of an array, for more info see:  
 | |
| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
 | |
| 
 | |
| 
 | |
| #### `<generator>.at(..)` / `<generator>.gat(..)`
 | |
| 
 | |
| ```bnf
 | |
| <generator>.at(<index>)
 | |
|     -> <value>
 | |
|     -> undefined
 | |
| 
 | |
| <generator>.gat(<index>)
 | |
|     -> <generator>
 | |
| ```
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<generator>.flat(..)`
 | |
| 
 | |
| ```bnf
 | |
| <generator>.flat()
 | |
| <generator>.flat(<depth>)
 | |
|     -> <generator>
 | |
| ```
 | |
| 
 | |
| Equivalent to `Array`'s `.flat(..)` but will return a generator instead of an 
 | |
| array, for more info see:  
 | |
| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
 | |
| 
 | |
| 
 | |
| #### `<generator>.shift()` / `<generator>.pop()` / `<generator>.gshift()` / `<generator>.gpop()`
 | |
| 
 | |
| Return the first/last item in generator.
 | |
| ```bnf
 | |
| <generator>.shift()
 | |
| <generator>.pop()
 | |
|     -> <value>
 | |
|     -> undefined
 | |
| ```
 | |
| 
 | |
| Return a `<generator>` that will yield the first/last item in the generator.
 | |
| ```bnf
 | |
| <generator>.gshift()
 | |
| <generator>.gpop()
 | |
|     -> <generator>
 | |
| ```
 | |
| 
 | |
| Note that there are no equivalents to `.push(..)` and `.unshift(..)` as they 
 | |
| would require breaking item processing order.
 | |
| 
 | |
| Note that `.shift()`/`.gshift()` will yield the item the generator is at at 
 | |
| time of call, this may not be the _first_ item if the generator is partially 
 | |
| depleted.
 | |
| 
 | |
| #### `<generator>.unshift(..)` / `<generator>.push(..)`
 | |
| 
 | |
| Add a value to the generator sequence at start/end.
 | |
| ```bnf
 | |
| <generator>.unshift(<value>)
 | |
| <generator>.push(<value>)
 | |
|     -> <generator>
 | |
| ```
 | |
| 
 | |
| Value added by `.unshift(..)` will be yielded by `<generator>` "first", i.e. on 
 | |
| _next_ call to `.next()`, regardless of the current generator state.
 | |
| 
 | |
| 
 | |
| #### `<generator>.promise()`
 | |
| 
 | |
| ```bnf
 | |
| <generator>.promise()
 | |
|     -> <promise>
 | |
| ```
 | |
| Return a promise and resolve it with the generator value.
 | |
| 
 | |
| Note that this will deplete the generator.
 | |
| 
 | |
| 
 | |
| #### `<generator>.then(..)` / `<generator>.catch(..)` / `<generator>.finally(..)`
 | |
| 
 | |
| ```bnf
 | |
| <generator>.then(<resolve>, <reject>)
 | |
|     -> <promise>
 | |
| 
 | |
| <generator>.then(<reject>)
 | |
|     -> <promise>
 | |
| 
 | |
| <generator>.finally(<handler>)
 | |
|     -> <promise>
 | |
| ```
 | |
| Shorthands to `<generator>.promise().then(..)` / `<generator>.promise().catch(..)` / `<generator>.promise().finally(..)`
 | |
| 
 | |
| These are the same as equivalent `Promise` methods, for more info see:  
 | |
| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
 | |
| 
 | |
| 
 | |
| #### `<generator>.toArray()`
 | |
| 
 | |
| Unwind a generator into an array
 | |
| ```bnf
 | |
| <generator>.toArray()
 | |
|     -> <array>
 | |
| ```
 | |
| 
 | |
| This is equivalent to `[...<generator>]` but more suited for the concatenative style.
 | |
| 
 | |
| 
 | |
| ### Treating iterators the same as generators
 | |
| 
 | |
| Most _iterator_ methods of `Array`, `Set` and `Map` are extended with the same
 | |
| API supported by the [`<generator>`](#generator-instance-iteration), so 
 | |
| effectively most built-in iterator methods can be transparently treated as 
 | |
| generators.
 | |
| 
 | |
| ```javascript
 | |
| // this will generate: [1, 4, 9]
 | |
| var L = [ ...[1, 2, 3]
 | |
|     // Note that this is implemented as an iterator in JS and not a generator...
 | |
|     .values()
 | |
|     .map(function(e){
 | |
|         return e * e }) ]
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### Generator constructor iteration
 | |
| 
 | |
| This API is essentially the same as [generator iteration](#generator-instance-iteration) 
 | |
| with some minor omissions, but will return a reusable generator _pipeline_ 
 | |
| instead of a generator.
 | |
| 
 | |
| ```javascript
 | |
| var sumOdds = generator.iter
 | |
|     .filter(function(e){
 | |
|         return e % 2 == 1 })
 | |
|     .reduce(function(r, e){ 
 | |
|         return r + e }, 0)
 | |
|     .pop()
 | |
| 
 | |
| // sumOdds(..) is essentially a function that can be reused...
 | |
| console.log(sumOdds([1, 2, 3])) // -> 4
 | |
| console.log(sumOdds([1, 2, 3, 4, 5, 6, 7])) // -> 16
 | |
| ```
 | |
| 
 | |
| The above code is the same in function to:
 | |
| ```javascript
 | |
| var sumOdds = function(lst){
 | |
|     return generator.iter(lst)
 | |
|         .filter(function(e){
 | |
|             return e % 2 == 1 })
 | |
|         .reduce(function(r, e){ 
 | |
|             return r + e }, 0)
 | |
|         .pop() }
 | |
| 
 | |
| console.log(sumOdds([1, 2, 3])) // -> 4
 | |
| console.log(sumOdds([1, 2, 3, 4, 5, 6, 7])) // -> 16
 | |
| ```
 | |
| 
 | |
| 
 | |
| <!-- XXX explain this as a language -->
 | |
| 
 | |
| `<Generator>` methods fall into two categories:
 | |
| - `<constructor>`  
 | |
|   create a new chained `<Generator>` object that when called will return 
 | |
|   a `<generator>`
 | |
| - `<finalizer>`  
 | |
|   create a chained _function_ that when called will return a `<value>`/`undefined`
 | |
| 
 | |
| ```bnf
 | |
| <Generator>.<constructor>(..)
 | |
|     -> <Generator>
 | |
| 
 | |
| <Generator>.<finalizer>(..)
 | |
|     -> <func>
 | |
| 
 | |
| <Generator>(<iterable>)
 | |
|     -> <generator>
 | |
| 
 | |
| <func>(<iterable>)
 | |
|     -> <value>
 | |
|     -> undefined
 | |
| ```
 | |
| 
 | |
| <!--
 | |
| XXX list the differences...
 | |
| -->
 | |
| 
 | |
| 
 | |
| #### `<Generator>.iter(..)`
 | |
| 
 | |
| This is a shorthand to [`iter(..)`](#generatoriter).
 | |
| 
 | |
| This is here mainly for compatibility with 
 | |
| [`Array`'s `.iter(..)`](#arrayiter--arrayiter).
 | |
| 
 | |
| #### `<Generator>.at(..)` / `<Generator>.gat(..)`
 | |
| 
 | |
| ```bnf
 | |
| <Generator>.at(<index>)
 | |
|     -> <func>
 | |
| 
 | |
| <Generator>.gat(<index>)
 | |
|     -> <Generator>
 | |
| ```
 | |
| 
 | |
| Equivalents to [`<generator>`'s `.at(..)`/`.gat(..)`](#generatorat--generatorgat) 
 | |
| but returning a reusable `<func>`/`<Generator>`.
 | |
| 
 | |
| 
 | |
| #### `<Generator>.shift()` / `<Generator>.pop()` / `<Generator>.gshift()` / `<Generator>.gpop()`
 | |
| 
 | |
| ```bnf
 | |
| <Generator>.shift()
 | |
| <Generator>.pop()
 | |
|     -> <func>
 | |
| 
 | |
| <Generator>.gshift()
 | |
| <Generator>.gpop()
 | |
|     -> <Generator>
 | |
| ```
 | |
| 
 | |
| Note that `.shift()`/`.gshift()` will get the element the generator is at 
 | |
| currently which may not be the first element in the sequence.
 | |
| 
 | |
| Equivalents to [`<generator>`'s `.shift(..)`/`.pop(..)`/..](#generatorshift--generatorpop--generatorgshift--generatorgpop) 
 | |
| but returning a reusable `<func>`/`<Generator>`.
 | |
| 
 | |
| 
 | |
| #### `<generator>.unshift(..)` / `<generator>.push(..)`
 | |
| 
 | |
| ```bnf
 | |
| <Generator>.unshift(<value>)
 | |
| <Generator>.push(<value>)
 | |
|     -> <Generator>
 | |
| ```
 | |
| 
 | |
| Equivalents to [`<generator>`'s `.unshift(..)`/`.push(..)`](#generatorunshift--generatorpush) 
 | |
| but returning a reusable `<Generator>`.
 | |
| 
 | |
| 
 | |
| #### `<Generator>.slice(..)`
 | |
| 
 | |
| ```bnf
 | |
| <Generator>.slice(<from>)
 | |
| <Generator>.slice(<from>, <to>)
 | |
|     -> <Generator>
 | |
| ```
 | |
| 
 | |
| Unlike `Array`'s `.slice(..)` this does not support negative indexes.
 | |
| 
 | |
| Equivalent to [`<generator>`'s `.slice(..)`](#generatorslice) 
 | |
| but returning a reusable `<Generator>`.
 | |
| 
 | |
| 
 | |
| #### `<Generator>.map(..)` / `<Generator>.filter(..)` / `<Generator>.reduce(..)` / `<Generator>.flat()`
 | |
| 
 | |
| Counterparts to `<generator>`'s 
 | |
| [`.map(..)`, `.filter(..)`](#generatormap--generatorfilter), [`.reduce(..)`/`.greduce(..)`](#generatorreduce--generatorgreduce) and 
 | |
| [`.flat(..)`](#generatorflat) 
 | |
| but return a `<Generator>`.
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<Generator>.toArray()`
 | |
| 
 | |
| Return a function that will return a `<generator>` output as an `Array`.
 | |
| ```bnf
 | |
| <Generator>.toArray()
 | |
|     -> <function>
 | |
| ```
 | |
| 
 | |
| 
 | |
| #### `<Generator>.then(..)` / `<Generator>.catch(..)` / `<Generator>.finally(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| ### Generator combinators
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| #### `<Generator>.chain(..)` / `<generator>.chain(..)`
 | |
| 
 | |
| ```bnf
 | |
| <Generator>.chain(<Generator>, ..)
 | |
|     -> <Generator>
 | |
|     
 | |
| <generator>.chain(<Generator>, ..)
 | |
|     -> <generator>
 | |
| ```
 | |
| <!-- XXX -->
 | |
| 
 | |
| ```javascript
 | |
| // double each element...
 | |
| var x2 = generator.iter
 | |
|     .map(function(e){ return e * 2 })
 | |
| 
 | |
| generator.range(0, 100).chain(x2)
 | |
| ```
 | |
| 
 | |
| #### `<Generator>.concat(..)` / `<generator>.concat(..)` 
 | |
| 
 | |
| Concatenate the results from generators
 | |
| ```bnf
 | |
| <Generator>.concat(<Generator>, ..)
 | |
|     -> <Generator>
 | |
|     
 | |
| <generator>.concat(<generator>, ..)
 | |
|     -> <generator>
 | |
| ```
 | |
| 
 | |
| 
 | |
| <!-- XXX Example -->
 | |
| 
 | |
| 
 | |
| <!-- XXX
 | |
| #### generator.zip(..) / `<Generator>.zip(..)` / `<generator>.zip(..)` 
 | |
| -->
 | |
| 
 | |
| <!-- XXX
 | |
| #### `<Generator>.tee(..)` / `<generator>.tee(..)` 
 | |
| -->
 | |
| 
 | |
| 
 | |
| 
 | |
| ### Generator library
 | |
| 
 | |
| #### `generator.range(..)`
 | |
| 
 | |
| Create a generator yielding a range of numbers
 | |
| ```bnf
 | |
| range()
 | |
| range(<to>)
 | |
| range(<from>, <to>)
 | |
| range(<from>, <to>, <step>)
 | |
|     -> <generator>
 | |
| ```
 | |
| 
 | |
| <!-- XXX examples... -->
 | |
| 
 | |
| 
 | |
| #### `generator.repeat(..)`
 | |
| 
 | |
| Create a generator repeatedly yielding `<value>`
 | |
| ```bnf
 | |
| repeat()
 | |
| repeat(<value>)
 | |
| repeat(<value>, <stop>)
 | |
|     -> <generator>
 | |
| 
 | |
| <stop>(<value>)
 | |
|     -> <bool>
 | |
| ```
 | |
| 
 | |
| If no value is given `true` is yielded by default.
 | |
| 
 | |
| `<stop>` if given will be called with each `<value>` before it is yielded and 
 | |
| if it returns `false` the iteration is stopped.
 | |
| 
 | |
| <!-- XXX examples... -->
 | |
| 
 | |
| 
 | |
| #### `generator.produce(..)`
 | |
| 
 | |
| Create a generator calling a function to produce yielded values
 | |
| ```bnf
 | |
| produce()
 | |
| produce(<func>)
 | |
|     -> <generator>
 | |
| 
 | |
| <func>()
 | |
|     -> <value>
 | |
| ```
 | |
| 
 | |
| `<func>(..)` can `throw` `STOP` or `STOP(<value>)` to stop production at any time.
 | |
| 
 | |
| <!-- XXX examples... -->
 | |
| 
 | |
| 
 | |
| 
 | |
| ### Generator helpers
 | |
| 
 | |
| #### `generator.stoppable(..)`
 | |
| 
 | |
| Wrap function/generator adding support for stopping mid-iteration by throwing `STOP`.
 | |
| 
 | |
| ```bnf
 | |
| stoppable(<generator>)
 | |
|     -> <generator>
 | |
| ```
 | |
| 
 | |
| <!-- XXX example? -->
 | |
| 
 | |
| 
 | |
| 
 | |
| ## Containers
 | |
| 
 | |
| ```javascript
 | |
| var containers = require('ig-types').containers
 | |
| ```
 | |
| or, to only import containers:
 | |
| ```javascript
 | |
| var containers = require('ig-types/containers')
 | |
| ```
 | |
| 
 | |
| Note that this will also import [`ig-types/Map`](#map).
 | |
| 
 | |
| 
 | |
| ### `containers.UniqueKeyMap()` (`Map`)
 | |
| 
 | |
| `UniqueKeyMap` implements a key-value container (i.e. `Map`) that supports
 | |
| and maintains _duplicate_ keys by appending an index to them.  
 | |
| The original keys are stored internally thus the renaming mechanics are 
 | |
| stable.
 | |
| 
 | |
| `UniqueKeyMap` extends the `Map` constructor, so all the usual `Map` 
 | |
| methods and properties apply here.
 | |
| 
 | |
| To construct an instance:
 | |
| ```javascript
 | |
| var x = new UniqueKeyMap()
 | |
| ```
 | |
| or:
 | |
| ```javascript
 | |
| // new is optional...
 | |
| var y = UniqueKeyMap()
 | |
| ```
 | |
| 
 | |
| `UniqueKeyMap` supports the same initialization signature as `Map` but 
 | |
| treats repeating keys differently.
 | |
| ```javascript
 | |
| var z = UniqueKeyMap([['a', 1], ['a', 2], ['b', 1]])
 | |
| ```
 | |
| 
 | |
| The second `"a"` item will automatically get re-keyed as `"a (1)"`:
 | |
| ```javascript
 | |
| console.log([...z.keys()]) // -> ['a', 'a (1)', 'b']
 | |
| ```
 | |
| 
 | |
| Note that `.set(..)` will never rewrite an element:
 | |
| ```javascript
 | |
| z.set('a', 3)
 | |
| 
 | |
| console.log([...z.keys()]) // -> ['a', 'a (1)', 'b', 'a (2)']
 | |
| 
 | |
| z.get('a') // -> 1
 | |
| z.get('a (1)') // -> 2
 | |
| ```
 | |
| 
 | |
| To get the generated key:
 | |
| ```javascript
 | |
| var k = z.set('a', 4, true)
 | |
| 
 | |
| console.log(k) // -> 'a (3)'
 | |
| ```
 | |
| 
 | |
| To explicitly rewrite an item:
 | |
| ```javascript
 | |
| z.reset('a (1)', 4)
 | |
| 
 | |
| z.get('a (1)') // -> 4
 | |
| ```
 | |
| 
 | |
| And we can _rename_ items, i.e. change their key:
 | |
| ```javascript
 | |
| z.rename('a (2)', 'c')
 | |
| 
 | |
| console.log([...z.keys()]) // -> ['a', 'a (1)', 'b', 'a (3)', 'c']
 | |
| ```
 | |
| 
 | |
| For more info on `Map` see:  
 | |
| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.set(..)`
 | |
| 
 | |
| ```bnf
 | |
| <unique-key-map>.reset(<key>, <item>)
 | |
|     -> <unique-key-map>
 | |
| 
 | |
| <unique-key-map>.reset(<key>, <item>, true)
 | |
|     -> <new-key>
 | |
| ```
 | |
| 
 | |
| Add an `<item>` to `<unique-key-map>`.
 | |
| 
 | |
| If `<key>` already exists then add an index to it to make it unique.
 | |
| 
 | |
| Key updating is done via [`<unique-key-map>.__key_pattern__`](#unique-key-map__key_pattern__).
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.reset(..)`
 | |
| 
 | |
| ```bnf
 | |
| <unique-key-map>.reset(<key>, <item>)
 | |
|     -> <unique-key-map>
 | |
| ```
 | |
| 
 | |
| Explicitly write an `<item>` under `<key>` as-is, this is like `Map`'s `.set(..)`.
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.rename(..)`
 | |
| 
 | |
| ```bnf
 | |
| <unique-key-map>.rename(<from-key>, <to-key>)
 | |
|     -> <unique-key-map>
 | |
| 
 | |
| <unique-key-map>.rename(<from-key>, <to-key>, true)
 | |
|     -> <new-key>
 | |
| ```
 | |
| 
 | |
| Rename item key from `<from-key>` to `<to-key>`.
 | |
| 
 | |
| Same mechanics apply as for [`.set(..)`](#unique-key-mapset) for key uniqueness.
 | |
| 
 | |
| Note, if [`.__unordered_rename__`](#unique-key-map__unordered_rename__) is 
 | |
| `false` (default) this calls [`.orderedRename(..)`](#unique-key-maporderedrename) 
 | |
| otherwise [`.unorderedRename(..)`](#unique-key-mapunorderedrename) is called.
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.orderedRename(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.unorderedRename(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.keysOf(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.originalKey(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.uniqueKey(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.__key_pattern__`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<unique-key-map>.__unordered_rename__`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| ## Event
 | |
| 
 | |
| ```javascript
 | |
| var event = require('ig-types/event')
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### `event.Eventfull(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| ### `event.Event(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| ### `event.TRIGGER`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| Special value when passed to an event method as first argument will force it 
 | |
| to trigger event if the first argument was a function.
 | |
| 
 | |
| ### `event.EventHandlerMixin`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<obj>.on(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<obj>.one(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<obj>.off(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<obj>.trigger(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| ### `event.EventDocMixin`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<obj>.eventfull`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<obj>.events`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| ### `event.EventMixin`
 | |
| 
 | |
| Combines [`event.EventHandlerMixin`](#eventeventhandlermixin) and 
 | |
| [`event.EventDocMixin`](#eventeventdocmixin).
 | |
| 
 | |
| ## Runner
 | |
| 
 | |
| ```javascript
 | |
| var runner = require('ig-types/runner')
 | |
| ```
 | |
| 
 | |
| ### Micro task queue
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| This includes [`event.EventMixin`](#eventeventmixin).
 | |
| 
 | |
| #### `runner.STOP`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `runner.SKIP`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `Queue(..)` / `Queue.runTasks(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `Queue.handle(..)`
 | |
| 
 | |
| Create a handler queue object.
 | |
| ```bnf
 | |
| Queue.handle(<func>, ...<data>)
 | |
| Queue.handle(<options>, <func>, ...<data>)
 | |
|     -> <queue>
 | |
| ```
 | |
| 
 | |
| A handler queue is a queue that has a single handler function (`.handle(..)`) 
 | |
| that handles the queue data.
 | |
| 
 | |
| This is a shorthand for:
 | |
| ```javascript
 | |
| var handler_queue = Queue({
 | |
|         handler: function(item){ .. }, 
 | |
|         .. 
 | |
|     }, 
 | |
|     .. )
 | |
| ```
 | |
| 
 | |
| <!-- XXX settings... -->
 | |
| 
 | |
| 
 | |
| #### `<queue>.state`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `<queue>.start(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<queue>.stop(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `<queue>.runTask(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `<queue>.tasksAdded(..)` (event)
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<queue>.taskStarting(..)` (event)
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<queue>.taskFailed(..)` (event)
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<queue>.taskCompleted(..)` (event)
 | |
| 
 | |
| Event, triggered when a task is completed passing in its result.
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `<queue>.queueEmpty(..)` (event)
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `<queue>.prioritize(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<queue>.delay(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `<queue>.add(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<queue>.clear(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `FinalizableQueue(..)` / `FinalizableQueue.runTasks(..)` (Queue)
 | |
| 
 | |
| This is similar to `Queue(..)` but adds two terminal states (`"done"` and 
 | |
| `"aborted"`) and a `promise`-mapping.
 | |
| 
 | |
| ```bnf
 | |
| FinalizableQueue.handle(<func>, ...<data>)
 | |
| FinalizableQueue.handle(<options>, <func>, ...<data>)
 | |
|     -> <finalizable-queue>
 | |
| ```
 | |
| 
 | |
| When a `<finalizable-queue>` reaches a terminal state it is frozen.
 | |
| 
 | |
| #### `<finalizable-queue>.done(..)` (event/method)
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<finalizable-queue>.abort(..)` (event/method)
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `<finalizable-queue>.promise(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<finalizable-queue>.then(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<finalizable-queue>.catch(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| ### Large task management
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `runner.TaskManager(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| This includes [`event.EventMixin`](#eventeventmixin).
 | |
| 
 | |
| #### `<task-manager>.Task(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<task-manager>.sync_start`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<task-manager>.record_times`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `<task-manager>.titled(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<task-manager>.send(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<task-manager>.stop(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `<task-manager>.done(..)` (event)
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<task-manager>.error(..)` (event)
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `<task-manager>.tasksDone(..)` (event)
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #### `runner.TaskTicket(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| #### `runner.TaskMixin(..)`
 | |
| 
 | |
| <!-- XXX -->
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| ## License
 | |
| 
 | |
| [BSD 3-Clause License](./LICENSE)
 | |
| 
 | |
| Copyright (c) 2020, Alex A. Naanou,  
 | |
| All rights reserved.
 | |
| 
 | |
| 
 | |
| <!-- vim:set ts=4 sw=4 spell : -->
 |