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.
		
		
		
		
		
			
		
			
				
					
					
						
							163 lines
						
					
					
						
							4.7 KiB
						
					
					
				
			
		
		
	
	
							163 lines
						
					
					
						
							4.7 KiB
						
					
					
				
 | 
						|
/* | 
						|
* Licensed to the Apache Software Foundation (ASF) under one | 
						|
* or more contributor license agreements.  See the NOTICE file | 
						|
* distributed with this work for additional information | 
						|
* regarding copyright ownership.  The ASF licenses this file | 
						|
* to you under the Apache License, Version 2.0 (the | 
						|
* "License"); you may not use this file except in compliance | 
						|
* with the License.  You may obtain a copy of the License at | 
						|
* | 
						|
*   http://www.apache.org/licenses/LICENSE-2.0 | 
						|
* | 
						|
* Unless required by applicable law or agreed to in writing, | 
						|
* software distributed under the License is distributed on an | 
						|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
						|
* KIND, either express or implied.  See the License for the | 
						|
* specific language governing permissions and limitations | 
						|
* under the License. | 
						|
*/ | 
						|
 | 
						|
/* | 
						|
* Licensed to the Apache Software Foundation (ASF) under one | 
						|
* or more contributor license agreements.  See the NOTICE file | 
						|
* distributed with this work for additional information | 
						|
* regarding copyright ownership.  The ASF licenses this file | 
						|
* to you under the Apache License, Version 2.0 (the | 
						|
* "License"); you may not use this file except in compliance | 
						|
* with the License.  You may obtain a copy of the License at | 
						|
* | 
						|
*   http://www.apache.org/licenses/LICENSE-2.0 | 
						|
* | 
						|
* Unless required by applicable law or agreed to in writing, | 
						|
* software distributed under the License is distributed on an | 
						|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
						|
* KIND, either express or implied.  See the License for the | 
						|
* specific language governing permissions and limitations | 
						|
* under the License. | 
						|
*/ | 
						|
function defaultKeyGetter(item) { | 
						|
  return item; | 
						|
} | 
						|
/** | 
						|
 * @param {Array} oldArr | 
						|
 * @param {Array} newArr | 
						|
 * @param {Function} oldKeyGetter | 
						|
 * @param {Function} newKeyGetter | 
						|
 * @param {Object} [context] Can be visited by this.context in callback. | 
						|
 */ | 
						|
 | 
						|
 | 
						|
function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) { | 
						|
  this._old = oldArr; | 
						|
  this._new = newArr; | 
						|
  this._oldKeyGetter = oldKeyGetter || defaultKeyGetter; | 
						|
  this._newKeyGetter = newKeyGetter || defaultKeyGetter; | 
						|
  this.context = context; | 
						|
} | 
						|
 | 
						|
DataDiffer.prototype = { | 
						|
  constructor: DataDiffer, | 
						|
 | 
						|
  /** | 
						|
   * Callback function when add a data | 
						|
   */ | 
						|
  add: function (func) { | 
						|
    this._add = func; | 
						|
    return this; | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * Callback function when update a data | 
						|
   */ | 
						|
  update: function (func) { | 
						|
    this._update = func; | 
						|
    return this; | 
						|
  }, | 
						|
 | 
						|
  /** | 
						|
   * Callback function when remove a data | 
						|
   */ | 
						|
  remove: function (func) { | 
						|
    this._remove = func; | 
						|
    return this; | 
						|
  }, | 
						|
  execute: function () { | 
						|
    var oldArr = this._old; | 
						|
    var newArr = this._new; | 
						|
    var oldDataIndexMap = {}; | 
						|
    var newDataIndexMap = {}; | 
						|
    var oldDataKeyArr = []; | 
						|
    var newDataKeyArr = []; | 
						|
    var i; | 
						|
    initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this); | 
						|
    initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this); // Travel by inverted order to make sure order consistency | 
						|
    // when duplicate keys exists (consider newDataIndex.pop() below). | 
						|
    // For performance consideration, these code below do not look neat. | 
						|
 | 
						|
    for (i = 0; i < oldArr.length; i++) { | 
						|
      var key = oldDataKeyArr[i]; | 
						|
      var idx = newDataIndexMap[key]; // idx can never be empty array here. see 'set null' logic below. | 
						|
 | 
						|
      if (idx != null) { | 
						|
        // Consider there is duplicate key (for example, use dataItem.name as key). | 
						|
        // We should make sure every item in newArr and oldArr can be visited. | 
						|
        var len = idx.length; | 
						|
 | 
						|
        if (len) { | 
						|
          len === 1 && (newDataIndexMap[key] = null); | 
						|
          idx = idx.unshift(); | 
						|
        } else { | 
						|
          newDataIndexMap[key] = null; | 
						|
        } | 
						|
 | 
						|
        this._update && this._update(idx, i); | 
						|
      } else { | 
						|
        this._remove && this._remove(i); | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    for (var i = 0; i < newDataKeyArr.length; i++) { | 
						|
      var key = newDataKeyArr[i]; | 
						|
 | 
						|
      if (newDataIndexMap.hasOwnProperty(key)) { | 
						|
        var idx = newDataIndexMap[key]; | 
						|
 | 
						|
        if (idx == null) { | 
						|
          continue; | 
						|
        } // idx can never be empty array here. see 'set null' logic above. | 
						|
 | 
						|
 | 
						|
        if (!idx.length) { | 
						|
          this._add && this._add(idx); | 
						|
        } else { | 
						|
          for (var j = 0, len = idx.length; j < len; j++) { | 
						|
            this._add && this._add(idx[j]); | 
						|
          } | 
						|
        } | 
						|
      } | 
						|
    } | 
						|
  } | 
						|
}; | 
						|
 | 
						|
function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) { | 
						|
  for (var i = 0; i < arr.length; i++) { | 
						|
    // Add prefix to avoid conflict with Object.prototype. | 
						|
    var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i); | 
						|
    var existence = map[key]; | 
						|
 | 
						|
    if (existence == null) { | 
						|
      keyArr.push(key); | 
						|
      map[key] = i; | 
						|
    } else { | 
						|
      if (!existence.length) { | 
						|
        map[key] = existence = [existence]; | 
						|
      } | 
						|
 | 
						|
      existence.push(i); | 
						|
    } | 
						|
  } | 
						|
} | 
						|
 | 
						|
var _default = DataDiffer; | 
						|
module.exports = _default; |