181 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @module types
 | |
|  */
 | |
| 
 | |
| import { Item } from '../structs/Item.mjs'
 | |
| import { Type } from '../structs/Type.mjs'
 | |
| import { ItemJSON } from '../structs/ItemJSON.mjs'
 | |
| import { logItemHelper } from '../protocols/syncProtocol.mjs'
 | |
| import { YEvent } from '../utils/YEvent.mjs'
 | |
| 
 | |
| /**
 | |
|  * Event that describes the changes on a YMap.
 | |
|  */
 | |
| export class YMapEvent extends YEvent {
 | |
|   /**
 | |
|    * @param {YMap} ymap The YArray that changed.
 | |
|    * @param {Set<any>} subs The keys that changed.
 | |
|    * @param {boolean} remote Whether the change was created by a remote peer.
 | |
|    */
 | |
|   constructor (ymap, subs, remote) {
 | |
|     super(ymap)
 | |
|     this.keysChanged = subs
 | |
|     this.remote = remote
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A shared Map implementation.
 | |
|  */
 | |
| export class YMap extends Type {
 | |
|   /**
 | |
|    * Creates YMap Event and calls observers.
 | |
|    *
 | |
|    * @private
 | |
|    */
 | |
|   _callObserver (transaction, parentSubs, remote) {
 | |
|     this._callEventHandler(transaction, new YMapEvent(this, parentSubs, remote))
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Transforms this Shared Type to a JSON object.
 | |
|    *
 | |
|    * @return {Object}
 | |
|    */
 | |
|   toJSON () {
 | |
|     const map = {}
 | |
|     for (let [key, item] of this._map) {
 | |
|       if (!item._deleted) {
 | |
|         let res
 | |
|         if (item instanceof Type) {
 | |
|           if (item.toJSON !== undefined) {
 | |
|             res = item.toJSON()
 | |
|           } else {
 | |
|             res = item.toString()
 | |
|           }
 | |
|         } else {
 | |
|           res = item._content[0]
 | |
|         }
 | |
|         map[key] = res
 | |
|       }
 | |
|     }
 | |
|     return map
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the keys for each element in the YMap Type.
 | |
|    *
 | |
|    * @return {Array}
 | |
|    */
 | |
|   keys () {
 | |
|     // TODO: Should return either Iterator or Set!
 | |
|     let keys = []
 | |
|     for (let [key, value] of this._map) {
 | |
|       if (!value._deleted) {
 | |
|         keys.push(key)
 | |
|       }
 | |
|     }
 | |
|     return keys
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Remove a specified element from this YMap.
 | |
|    *
 | |
|    * @param {string} key The key of the element to remove.
 | |
|    */
 | |
|   delete (key) {
 | |
|     this._transact((y) => {
 | |
|       let c = this._map.get(key)
 | |
|       if (y !== null && c !== undefined) {
 | |
|         c._delete(y)
 | |
|       }
 | |
|     })
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Adds or updates an element with a specified key and value.
 | |
|    *
 | |
|    * @param {string} key The key of the element to add to this YMap
 | |
|    * @param {Object | string | number | Type} value The value of the element to add
 | |
|    */
 | |
|   set (key, value) {
 | |
|     this._transact(y => {
 | |
|       const old = this._map.get(key) || null
 | |
|       if (old !== null) {
 | |
|         if (
 | |
|           old.constructor === ItemJSON &&
 | |
|           !old._deleted && old._content[0] === value
 | |
|         ) {
 | |
|           // Trying to overwrite with same value
 | |
|           // break here
 | |
|           return value
 | |
|         }
 | |
|         if (y !== null) {
 | |
|           old._delete(y)
 | |
|         }
 | |
|       }
 | |
|       let v
 | |
|       if (typeof value === 'function') {
 | |
|         v = new value() // eslint-disable-line new-cap
 | |
|         value = v
 | |
|       } else if (value instanceof Item) {
 | |
|         v = value
 | |
|       } else {
 | |
|         v = new ItemJSON()
 | |
|         v._content = [value]
 | |
|       }
 | |
|       v._right = old
 | |
|       v._right_origin = old
 | |
|       v._parent = this
 | |
|       v._parentSub = key
 | |
|       if (y !== null) {
 | |
|         v._integrate(y)
 | |
|       } else {
 | |
|         this._map.set(key, v)
 | |
|       }
 | |
|     })
 | |
|     return value
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns a specified element from this YMap.
 | |
|    *
 | |
|    * @param {string} key The key of the element to return.
 | |
|    */
 | |
|   get (key) {
 | |
|     let v = this._map.get(key)
 | |
|     if (v === undefined || v._deleted) {
 | |
|       return undefined
 | |
|     }
 | |
|     if (v instanceof Type) {
 | |
|       return v
 | |
|     } else {
 | |
|       return v._content[v._content.length - 1]
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns a boolean indicating whether the specified key exists or not.
 | |
|    *
 | |
|    * @param {string} key The key to test.
 | |
|    */
 | |
|   has (key) {
 | |
|     let v = this._map.get(key)
 | |
|     if (v === undefined || v._deleted) {
 | |
|       return false
 | |
|     } else {
 | |
|       return true
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Transform this YXml Type to a readable format.
 | |
|    * Useful for logging as all Items and Delete implement this method.
 | |
|    *
 | |
|    * @private
 | |
|    */
 | |
|   _logString () {
 | |
|     return logItemHelper('YMap', this, `mapSize:${this._map.size}`)
 | |
|   }
 | |
| }
 |