You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							156 lines
						
					
					
						
							4.1 KiB
						
					
					
				
			
		
		
	
	
							156 lines
						
					
					
						
							4.1 KiB
						
					
					
				| var equal = require('deep-equal');
 | |
| var extend = require('extend');
 | |
| 
 | |
| 
 | |
| var lib = {
 | |
|   attributes: {
 | |
|     compose: function (a, b, keepNull) {
 | |
|       if (typeof a !== 'object') a = {};
 | |
|       if (typeof b !== 'object') b = {};
 | |
|       var attributes = extend(true, {}, b);
 | |
|       if (!keepNull) {
 | |
|         attributes = Object.keys(attributes).reduce(function (copy, key) {
 | |
|           if (attributes[key] != null) {
 | |
|             copy[key] = attributes[key];
 | |
|           }
 | |
|           return copy;
 | |
|         }, {});
 | |
|       }
 | |
|       for (var key in a) {
 | |
|         if (a[key] !== undefined && b[key] === undefined) {
 | |
|           attributes[key] = a[key];
 | |
|         }
 | |
|       }
 | |
|       return Object.keys(attributes).length > 0 ? attributes : undefined;
 | |
|     },
 | |
| 
 | |
|     diff: function(a, b) {
 | |
|       if (typeof a !== 'object') a = {};
 | |
|       if (typeof b !== 'object') b = {};
 | |
|       var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) {
 | |
|         if (!equal(a[key], b[key])) {
 | |
|           attributes[key] = b[key] === undefined ? null : b[key];
 | |
|         }
 | |
|         return attributes;
 | |
|       }, {});
 | |
|       return Object.keys(attributes).length > 0 ? attributes : undefined;
 | |
|     },
 | |
| 
 | |
|     transform: function (a, b, priority) {
 | |
|       if (typeof a !== 'object') return b;
 | |
|       if (typeof b !== 'object') return undefined;
 | |
|       if (!priority) return b;  // b simply overwrites us without priority
 | |
|       var attributes = Object.keys(b).reduce(function (attributes, key) {
 | |
|         if (a[key] === undefined) attributes[key] = b[key];  // null is a valid value
 | |
|         return attributes;
 | |
|       }, {});
 | |
|       return Object.keys(attributes).length > 0 ? attributes : undefined;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   iterator: function (ops) {
 | |
|     return new Iterator(ops);
 | |
|   },
 | |
| 
 | |
|   length: function (op) {
 | |
|     if (typeof op['delete'] === 'number') {
 | |
|       return op['delete'];
 | |
|     } else if (typeof op.retain === 'number') {
 | |
|       return op.retain;
 | |
|     } else {
 | |
|       return typeof op.insert === 'string' ? op.insert.length : 1;
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| 
 | |
| function Iterator(ops) {
 | |
|   this.ops = ops;
 | |
|   this.index = 0;
 | |
|   this.offset = 0;
 | |
| };
 | |
| 
 | |
| Iterator.prototype.hasNext = function () {
 | |
|   return this.peekLength() < Infinity;
 | |
| };
 | |
| 
 | |
| Iterator.prototype.next = function (length) {
 | |
|   if (!length) length = Infinity;
 | |
|   var nextOp = this.ops[this.index];
 | |
|   if (nextOp) {
 | |
|     var offset = this.offset;
 | |
|     var opLength = lib.length(nextOp)
 | |
|     if (length >= opLength - offset) {
 | |
|       length = opLength - offset;
 | |
|       this.index += 1;
 | |
|       this.offset = 0;
 | |
|     } else {
 | |
|       this.offset += length;
 | |
|     }
 | |
|     if (typeof nextOp['delete'] === 'number') {
 | |
|       return { 'delete': length };
 | |
|     } else {
 | |
|       var retOp = {};
 | |
|       if (nextOp.attributes) {
 | |
|         retOp.attributes = nextOp.attributes;
 | |
|       }
 | |
|       if (typeof nextOp.retain === 'number') {
 | |
|         retOp.retain = length;
 | |
|       } else if (typeof nextOp.insert === 'string') {
 | |
|         retOp.insert = nextOp.insert.substr(offset, length);
 | |
|       } else {
 | |
|         // offset should === 0, length should === 1
 | |
|         retOp.insert = nextOp.insert;
 | |
|       }
 | |
|       return retOp;
 | |
|     }
 | |
|   } else {
 | |
|     return { retain: Infinity };
 | |
|   }
 | |
| };
 | |
| 
 | |
| Iterator.prototype.peek = function () {
 | |
|   return this.ops[this.index];
 | |
| };
 | |
| 
 | |
| Iterator.prototype.peekLength = function () {
 | |
|   if (this.ops[this.index]) {
 | |
|     // Should never return 0 if our index is being managed correctly
 | |
|     return lib.length(this.ops[this.index]) - this.offset;
 | |
|   } else {
 | |
|     return Infinity;
 | |
|   }
 | |
| };
 | |
| 
 | |
| Iterator.prototype.peekType = function () {
 | |
|   if (this.ops[this.index]) {
 | |
|     if (typeof this.ops[this.index]['delete'] === 'number') {
 | |
|       return 'delete';
 | |
|     } else if (typeof this.ops[this.index].retain === 'number') {
 | |
|       return 'retain';
 | |
|     } else {
 | |
|       return 'insert';
 | |
|     }
 | |
|   }
 | |
|   return 'retain';
 | |
| };
 | |
| 
 | |
| Iterator.prototype.rest = function () {
 | |
|   if (!this.hasNext()) {
 | |
|     return [];
 | |
|   } else if (this.offset === 0) {
 | |
|     return this.ops.slice(this.index);
 | |
|   } else {
 | |
|     var offset = this.offset;
 | |
|     var index = this.index;
 | |
|     var next = this.next();
 | |
|     var rest = this.ops.slice(this.index);
 | |
|     this.offset = offset;
 | |
|     this.index = index;
 | |
|     return [next].concat(rest);
 | |
|   }
 | |
| };
 | |
| 
 | |
| 
 | |
| module.exports = lib;
 |