UNPKG

@skele/core

Version:

Core package of the Skele framework. Element definitions, registrations and traversal.

323 lines (271 loc) 8.27 kB
'use strict' /** * Copyright (c) 2014-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** * Cursor is expected to be required in a node or other CommonJS context: * * var Cursor = require('immutable/contrib/cursor'); * * If you wish to use it in the browser, please check out Browserify or WebPack! */ import { Iterable, Seq, Map, Record } from 'immutable' const Iterator = Iterable.Iterator function cursorFrom(rootData, keyPath) { if (arguments.length === 1) { keyPath = [] } else { keyPath = valToKeyPath(keyPath) } return makeCursor(rootData, keyPath) } var KeyedCursorPrototype = Object.create(Seq.Keyed.prototype) var IndexedCursorPrototype = Object.create(Seq.Indexed.prototype) function KeyedCursor(rootData, keyPath, value) { this.size = value.size this._value = value this._rootData = rootData this._keyPath = keyPath } KeyedCursorPrototype.constructor = KeyedCursor function IndexedCursor(rootData, keyPath, value) { this.size = value.size this._rootData = rootData this._value = value this._keyPath = keyPath } IndexedCursorPrototype.constructor = IndexedCursor KeyedCursorPrototype.toString = function() { return this.__toString('Cursor {', '}') } IndexedCursorPrototype.toString = function() { return this.__toString('Cursor [', ']') } KeyedCursorPrototype.deref = KeyedCursorPrototype.valueOf = IndexedCursorPrototype.deref = IndexedCursorPrototype.valueOf = function( notSetValue ) { return this._value } KeyedCursorPrototype.get = IndexedCursorPrototype.get = function( key, notSetValue ) { var value = this._value.get(key, NOT_SET) return value === NOT_SET ? notSetValue : wrappedValue(this, [key], value) } KeyedCursorPrototype.getIn = IndexedCursorPrototype.getIn = function( keyPath, notSetValue ) { keyPath = listToKeyPath(keyPath) if (keyPath.length === 0) { return this } var value = this._value.getIn(keyPath, NOT_SET) return value === NOT_SET ? notSetValue : wrappedValue(this, keyPath, value) } IndexedCursorPrototype.set = KeyedCursorPrototype.set = function(key, value) { return updateCursor(this, this._value.set(key, value)) } IndexedCursorPrototype.push = function(/* values */) { var args = arguments var m = this._value return updateCursor(this, m.push.apply(m, args)) } IndexedCursorPrototype.pop = function() { return updateCursor(this, this._value.pop()) } IndexedCursorPrototype.unshift = function(/* values */) { var args = arguments var m = this._value return updateCursor(this, m.unshift.apply(m, args)) } IndexedCursorPrototype.shift = function() { return updateCursor(this, this._value.shift()) } IndexedCursorPrototype.setIn = KeyedCursorPrototype.setIn = Map.prototype.setIn KeyedCursorPrototype.remove = KeyedCursorPrototype[ 'delete' ] = IndexedCursorPrototype.remove = IndexedCursorPrototype['delete'] = function( key ) { return updateCursor(this, this._value.remove(key)) } IndexedCursorPrototype.removeIn = IndexedCursorPrototype.deleteIn = KeyedCursorPrototype.removeIn = KeyedCursorPrototype.deleteIn = Map.prototype.deleteIn KeyedCursorPrototype.clear = IndexedCursorPrototype.clear = function() { return updateCursor(this, this._value.clear()) } // TODO maybe remove? IndexedCursorPrototype.update = KeyedCursorPrototype.update = function( keyOrFn, notSetValue, updater ) { return arguments.length === 1 ? updateCursor(this, this._value.update(keyOrFn)) : this.updateIn([keyOrFn], notSetValue, updater) } // TODO maybe remove? IndexedCursorPrototype.updateIn = KeyedCursorPrototype.updateIn = function( keyPath, notSetValue, updater ) { return updateCursor(this, this._value.updateIn(keyPath, notSetValue, updater)) } IndexedCursorPrototype.merge = KeyedCursorPrototype.merge = function(/*...iters*/) { var args = arguments var m = this._value return updateCursor(this, m.merge.apply(m, args)) } IndexedCursorPrototype.mergeWith = KeyedCursorPrototype.mergeWith = function( merger /*, ...iters*/ ) { var args = arguments var m = this._value return updateCursor(this, m.mergeWith.apply(m, args)) } IndexedCursorPrototype.mergeIn = KeyedCursorPrototype.mergeIn = Map.prototype.mergeIn IndexedCursorPrototype.mergeDeep = KeyedCursorPrototype.mergeDeep = function(/*...iters*/) { var args = arguments return updateCursor(this, this._value.mergeDeep.apply(m, args)) } IndexedCursorPrototype.mergeDeepWith = KeyedCursorPrototype.mergeDeepWith = function( merger /*, ...iters*/ ) { var args = arguments return updateCursor(this, this._value.mergeDeepWith.apply(m, args)) } IndexedCursorPrototype.mergeDeepIn = KeyedCursorPrototype.mergeDeepIn = Map.prototype.mergeDeepIn KeyedCursorPrototype.withMutations = IndexedCursorPrototype.withMutations = function( fn ) { return updateCursor(this, (this._value || Map()).withMutations(fn)) } KeyedCursorPrototype.cursor = IndexedCursorPrototype.cursor = function( subKeyPath ) { subKeyPath = valToKeyPath(subKeyPath) return subKeyPath.length === 0 ? this : subCursor(this, subKeyPath) } /** * All iterables need to implement __iterate */ KeyedCursorPrototype.__iterate = IndexedCursorPrototype.__iterate = function( fn, reverse ) { var cursor = this var deref = cursor.deref() return deref && deref.__iterate ? deref.__iterate(function(v, k) { return fn(wrappedValue(cursor, [k], v), k, cursor) }, reverse) : 0 } /** * All iterables need to implement __iterator */ KeyedCursorPrototype.__iterator = IndexedCursorPrototype.__iterator = function( type, reverse ) { var deref = this.deref() var cursor = this var iterator = deref && deref.__iterator && deref.__iterator(Iterator.ENTRIES, reverse) return new Iterator(function() { if (!iterator) { return { value: undefined, done: true } } var step = iterator.next() if (step.done) { return step } var entry = step.value var k = entry[0] var v = wrappedValue(cursor, [k], entry[1]) return { value: type === Iterator.KEYS ? k : type === Iterator.VALUES ? v : [k, v], done: false, } }) } KeyedCursor.prototype = KeyedCursorPrototype IndexedCursor.prototype = IndexedCursorPrototype var NOT_SET = {} // Sentinel value function makeCursor(rootData, keyPath, value) { if (arguments.length < 3) { value = rootData.getIn(keyPath) } if (value && value.deref) { throw new Error('Placing cursors inside cursors is not allowed!') } if (!Iterable.isIterable(value)) return value var CursorClass = Iterable.isIndexed(value) ? IndexedCursor : KeyedCursor var cursor = new CursorClass(rootData, keyPath, value) if (value instanceof Record) { defineRecordProperties(cursor, value) } return cursor } function defineRecordProperties(cursor, value) { try { value._keys.forEach(setProp.bind(undefined, cursor)) } catch (error) { // Object.defineProperty failed. Probably IE8. } } function setProp(prototype, name) { Object.defineProperty(prototype, name, { get: function() { return this.get(name) }, set: function(value) { if (!this.__ownerID) { throw new Error('Cannot set on an immutable record.') } }, }) } function wrappedValue(cursor, keyPath, value) { return Iterable.isIterable(value) ? subCursor(cursor, keyPath, value) : value } function subCursor(cursor, keyPath, value) { if (arguments.length < 3) { return makeCursor( // call without value cursor._rootData, newKeyPath(cursor._keyPath, keyPath) ) } return makeCursor( cursor._rootData, newKeyPath(cursor._keyPath, keyPath), value ) } function updateCursor(cursor, newVal) { return makeCursor(cursor._rootData, cursor._keyPath, newVal) } function newKeyPath(head, tail) { return head.concat(listToKeyPath(tail)) } function listToKeyPath(list) { return Array.isArray(list) ? list : Iterable(list).toArray() } function valToKeyPath(val) { return Array.isArray(val) ? val : Iterable.isIterable(val) ? val.toArray() : [val] } export default { from: cursorFrom, } export { cursorFrom as from }