build(prettier): support optional chain with TS 3.7

fix #46
This commit is contained in:
yuqi.pyq 2019-11-12 15:37:31 +08:00
parent 89424b8fc2
commit 00493b81e6
21 changed files with 8043 additions and 553 deletions

View File

@ -50,6 +50,7 @@
"enzyme-adapter-react-16": "^1.5.0", "enzyme-adapter-react-16": "^1.5.0",
"enzyme-to-json": "^3.0.0-beta6", "enzyme-to-json": "^3.0.0-beta6",
"gatsby": "^2.17.7", "gatsby": "^2.17.7",
"geotiff": "^1.0.0-beta.6",
"gh-pages": "^2.1.1", "gh-pages": "^2.1.1",
"gl": "^4.4.0", "gl": "^4.4.0",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
@ -62,7 +63,7 @@
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^7.0.18", "postcss": "^7.0.18",
"postcss-plugin": "^1.0.0", "postcss-plugin": "^1.0.0",
"prettier": "^1.18.2", "prettier": "^1.19.1",
"raw-loader": "^1.0.0", "raw-loader": "^1.0.0",
"react": "^16.8.6", "react": "^16.8.6",
"react-docgen-typescript-loader": "^3.1.0", "react-docgen-typescript-loader": "^3.1.0",
@ -129,8 +130,5 @@
"commitizen": { "commitizen": {
"path": "cz-conventional-changelog" "path": "cz-conventional-changelog"
} }
},
"dependencies": {
"geotiff": "^1.0.0-beta.6"
} }
} }

View File

