156 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
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;
 |