| 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; |