@ -220,7 +220,8 @@ export default class Layers extends Control {
const obj = this.layerService.getLayer(e.target.layerId); const obj = this.layerService.getLayer(e.target.layerId);
const type = obj.overlay // @ts-ignore
const type = obj?.overlay
? e.type === 'add' ? e.type === 'add'
? 'overlayadd' ? 'overlayadd'
: 'overlayremove' : 'overlayremove'

View File

@ -17,10 +17,6 @@ export default class LayerService implements ILayerService {
public add(layer: ILayer) { public add(layer: ILayer) {
this.layers.push(layer); this.layers.push(layer);
this.initPlugin(layer);
layer.init();
// 添加完成需要触发重绘
// this.renderLayers();
} }
public initLayers() { public initLayers() {
@ -75,11 +71,7 @@ export default class LayerService implements ILayerService {
this.layers.forEach((layer) => layer.destroy()); this.layers.forEach((layer) => layer.destroy());
this.layers = []; this.layers = [];
} }
private initPlugin(layer: ILayer) {
for (const plugin of layer.plugins) {
plugin.apply(layer);
}
}
private clear() { private clear() {
this.renderService.clear({ this.renderService.clear({
color: [0, 0, 0, 0], color: [0, 0, 0, 0],

View File

@ -4,7 +4,7 @@ import { gl } from '../renderer/gl';
import { IAttribute } from '../renderer/IAttribute'; import { IAttribute } from '../renderer/IAttribute';
import { IElements } from '../renderer/IElements'; import { IElements } from '../renderer/IElements';
import { IRendererService } from '../renderer/IRendererService'; import { IRendererService } from '../renderer/IRendererService';
import { IParseDataItem } from '../source/ISourceService' import { IParseDataItem } from '../source/ISourceService';
import { ILayer } from './ILayerService'; import { ILayer } from './ILayerService';
import { import {
IEncodeFeature, IEncodeFeature,
@ -216,7 +216,8 @@ export default class StyleAttributeService implements IStyleAttributeService {
) { ) {
descriptors.forEach((descriptor, attributeIdx) => { descriptors.forEach((descriptor, attributeIdx) => {
if (descriptor && descriptor.update) { if (descriptor && descriptor.update) {
const normal = normalsForCurrentFeature?.slice( const normal =
normalsForCurrentFeature?.slice(
vertexIdx * 3, vertexIdx * 3,
vertexIdx * 3 + 3, vertexIdx * 3 + 3,
) || []; ) || [];

View File

@ -20,9 +20,9 @@ export interface IMapService {
getMarkerContainer(): HTMLElement; getMarkerContainer(): HTMLElement;
// MapEvent // 定义事件类型 // MapEvent // 定义事件类型
on(type: string, hander: (...args: any[]) => void): void; on(type: string, handler: (...args: any[]) => void): void;
off(type: string, hander: (...args: any[]) => void): void; off(type: string, handler: (...args: any[]) => void): void;
once(type: string, hander: (...args: any[]) => void): void; once(type: string, handler: (...args: any[]) => void): void;
// get dom // get dom
getContainer(): HTMLElement | null; getContainer(): HTMLElement | null;
getSize(): [number, number]; getSize(): [number, number];

View File

@ -24,7 +24,8 @@ function decodePickingColor(color: Uint8Array): number {
* @see https://github.com/antvis/L7/blob/next/dev-docs/PixelPickingEngine.md * @see https://github.com/antvis/L7/blob/next/dev-docs/PixelPickingEngine.md
*/ */
@injectable() @injectable()
export default class PixelPickingPass<InitializationOptions = {}> implements IPass<InitializationOptions> { export default class PixelPickingPass<InitializationOptions = {}>
implements IPass<InitializationOptions> {
@lazyInject(TYPES.IRendererService) @lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService; protected readonly rendererService: IRendererService;
@ -132,7 +133,12 @@ export default class PixelPickingPass<InitializationOptions = {}> implements IPa
const xInDevicePixel = x * window.devicePixelRatio; const xInDevicePixel = x * window.devicePixelRatio;
const yInDevicePixel = y * window.devicePixelRatio; const yInDevicePixel = y * window.devicePixelRatio;
if (xInDevicePixel > width || xInDevicePixel < 0 || yInDevicePixel > height || yInDevicePixel < 0) { if (
xInDevicePixel > width ||
xInDevicePixel < 0 ||
yInDevicePixel > height ||
yInDevicePixel < 0
) {
return; return;
} }
@ -214,7 +220,9 @@ export default class PixelPickingPass<InitializationOptions = {}> implements IPa
const { clear, useFramebuffer } = this.rendererService; const { clear, useFramebuffer } = this.rendererService;
// 先输出到 PostProcessor // 先输出到 PostProcessor
const readFBO = this.layer.multiPassRenderer.getPostProcessor().getReadFBO(); const readFBO = this.layer.multiPassRenderer
.getPostProcessor()
.getReadFBO();
this.layer.hooks.beforeRender.call(); this.layer.hooks.beforeRender.call();
useFramebuffer(readFBO, () => { useFramebuffer(readFBO, () => {
clear({ clear({

View File

@ -59,7 +59,8 @@ export default class Scene extends EventEmitter implements ISceneService {
/** /**
* *
*/ */
private inited: boolean; private inited: boolean = false;
private initPromise: Promise<void>;
/** /**
* canvas * canvas
@ -87,6 +88,8 @@ export default class Scene extends EventEmitter implements ISceneService {
public init(globalConfig: IGlobalConfig) { public init(globalConfig: IGlobalConfig) {
this.initClear(); this.initClear();
this.configService.setAndCheckConfig(globalConfig); this.configService.setAndCheckConfig(globalConfig);
// 初始化 ShaderModule
this.shaderModule.registerBuiltinModules();
// 初始化资源管理 图片 // 初始化资源管理 图片
this.iconService.init(); this.iconService.init();
@ -134,9 +137,6 @@ export default class Scene extends EventEmitter implements ISceneService {
this.logger.error('容器 id 不存在'); this.logger.error('容器 id 不存在');
} }
// 初始化 ShaderModule
this.shaderModule.registerBuiltinModules();
// 初始化 container 上的交互 // 初始化 container 上的交互
this.interactionService.init(); this.interactionService.init();
@ -144,24 +144,26 @@ export default class Scene extends EventEmitter implements ISceneService {
}); });
// TODOinit worker, fontAtlas... // TODOinit worker, fontAtlas...
// 执行异步并行初始化任务
this.initPromise = this.hooks.init.promise(this.configService.getConfig());
} }
public addLayer(layer: ILayer) { public addLayer(layer: ILayer) {
this.logger.info(`add layer ${layer.name}`); this.logger.info(`add layer ${layer.name}`);
this.layerService.add(layer); this.layerService.add(layer);
} }
public async render() { public async render() {
// 首次初始化,或者地图的容器被强制销毁的需要重新初始化 // 首次初始化,或者地图的容器被强制销毁的需要重新初始化
if (!this.inited) { if (!this.inited) {
// 首次渲染需要等待底图、相机初始化 // 还未初始化完成需要等待
await this.hooks.init.promise(this.configService.getConfig()); await this.initPromise;
// 初始化marker 容器 // 初始化 marker 容器 TODO: 可以放到 map 初始化方法中?
this.map.addMarkerContainer(); this.map.addMarkerContainer();
this.inited = true; this.layerService.initLayers();
this.layerService.renderLayers(); this.inited = true;
this.emit('loaded'); this.emit('loaded');
} }

View File

@ -85,7 +85,12 @@ export function packCircleVertex(
packUint8ToFloat(strokeColor[2], strokeColor[3]), packUint8ToFloat(strokeColor[2], strokeColor[3]),
]; ];
[[-1, -1], [1, -1], [1, 1], [-1, 1]].forEach(([extrudeX, extrudeY]) => { [
[-1, -1],
[1, -1],
[1, 1],
[-1, 1],
].forEach(([extrudeX, extrudeY]) => {
// vec4( // vec4(
// color, // color,
// color, // color,

View File

@ -52,9 +52,7 @@ let layerIdCounter = 0;
/** /**
* Layer * Layer
*/ */
const defaultLayerInitializationOptions: Partial< const defaultLayerInitializationOptions: Partial<ILayerInitializationOptions> = {
ILayerInitializationOptions
> = {
minZoom: 0, minZoom: 0,
maxZoom: 20, maxZoom: 20,
visible: true, visible: true,
@ -142,12 +140,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
>; >;
private scaleOptions: IScaleOptions = {}; private scaleOptions: IScaleOptions = {};
private enodeOptions: {
[type: string]: {
field: string;
};
};
@lazyInject(TYPES.IInteractionService) @lazyInject(TYPES.IInteractionService)
private readonly interactionService: IInteractionService; private readonly interactionService: IInteractionService;
@ -343,7 +335,10 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
public fitBounds(): void { public fitBounds(): void {
const source = this.getSource(); const source = this.getSource();
const extent = source.extent; const extent = source.extent;
this.map.fitBounds([[extent[0], extent[1]], [extent[2], extent[3]]]); this.map.fitBounds([
[extent[0], extent[1]],
[extent[2], extent[3]],
]);
} }
public destroy() { public destroy() {

View File

@ -83,9 +83,9 @@ export function PolygonExtrudeTriangulation(feature: IEncodeFeature) {
export function HeatmapGridTriangulation(feature: IEncodeFeature) { export function HeatmapGridTriangulation(feature: IEncodeFeature) {
const { shape } = feature; const { shape } = feature;
const { positions, index } = getHeatmapGeometry(shape as const { positions, index } = getHeatmapGeometry(
| ShapeType2D shape as ShapeType2D | ShapeType3D,
| ShapeType3D); );
return { return {
vertices: positions, // [ x, y, z ] 多边形顶点 vertices: positions, // [ x, y, z ] 多边形顶点
indices: index, indices: index,

View File

@ -89,7 +89,10 @@ export default class DataMappingPlugin implements ILayerPlugin {
const params: unknown[] = []; const params: unknown[] = [];
scalers.forEach(({ field }) => { scalers.forEach(({ field }) => {
if (record.hasOwnProperty(field) || attribute.scale?.type ==='variable') { if (
record.hasOwnProperty(field) ||
attribute.scale?.type === 'variable'
) {
params.push(record[field]); params.push(record[field]);
} }
}); });

View File

@ -48,7 +48,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
[field: string]: IStyleScale; [field: string]: IStyleScale;
} = {}; } = {};
private scaleOptions: IScaleOptions = {} private scaleOptions: IScaleOptions = {};
public apply(layer: ILayer) { public apply(layer: ILayer) {
layer.hooks.init.tap('FeatureScalePlugin', () => { layer.hooks.init.tap('FeatureScalePlugin', () => {
@ -84,10 +84,11 @@ export default class FeatureScalePlugin implements ILayerPlugin {
// 创建Scale // 创建Scale
const attributeScale = attribute.scale; const attributeScale = attribute.scale;
attributeScale.names = this.parseFields(attribute!.scale!.field || []); attributeScale.names = this.parseFields(attribute!.scale!.field || []);
const scales: IStyleScale[] = attributeScale.names const scales: IStyleScale[] = attributeScale.names.map(
.map((field:string) => { (field: string) => {
return this.getOrCreateScale(field, attribute, dataArray); return this.getOrCreateScale(field, attribute, dataArray);
}) },
);
// 为scales 设置值区间 // 为scales 设置值区间
if (scales.some((scale) => scale.type === StyleScaleType.VARIABLE)) { if (scales.some((scale) => scale.type === StyleScaleType.VARIABLE)) {
@ -110,12 +111,11 @@ export default class FeatureScalePlugin implements ILayerPlugin {
return { return {
field: scale.field, field: scale.field,
func: scale.scale, func: scale.scale,
} };
}); });
attribute.needRescale = false; attribute.needRescale = false;
} }
}); });
} }
private getOrCreateScale( private getOrCreateScale(
@ -123,7 +123,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
attribute: IStyleAttribute, attribute: IStyleAttribute,
dataArray: IParseDataItem[], dataArray: IParseDataItem[],
) { ) {
const scalekey = [field,attribute.name].join('_') const scalekey = [field, attribute.name].join('_');
if (this.scaleCache[scalekey]) { if (this.scaleCache[scalekey]) {
return this.scaleCache[scalekey]; return this.scaleCache[scalekey];
} }
@ -134,11 +134,11 @@ export default class FeatureScalePlugin implements ILayerPlugin {
styleScale.type === StyleScaleType.VARIABLE && styleScale.type === StyleScaleType.VARIABLE &&
attribute.scale?.values && attribute.scale?.values &&
attribute.scale?.values.length > 0 attribute.scale?.values.length > 0
) { // 只有变量初始化range ) {
// 只有变量初始化range
styleScale.scale.range(attribute.scale?.values); styleScale.scale.range(attribute.scale?.values);
} }
return this.scaleCache[scalekey]; return this.scaleCache[scalekey];
} }
@ -167,7 +167,6 @@ export default class FeatureScalePlugin implements ILayerPlugin {
option: scaleOption, option: scaleOption,
}; };
if (!data || !data.length) { if (!data || !data.length) {
if (scaleOption && scaleOption.type) { if (scaleOption && scaleOption.type) {
styleScale.scale = this.createDefaultScale(scaleOption); styleScale.scale = this.createDefaultScale(scaleOption);
} else { } else {
@ -176,7 +175,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
} }
return styleScale; return styleScale;
} }
const firstValue = (data!.find((d) => !isNil(d[field])))?.[field] const firstValue = data!.find((d) => !isNil(d[field]))?.[field];
// 常量 Scale // 常量 Scale
if (isNumber(field) || (isNil(firstValue) && !scaleOption)) { if (isNumber(field) || (isNil(firstValue) && !scaleOption)) {
styleScale.scale = d3.scaleOrdinal([field]); styleScale.scale = d3.scaleOrdinal([field]);
@ -190,7 +189,6 @@ export default class FeatureScalePlugin implements ILayerPlugin {
Object.assign(cfg, scaleOption); Object.assign(cfg, scaleOption);
styleScale.scale = this.createDefaultScale(cfg); styleScale.scale = this.createDefaultScale(cfg);
styleScale.option = cfg; styleScale.option = cfg;
} }
return styleScale; return styleScale;
} }
@ -218,7 +216,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
} else if (type === ScaleTypes.CAT) { } else if (type === ScaleTypes.CAT) {
cfg.domain = uniq(values); cfg.domain = uniq(values);
} else if (type === ScaleTypes.QUANTILE) { } else if (type === ScaleTypes.QUANTILE) {
cfg.domain = values cfg.domain = values;
} }
return cfg; return cfg;
} }

View File

@ -4,7 +4,14 @@ import { polygonTriangulation } from '..';
describe('PolygonTriangulation', () => { describe('PolygonTriangulation', () => {
it('should do triangulation with a polygon correctly', () => { it('should do triangulation with a polygon correctly', () => {
const mockFeature: IEncodeFeature = { const mockFeature: IEncodeFeature = {
coordinates: [[[0, 0], [1, 0], [1, 1], [0, 1]]], coordinates: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
],
],
color: [1, 0, 0, 0], color: [1, 0, 0, 0],
}; };
const { indices, vertices, size } = polygonTriangulation(mockFeature); const { indices, vertices, size } = polygonTriangulation(mockFeature);

View File

@ -34,7 +34,7 @@ export default class AMapService implements IMapService {
@inject(TYPES.ICoordinateSystemService) @inject(TYPES.ICoordinateSystemService)
private readonly coordinateSystemService: ICoordinateSystemService; private readonly coordinateSystemService: ICoordinateSystemService;
@inject(TYPES.IEventEmitter) @inject(TYPES.IEventEmitter)
private eventEmitter: IEventEmitter; private eventEmitter: any;
private markerContainer: HTMLElement; private markerContainer: HTMLElement;
private $mapContainer: HTMLElement | null; private $mapContainer: HTMLElement | null;
private $jsapi: HTMLScriptElement; private $jsapi: HTMLScriptElement;
@ -58,15 +58,19 @@ export default class AMapService implements IMapService {
} }
// map event // map event
public on(type: string, handle: (...args: any[]) => void): void { public on(type: string, handler: (...args: any[]) => void): void {
if (MapServiceEvent.indexOf(type) !== -1) { if (MapServiceEvent.indexOf(type) !== -1) {
this.eventEmitter.on(type, handle); this.eventEmitter.on(type, handler);
} else { } else {
this.map.on(type, handle); this.map.on(type, handler);
} }
} }
public off(type: string, handle: (...args: any[]) => void): void { public off(type: string, handler: (...args: any[]) => void): void {
this.map.off(type, handle); if (MapServiceEvent.indexOf(type) !== -1) {
this.eventEmitter.off(type, handler);
} else {
this.map.off(type, handler);
}
} }
public getContainer(): HTMLElement | null { public getContainer(): HTMLElement | null {
@ -105,7 +109,10 @@ export default class AMapService implements IMapService {
const amapBound = this.map.getBounds().toBounds(); const amapBound = this.map.getBounds().toBounds();
const NE = amapBound.getNorthEast(); const NE = amapBound.getNorthEast();
const SW = amapBound.getSouthWest(); const SW = amapBound.getSouthWest();
return [[NE.getLng(), NE.getLat()], [SW.getLng(), SW.getLat()]]; return [
[NE.getLng(), NE.getLat()],
[SW.getLng(), SW.getLat()],
];
} }
public getMinZoom(): number { public getMinZoom(): number {
@ -200,7 +207,7 @@ export default class AMapService implements IMapService {
}); });
// 监听地图相机时间 // 监听地图相机时间
this.map.on('camerachange', this.handleCameraChanged); this.map.on('camerachange', this.handlerCameraChanged);
this.emit('mapload'); this.emit('mapload');
resolve(); resolve();
}; };
@ -238,7 +245,7 @@ export default class AMapService implements IMapService {
this.cameraChangedCallback = callback; this.cameraChangedCallback = callback;
} }
private handleCameraChanged = (e: IAMapEvent): void => { private handlerCameraChanged = (e: IAMapEvent): void => {
const { const {
fov, fov,
near, near,
@ -270,7 +277,7 @@ export default class AMapService implements IMapService {
// set coordinate system // set coordinate system
// if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) { // if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) {
// // TODO:偏移坐标系高德地图不支持 pith bear 同步 // // TODO:偏移坐标系高德地图不支持 pitch bear 同步
// this.coordinateSystemService.setCoordinateSystem( // this.coordinateSystemService.setCoordinateSystem(
// CoordinateSystem.P20_OFFSET, // CoordinateSystem.P20_OFFSET,
// ); // );

View File

@ -1,34 +0,0 @@
import {
Bounds,
container,
CoordinateSystem,
ICoordinateSystemService,
ILngLat,
IMapConfig,
IMapService,
IPoint,
IViewport,
MapType,
TYPES,
} from '@l7/core';
import { DOM } from '@l7/utils';
import { inject, injectable } from 'inversify';
import { IAMapEvent, IAMapInstance } from '../typings/index';
@injectable()
export default class MapService<MapInstance> implements IMapService {
public map: MapInstance;
@inject(TYPES.ICoordinateSystemService)
protected readonly coordinateSystemService: ICoordinateSystemService;
@inject(TYPES.IEventEmitter)
protected eventEmitter: IEventEmitter;
protected markerContainer: HTMLElement;
protected $mapContainer: HTMLElement | null;
private cameraChangedCallback: (viewport: IViewport) => void;
public getMarkerContainer(): HTMLElement {
return this.markerContainer;
}
}

View File

@ -40,7 +40,7 @@ export default class MapboxService implements IMapService {
private readonly coordinateSystemService: ICoordinateSystemService; private readonly coordinateSystemService: ICoordinateSystemService;
@inject(TYPES.IEventEmitter) @inject(TYPES.IEventEmitter)
private eventEmitter: IEventEmitter; private eventEmitter: any;
private viewport: Viewport; private viewport: Viewport;
private markerContainer: HTMLElement; private markerContainer: HTMLElement;
private cameraChangedCallback: (viewport: IViewport) => void; private cameraChangedCallback: (viewport: IViewport) => void;

View File

@ -102,15 +102,16 @@ class Scene {
// 依赖注入 // 依赖注入
this.sceneService = container.get<ISceneService>(TYPES.ISceneService); this.sceneService = container.get<ISceneService>(TYPES.ISceneService);
this.sceneService.init(config);
this.mapService = container.get<IMapService>(TYPES.IMapService); this.mapService = container.get<IMapService>(TYPES.IMapService);
this.iconService = container.get<IIconService>(TYPES.IIconService); this.iconService = container.get<IIconService>(TYPES.IIconService);
this.controlService = container.get<IControlService>(TYPES.IControlService); this.controlService = container.get<IControlService>(TYPES.IControlService);
this.layerService = container.get<ILayerService>(TYPES.ILayerService); this.layerService = container.get<ILayerService>(TYPES.ILayerService);
mapType = this.mapService.getType(); mapType = this.mapService.getType();
// 初始化 scene
this.init(); // 初始化 scene
this.sceneService.init(config);
// 初始化组件
this.initControl();
} }
public getMapService(): IMapService { public getMapService(): IMapService {
@ -265,11 +266,6 @@ class Scene {
// TODO: 清理其他 Service 例如 IconService // TODO: 清理其他 Service 例如 IconService
} }
private init(): void {
this.initControl();
this.render();
}
private initControl(): void { private initControl(): void {
this.addControl(new Logo()); this.addControl(new Logo());
} }

View File

@ -19,7 +19,10 @@ export default function image(
dataArray: [ dataArray: [
{ {
_id: 0, _id: 0,
coordinates: [[extent[0], extent[1]], [extent[2], extent[3]]], coordinates: [
[extent[0], extent[1]],
[extent[2], extent[3]],
],
}, },
], ],
}; };

View File

@ -11,7 +11,10 @@ export default function raster(data: number[], cfg: IRasterCfg): IParserData {
height, height,
min, min,
max, max,
coordinates: [[extent[0], extent[1]], [extent[2], extent[3]]], coordinates: [
[extent[0], extent[1]],
[extent[2], extent[3]],
],
}, },
], ],
}; };

View File

@ -64,7 +64,6 @@ export default class Mapbox extends React.Component {
.addTo(scene); .addTo(scene);
scene.addControl(zoomControl); scene.addControl(zoomControl);
scene.addControl(scaleControl); scene.addControl(scaleControl);
console.log(layer);
// layer.fitBounds(); // layer.fitBounds();
}); });
} }

8352
yarn.lock

File diff suppressed because it is too large Load Diff