({ num }: { num: number }) => {
  const n = num % colors.length
  const color = colors[n]

  return (
    <svg viewBox="0 0 24 24" fill={color}>
      <path d="M21.5 24h-19c-1.379 0-2.5-1.122-2.5-2.5v-19c0-1.379 1.122-2.5 2.5-2.5h19c1.379 0 2.5 1.122 2.5 2.5v19c0 1.379-1.122 2.5-2.5 2.5zm-19-23c-.827 0-1.5.673-1.5 1.5v19c0 .827.673 1.5 1.5 1.5h19c.827 0 1.5-.673 1.5-1.5v-19c0-.827-.673-1.5-1.5-1.5h-19zM15.598 12.385zM19.438 15.417l-.002-.005v-.001c-.875-2.226-2.484-3.054-3.445-3.549l-.273-.143c.029-.497-.025-1.034-.167-1.599l-.128.032.123-.044c-.765-2.152-1.757-2.585-2.632-2.967l-.006-.003-.535-2.121c.357-.065.628-.375.628-.752.001-.423-.342-.765-.765-.765s-.766.342-.766.765c0 .358.248.657.581.74l-.825 1.654-.014-.003-.024-.003c-1.053-.033-1.842.369-2.5.947-.633-.322-1.515-.729-2.158-1.814.107-.12.174-.276.174-.45 0-.375-.303-.678-.678-.678s-.678.303-.678.678.303.678.678.678l.221-.04c.416.597 1.09 1.181 1.347 2.828l-.072.091.104.081-.112-.067c-1.157 1.914-.793 4.248.207 5.37-.998 2.546-1.035 4.681-.097 5.868l.002.002.003.003c.119.162.313.233.524.233.189 0 .39-.057.559-.154.312-.179.441-.459.326-.713l-.12.054.119-.056c-.581-1.243-.474-2.713.314-4.37.4.131.778.208 1.145.234l.139.73c.264 1.418.514 2.757 1.297 4.006.132.264.453.387.777.387.122 0 .245-.018.357-.051.385-.116.591-.399.537-.738l-.129.021.125-.042c-.204-.606-.431-1.146-.649-1.67-.373-.894-.725-1.742-.891-2.737.407-.042.797-.129 1.161-.261.825.692 1.661 1.492 2.743 3.406h.001c.072.14.224.215.41.215.105 0 .222-.024.339-.073.365-.155.652-.531.477-1.006v-.001c-.432-1.849-1.426-2.778-2.428-3.547.162-.175.311-.366.442-.576.75.399 1.878 1.005 3.127 2.766l.047.067.011-.008c.151.156.317.24.48.24.096 0 .191-.027.279-.084.306-.194.439-.662.29-1.005zm-8.878-2.493c-.947 0-1.713-.767-1.713-1.713s.767-1.713 1.713-1.713c.947 0 1.713.767 1.713 1.713s-.767 1.713-1.713 1.713zm6.587 4.648l-.084.021v-.001l.084-.02zm-2.007-5.312zm.022 1.006zM11.225 11.604c0 .385-.312.697-.697.697s-.697-.312-.697-.697c0-.385.312-.697.697-.697s.697.312.697.697z" />
    </svg>


(): Struct<string, null> => {
  return define<string>('email', (value) => isEmail(value));
}


(value) => isEmail(value)


(): Struct<string, null> => {
  return size(string(), 27);
}


result => result.insertedId


@Entity({ name: 'user' })
export class UserEntity extends MongoEntity<User> implements User {

  @Column()
  _id!: ObjectId;

  @Column({ index: true, unique: true })
  id!: number;

  @Column({ index: true, unique: true })
  idstr!: string;

  @Column()
  class!: number;

  @Column()
  screen_name!: string;

  @Column()
  name!: string;

  @Column()
  province!: string;

  @Column()
  city!: string;

  @Column()
  location!: string;

  @Column()
  description!: string;

  @Column()
  url!: string;

  @Column()
  profile_image_url!: string;

  @Column()
  cover_image_phone!: string;

  @Column()
  profile_url!: string;

  @Column()
  domain!: string;

  @Column()
  weihao!: string;

  @Column()
  gender!: string;

  @Column()
  followers_count!: number;

  @Column()
  friends_count!: number;

  @Column()
  pagefriends_count!: number;

  @Column()
  statuses_count!: number;

  @Column()
  video_status_count!: number;

  @Column()
  video_play_count!: number;

  @Column()
  favourites_count!: number;

  @Column()
  created_at!: string;

  @Column()
  following!: boolean;

  @Column()
  allow_all_act_msg!: boolean;

  @Column()
  geo_enabled!: boolean;

  @Column()
  verified!: boolean;

  @Column()
  verified_type!: number;

  @Column()
  remark!: string;

  @Column()
  email!: string;

  @Column()
  insecurity!: Insecurity;

  @Column()
  ptype!: number;

  @Column()
  allow_all_comment!: boolean;

  @Column()
  avatar_large!: string;

  @Column()
  avatar_hd!: string;

  @Column()
  verified_reason!: string;

  @Column()
  verified_trade!: string;

  @Column()
  verified_reason_url!: string;

  @Column()
  verified_source!: string;

  @Column()
  verified_source_url!: string;

  @Column()
  follow_me!: boolean;

  @Column()
  like!: boolean;

  @Column()
  like_me!: boolean;

  @Column()
  online_status!: number;

  @Column()
  bi_followers_count!: number;

  @Column()
  lang!: string;

  @Column()
  star!: number;

  @Column()
  mbtype!: number;

  @Column()
  mbrank!: number;

  @Column()
  block_word!: number;

  @Column()
  block_app!: number;

  @Column()
  credit_score!: number;

  @Column()
  user_ability!: number;

  @Column()
  urank!: number;

  @Column()
  story_read_state!: number;

  @Column()
  vclub_member!: number;

  @Column()
  is_teenager!: number;

  @Column()
  is_guardian!: number;

  @Column()
  is_teenager_list!: number;

  @Column()
  pc_new!: number;

  @Column()
  special_follow!: boolean;

  @Column()
  planet_video!: number;

  @Column()
  video_mark!: number;

  @Column()
  live_status!: number;

  @Column()
  tab_manage!: string;

  @Column()
  cardid!: string;

  @Column()
  avatargj_id!: string;

  @Column()
  verified_state?: number | undefined;

  @Column()
  verified_level?: number | undefined;

  @Column()
  verified_type_ext?: number | undefined;

  @Column()
  has_service_tel?: boolean | undefined;

  @Column()
  verified_reason_modified!: string;

  @Column()
  verified_contact_name!: string;

  @Column()
  verified_contact_email!: string;

  @Column()
  verified_contact_mobile!: string;

  @Column()
  cover_image!: string;

  @Column()
  unicom_free_pc!: string;

  async getRandomList({ take }: Pick<PaginationParam, 'take'>) {
    return { list: await this.collection.aggregate([{ $sample: { size: take } }]).toArray() };
  }

  async insertOne(user: User) {
    const id = await this.collection
      .insertOne(user)
      .then(result => result.insertedId);
    return this.collection.findOne({ _id: new ObjectId(id) });
  }

  async deleteOne(id: UserID) {
    await this.collection.deleteOne({ _id: new ObjectId(id) });
    return [id];
  }

  async updateOne(id: UserID, user: Partial<User>) {
    await this.collection.updateOne(
      { _id: new ObjectId(id) },
      { $set: user },
    );
    return this.collection.findOne({ _id: new ObjectId(id) });
  }

  async findOne(id: UserID) {
    return this.collection.findOne({ id });
  }

}


export type UserCollection = UserEntity['collection'];


async getRandomList({ take }: Pick<PaginationParam, 'take'>) {
    return { list: await this.collection.aggregate([{ $sample: { size: take } }]).toArray() };
  }


async insertOne(user: User) {
    const id = await this.collection
      .insertOne(user)
      .then(result => result.insertedId);
    return this.collection.findOne({ _id: new ObjectId(id) });
  }


async deleteOne(id: UserID) {
    await this.collection.deleteOne({ _id: new ObjectId(id) });
    return [id];
  }


async updateOne(id: UserID, user: Partial<User>) {
    await this.collection.updateOne(
      { _id: new ObjectId(id) },
      { $set: user },
    );
    return this.collection.findOne({ _id: new ObjectId(id) });
  }


async findOne(id: UserID) {
    return this.collection.findOne({ id });
  }


// import loadable from "@loadable/component"
// const Home = loadable(() => import("../Home"))
// const Login = loadable(() => import("../Login"))
export default function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<Loading text="loading contents..." />}


() => import(/*webpackChunkName:"terrace"*/"../Terrace")


() => import(/*webpackChunkName:"carrier"*/"../Carrier")


() => import(/*webpackChunkName:"home"*/"../Home")


() => import(/*webpackChunkName:"school" */"../School")


() => import(/*webpackChunkName:"cinema" */"../Cinema")


() => import(/*webpackChunkName:"court" */"../Court")


() => import(/*webpackChunkName:"transportation" */"../Transportation")


() => import(/*webpackChunkName:"cosmodrome" */"../Cosmodrome")


() => import(/*webpackChunkName:"dam" */"../Dam")


() => import(/*webpackChunkName:"gymnasium" */"../Gymnasium")


() => import(/*webpackChunkName:"hospital" */"../Hospital")


() => import(/*webpackChunkName:"bank" */'../Bank')


()=>import(/*webpackChunkName:"government" */'../Government')


export function validateXsd(): ThunkAction<Promise<void>, State, void, Action> {
    return async (dispatch, getState) => {
        dispatch({ type: Types.LoadValidation_Start } as LoadValidationStartAction);

        const state = getState();
        const projectRevision = currentProjectRevisionSelector(state);
        if (!projectRevision) {
            throw new Error("Missing project!");
        }
        const { projectData } = state;
        const xsd = xsdToString(
            projectData.xsd,
            projectData.xsdAnalysis ? projectData.xsdAnalysis.determineXsdPrefixIncludingColon() : '',
            projectRevision.baseUri,
        );

        const baseURI = projectRevision.baseUri || undefined;

        let errors: ValidationError[];
        try {
            errors = await doValidation(xsd, baseURI);
        } catch (err) {
            dispatch({
                type: Types.LoadValidation_Fail,
                error: true,
                payload: err,
            } as LoadValidationFailAction);
            return;
        }

        if (!errors.length) {
            dispatch({
                type: Types.LoadValidation_Finish,
                payload: {},
            } as LoadValidationFinishAction);
        }

        const result: Dictionary<ValidationError[]> = validateDuplicateAttributes(projectData.xsd);

        function pushError(id: string, err: ValidationError) {
            if (!result[id]) {
                result[id] = [] as ValidationError[];
            }
            result[id].push(err);
        }

        const li = calculateLineInformation(projectData.xsd);
        const startElementLines = li.elements.map(([line]) => line);
        for (const err of errors) {
            const idx = le(startElementLines, err.line);
            if (idx === -1) {
                continue;
            }
            const [_start, _end, id] = li.elements[idx];
            const attrs = li.attributes.get(id);
            if (attrs) {
                const attrStartColumns = attrs.map(([start]) => start);
                const attrIdx = le(attrStartColumns, err.column);
                if (attrIdx !== -1) {
                    const [start, len, id] = attrs[attrIdx];
                    if (start + len >= err.column) {
                        pushError(id, err);
                        continue;
                    }
                }
            }

            pushError(id, err);
        }

        dispatch({
            type: Types.LoadValidation_Finish,
            payload: result,
        } as LoadValidationFinishAction);
    };
}


function pushError(id: string, err: ValidationError) {
            if (!result[id]) {
                result[id] = [] as ValidationError[];
            }
            result[id].push(err);
        }


function calculateLineInformation(tree: Dictionary<XsdElement>): RowColumnResult {
    const root = xsdRootElementSelector(tree);
    if (!root) {
        throw new Error("Missing root element.");
    }

    // First line is always <?xml> header
    const [res] = calculateElementPositions(tree, root, 2, 1, 2);
    return res;
}


function calculateElementPositions(
    tree: Dictionary<XsdElement>,
    el: XsdElement,
    baseRow: number,
    baseColumn: number,
    indent: number,
): [RowColumnResult, number] {
    const result: RowColumnResult = {
        attributes: new Map(),
        elements: [],
    };

    const attributes = values(el.attributes)
        .sort((a, b) => a.position - b.position);
    let curAttrPos = baseColumn + el.name.length + 1; // Element Name + <

    // Get the map for the corresponding element
    let attrMap = result.attributes.get(el.id);
    if (!attrMap) {
        attrMap = [];
        result.attributes.set(el.id, attrMap);
    }
    for (const attr of attributes) {
        // Attribute name + value + ="" + leading spacing
        const length = attr.name.length + stringifier.attEscape(attr.value).length + 4;
        attrMap.push([curAttrPos, length, attr.id]);
        curAttrPos += length;
    }

    if (!el.content) {
        result.elements.push([baseRow, 1, el.id]);
        return [result, 1];
    } else if (typeof el.content === 'string') {
        const newlineCount = countNewlines(el.content);
        result.elements.push([baseRow, newlineCount + 1, el.id]);
        return [result, newlineCount + 1];
    } else {
        let childLength = 0;
        const childEls: [number, number, string][] = [];

        // Recurse through all children
        for (const child of el.content.map(id => tree[id]).filter(el => el)) {
            const [res, length] = calculateElementPositions(
                tree,
                child,
                baseRow + childLength + 1,
                baseColumn + indent,
                indent,
            );
            childLength += length;
            for (const [k, v] of res.attributes.entries()) {
                result.attributes.set(k, v);
            }

            // Push into a temporary array first to preserve order
            childEls.push(...res.elements);
        }

        // Insert current element first to preserve order, then insert child elements
        result.elements.push([baseRow, childLength + 2, el.id]);
        result.elements.push(...childEls);

        return [result, childLength + 2]; // Start and end nodes
    }
}


function countNewlines(str: string): number {
    return [...str].filter(ch => ch === '\n').length;
}


async function doValidation(xsd: string, baseURI: string | undefined): Promise<ValidationError[]> {
    const resp = await fetch(`${API_BACKEND_URL}/validate`, {
        body: JSON.stringify({ xsd, baseURI }),
        method: 'post',
        headers: {
            'Accept': JSON_MIME,
            'Content-Type': JSON_MIME,
        },
    });
    const body = await resp.json();
    return body.success || !body.errors ? [] : body.errors;
}


function validateDuplicateAttributes(xsd: Dictionary<XsdElement>): Dictionary<ValidationError[]> {
    const result = {};

    values(xsd).forEach(element => {
        const attributes = values(element.attributes);
        const counted = attributes.reduce(
            (store, attr) => Object.assign(store, { [attr.name]: (store[attr.name] || 0) + 1 }),
            {},
        );

        Object.keys(counted)
            .filter(name => counted[name] > 1)
            .forEach(name => {
                attributes.filter(attr => attr.name === name)
                    .forEach(attr => result[attr.id] = [{
                        column: 0,
                        line: 0,
                        // tslint:disable-next-line:max-line-length
                        message: `Duplicate attribute '${name}'. This will lead to unexpected results in the final XSD.`,
                    }]);
            });
    });

    return result;
}


async (dispatch, getState) => {
        dispatch({ type: Types.LoadValidation_Start } as LoadValidationStartAction);

        const state = getState();
        const projectRevision = currentProjectRevisionSelector(state);
        if (!projectRevision) {
            throw new Error("Missing project!");
        }
        const { projectData } = state;
        const xsd = xsdToString(
            projectData.xsd,
            projectData.xsdAnalysis ? projectData.xsdAnalysis.determineXsdPrefixIncludingColon() : '',
            projectRevision.baseUri,
        );

        const baseURI = projectRevision.baseUri || undefined;

        let errors: ValidationError[];
        try {
            errors = await doValidation(xsd, baseURI);
        } catch (err) {
            dispatch({
                type: Types.LoadValidation_Fail,
                error: true,
                payload: err,
            } as LoadValidationFailAction);
            return;
        }

        if (!errors.length) {
            dispatch({
                type: Types.LoadValidation_Finish,
                payload: {},
            } as LoadValidationFinishAction);
        }

        const result: Dictionary<ValidationError[]> = validateDuplicateAttributes(projectData.xsd);

        function pushError(id: string, err: ValidationError) {
            if (!result[id]) {
                result[id] = [] as ValidationError[];
            }
            result[id].push(err);
        }

        const li = calculateLineInformation(projectData.xsd);
        const startElementLines = li.elements.map(([line]) => line);
        for (const err of errors) {
            const idx = le(startElementLines, err.line);
            if (idx === -1) {
                continue;
            }
            const [_start, _end, id] = li.elements[idx];
            const attrs = li.attributes.get(id);
            if (attrs) {
                const attrStartColumns = attrs.map(([start]) => start);
                const attrIdx = le(attrStartColumns, err.column);
                if (attrIdx !== -1) {
                    const [start, len, id] = attrs[attrIdx];
                    if (start + len >= err.column) {
                        pushError(id, err);
                        continue;
                    }
                }
            }

            pushError(id, err);
        }

        dispatch({
            type: Types.LoadValidation_Finish,
            payload: result,
        } as LoadValidationFinishAction);
    }


([line]) => line


([start]) => start


(a, b) => a.position - b.position


id => tree[id]


el => el


ch => ch === '\n'


element => {
        const attributes = values(element.attributes);
        const counted = attributes.reduce(
            (store, attr) => Object.assign(store, { [attr.name]: (store[attr.name] || 0) + 1 }),
            {},
        );

        Object.keys(counted)
            .filter(name => counted[name] > 1)
            .forEach(name => {
                attributes.filter(attr => attr.name === name)
                    .forEach(attr => result[attr.id] = [{
                        column: 0,
                        line: 0,
                        // tslint:disable-next-line:max-line-length
                        message: `Duplicate attribute '${name}'. This will lead to unexpected results in the final XSD.`,
                    }]);
            });
    }


(store, attr) => Object.assign(store, { [attr.name]: (store[attr.name] || 0) + 1 })


name => counted[name] > 1


name => {
                attributes.filter(attr => attr.name === name)
                    .forEach(attr => result[attr.id] = [{
                        column: 0,
                        line: 0,
                        // tslint:disable-next-line:max-line-length
                        message: `Duplicate attribute '${name}'. This will lead to unexpected results in the final XSD.`,
                    }]);
            }


attr => attr.name === name


attr => result[attr.id] = [{
                        column: 0,
                        line: 0,
                        // tslint:disable-next-line:max-line-length
                        message: `Duplicate attribute '${name}'. This will lead to unexpected results in the final XSD.`,
                    }]


export interface LoadValidationFailAction extends PayloadAction<Error> {
    type: Types.LoadValidation_Fail;
    error: true;
}


export interface LoadValidationFinishAction extends PayloadAction<Dictionary<ValidationError[]>> {
    type: Types.LoadValidation_Finish;
}


export interface LoadValidationStartAction {
    type: Types.LoadValidation_Start;
}


interface RowColumnResult {
    /**
     * The starting and ending positions of attributes by corresponding element
     * and attribute ID.
     */
    attributes: Map<string, [number, number, string][]>;

    /**
     * The starting line and outer length of all elements.
     */
    elements: [number, number, string][];
}


export type ValidationActions =
    | LoadValidationFailAction
    | LoadValidationFinishAction
    | LoadValidationStartAction;


() => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      declarations: [AppComponent]
    }).compileComponents();
  }));

  it("should create the app", () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  });

  it(`should have as title 'crypto.com-chain-wallet'`, () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app.title).toEqual("crypto.com-chain-wallet");
  });
}


() => {
    TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      declarations: [AppComponent]
    }).compileComponents();
  }


() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app.title).toEqual("crypto.com-chain-wallet");
  }


async (
  client: AxiosHttpClient,
  payload: {}
): Promise<AxiosResponse> => {
  return client.request({
    url: '',
    method: 'post',
    data: payload,
  })
}


() => {
  return [
    {
      jsonrpc: '2.0',
      method: defaultSupportedMethods[0],
    },
    {
      id: '1',
      jsonrpc: '2.0',
      method: defaultSupportedMethods[0],
    },
    {
      id: 1,
      method: defaultSupportedMethods[0],
    },
    {
      id: 1,
      jsonrpc: 2.0,
      method: defaultSupportedMethods[0],
    },
    { id: 1, jsonrpc: '2.0' },
    { id: 1, jsonrpc: '2.0', method: unsupportedMethod },
  ]
}


() => {
  const fullnodeHandler: FullnodeHandler = new DummyFullnodeHandler()
  let fullnodeRpcServer: FullnodeRpcServer
  let baseUrl: string
  let client: AxiosHttpClient

  beforeEach(() => {
    fullnodeRpcServer = new FullnodeRpcServer(fullnodeHandler, host, port)

    fullnodeRpcServer.listen()

    baseUrl = `http://${host}:${port}`
    client = new AxiosHttpClient(baseUrl)
  })

  afterEach(() => {
    if (!!fullnodeRpcServer) {
      fullnodeRpcServer.close()
    }
  })

  describe('single requests', () => {
    it('should work for valid requests & methods', async () => {
      const results: AxiosResponse[] = await Promise.all(
        Array.from(defaultSupportedMethods).map((x) =>
          request(client, { id: 1, jsonrpc: '2.0', method: x })
        )
      )

      results.forEach((r) => {
        r.status.should.equal(200)

        r.data.should.haveOwnProperty('id')
        r.data['id'].should.equal(1)

        r.data.should.haveOwnProperty('jsonrpc')
        r.data['jsonrpc'].should.equal('2.0')

        r.data.should.haveOwnProperty('result')
        r.data['result'].should.equal(dummyResponse)
      })
    })

    it('fails on bad format or method', async () => {
      const results: AxiosResponse[] = await Promise.all(
        getBadPayloads().map((x) => request(client, x))
      )
      results.forEach((r) => {
        r.status.should.equal(200)

        r.data.should.haveOwnProperty('id')
        r.data.should.haveOwnProperty('jsonrpc')
        r.data.should.haveOwnProperty('error')

        r.data['jsonrpc'].should.equal('2.0')
      })
    })

    it('reverts properly', async () => {
      const result: AxiosResponse = await request(client, {
        id: 1,
        jsonrpc: '2.0',
        method: revertMethod,
      })

      result.status.should.equal(200)

      result.data.should.haveOwnProperty('id')
      result.data.should.haveOwnProperty('jsonrpc')
      result.data.should.haveOwnProperty('error')
      result.data['error'].should.haveOwnProperty('message')
      result.data['error'].should.haveOwnProperty('code')
      result.data['error']['message'].should.equal(
        JSONRPC_ERRORS.REVERT_ERROR.message
      )
      result.data['error']['code'].should.equal(
        JSONRPC_ERRORS.REVERT_ERROR.code
      )

      result.data['jsonrpc'].should.equal('2.0')
    })
  })

  describe('batch requests', () => {
    it('should work for valid requests & methods', async () => {
      const batchRequest = Array.from(
        defaultSupportedMethods
      ).map((method, id) => ({ jsonrpc: '2.0', id, method }))
      const result: AxiosResponse = await request(client, batchRequest)

      result.status.should.equal(200)
      const results = result.data

      results.forEach((r, id) => {
        r.should.haveOwnProperty('id')
        r['id'].should.equal(id)

        r.should.haveOwnProperty('jsonrpc')
        r['jsonrpc'].should.equal('2.0')

        r.should.haveOwnProperty('result')
        r['result'].should.equal(dummyResponse)
      })
    })
    it('should fail on bad format or for valid requests & methods', async () => {
      const result: AxiosResponse = await request(client, getBadPayloads())

      result.status.should.equal(200)
      const results = result.data

      results.forEach((r) => {
        r.should.haveOwnProperty('id')
        r.should.haveOwnProperty('jsonrpc')
        r.should.haveOwnProperty('error')

        r['jsonrpc'].should.equal('2.0')
      })
    })
    it('should not allow batches of batches', async () => {
      const batchOfBatches = Array.from(
        defaultSupportedMethods
      ).map((method, id) => [{ jsonrpc: '2.0', id, method }])
      const result: AxiosResponse = await request(client, batchOfBatches)

      result.status.should.equal(200)
      const results = result.data

      results.forEach((r) => {
        r.should.haveOwnProperty('id')
        r.should.haveOwnProperty('jsonrpc')
        r.should.haveOwnProperty('error')
        r['error'].should.haveOwnProperty('message')
        r['error'].should.haveOwnProperty('code')
        r['error']['message'].should.equal(
          JSONRPC_ERRORS.INVALID_REQUEST.message
        )
        r['error']['code'].should.equal(JSONRPC_ERRORS.INVALID_REQUEST.code)

        r['jsonrpc'].should.equal('2.0')
      })
    })
  })
}


() => {
    fullnodeRpcServer = new FullnodeRpcServer(fullnodeHandler, host, port)

    fullnodeRpcServer.listen()

    baseUrl = `http://${host}:${port}`
    client = new AxiosHttpClient(baseUrl)
  }


() => {
    if (!!fullnodeRpcServer) {
      fullnodeRpcServer.close()
    }
  }


() => {
    it('should work for valid requests & methods', async () => {
      const results: AxiosResponse[] = await Promise.all(
        Array.from(defaultSupportedMethods).map((x) =>
          request(client, { id: 1, jsonrpc: '2.0', method: x })
        )
      )

      results.forEach((r) => {
        r.status.should.equal(200)

        r.data.should.haveOwnProperty('id')
        r.data['id'].should.equal(1)

        r.data.should.haveOwnProperty('jsonrpc')
        r.data['jsonrpc'].should.equal('2.0')

        r.data.should.haveOwnProperty('result')
        r.data['result'].should.equal(dummyResponse)
      })
    })

    it('fails on bad format or method', async () => {
      const results: AxiosResponse[] = await Promise.all(
        getBadPayloads().map((x) => request(client, x))
      )
      results.forEach((r) => {
        r.status.should.equal(200)

        r.data.should.haveOwnProperty('id')
        r.data.should.haveOwnProperty('jsonrpc')
        r.data.should.haveOwnProperty('error')

        r.data['jsonrpc'].should.equal('2.0')
      })
    })

    it('reverts properly', async () => {
      const result: AxiosResponse = await request(client, {
        id: 1,
        jsonrpc: '2.0',
        method: revertMethod,
      })

      result.status.should.equal(200)

      result.data.should.haveOwnProperty('id')
      result.data.should.haveOwnProperty('jsonrpc')
      result.data.should.haveOwnProperty('error')
      result.data['error'].should.haveOwnProperty('message')
      result.data['error'].should.haveOwnProperty('code')
      result.data['error']['message'].should.equal(
        JSONRPC_ERRORS.REVERT_ERROR.message
      )
      result.data['error']['code'].should.equal(
        JSONRPC_ERRORS.REVERT_ERROR.code
      )

      result.data['jsonrpc'].should.equal('2.0')
    })
  }


async () => {
      const results: AxiosResponse[] = await Promise.all(
        Array.from(defaultSupportedMethods).map((x) =>
          request(client, { id: 1, jsonrpc: '2.0', method: x })
        )
      )

      results.forEach((r) => {
        r.status.should.equal(200)

        r.data.should.haveOwnProperty('id')
        r.data['id'].should.equal(1)

        r.data.should.haveOwnProperty('jsonrpc')
        r.data['jsonrpc'].should.equal('2.0')

        r.data.should.haveOwnProperty('result')
        r.data['result'].should.equal(dummyResponse)
      })
    }


(x) =>
          request(client, { id: 1, jsonrpc: '2.0', method: x })


(r) => {
        r.status.should.equal(200)

        r.data.should.haveOwnProperty('id')
        r.data['id'].should.equal(1)

        r.data.should.haveOwnProperty('jsonrpc')
        r.data['jsonrpc'].should.equal('2.0')

        r.data.should.haveOwnProperty('result')
        r.data['result'].should.equal(dummyResponse)
      }


async () => {
      const results: AxiosResponse[] = await Promise.all(
        getBadPayloads().map((x) => request(client, x))
      )
      results.forEach((r) => {
        r.status.should.equal(200)

        r.data.should.haveOwnProperty('id')
        r.data.should.haveOwnProperty('jsonrpc')
        r.data.should.haveOwnProperty('error')

        r.data['jsonrpc'].should.equal('2.0')
      })
    }


(x) => request(client, x)


(r) => {
        r.status.should.equal(200)

        r.data.should.haveOwnProperty('id')
        r.data.should.haveOwnProperty('jsonrpc')
        r.data.should.haveOwnProperty('error')

        r.data['jsonrpc'].should.equal('2.0')
      }


async () => {
      const result: AxiosResponse = await request(client, {
        id: 1,
        jsonrpc: '2.0',
        method: revertMethod,
      })

      result.status.should.equal(200)

      result.data.should.haveOwnProperty('id')
      result.data.should.haveOwnProperty('jsonrpc')
      result.data.should.haveOwnProperty('error')
      result.data['error'].should.haveOwnProperty('message')
      result.data['error'].should.haveOwnProperty('code')
      result.data['error']['message'].should.equal(
        JSONRPC_ERRORS.REVERT_ERROR.message
      )
      result.data['error']['code'].should.equal(
        JSONRPC_ERRORS.REVERT_ERROR.code
      )

      result.data['jsonrpc'].should.equal('2.0')
    }


() => {
    it('should work for valid requests & methods', async () => {
      const batchRequest = Array.from(
        defaultSupportedMethods
      ).map((method, id) => ({ jsonrpc: '2.0', id, method }))
      const result: AxiosResponse = await request(client, batchRequest)

      result.status.should.equal(200)
      const results = result.data

      results.forEach((r, id) => {
        r.should.haveOwnProperty('id')
        r['id'].should.equal(id)

        r.should.haveOwnProperty('jsonrpc')
        r['jsonrpc'].should.equal('2.0')

        r.should.haveOwnProperty('result')
        r['result'].should.equal(dummyResponse)
      })
    })
    it('should fail on bad format or for valid requests & methods', async () => {
      const result: AxiosResponse = await request(client, getBadPayloads())

      result.status.should.equal(200)
      const results = result.data

      results.forEach((r) => {
        r.should.haveOwnProperty('id')
        r.should.haveOwnProperty('jsonrpc')
        r.should.haveOwnProperty('error')

        r['jsonrpc'].should.equal('2.0')
      })
    })
    it('should not allow batches of batches', async () => {
      const batchOfBatches = Array.from(
        defaultSupportedMethods
      ).map((method, id) => [{ jsonrpc: '2.0', id, method }])
      const result: AxiosResponse = await request(client, batchOfBatches)

      result.status.should.equal(200)
      const results = result.data

      results.forEach((r) => {
        r.should.haveOwnProperty('id')
        r.should.haveOwnProperty('jsonrpc')
        r.should.haveOwnProperty('error')
        r['error'].should.haveOwnProperty('message')
        r['error'].should.haveOwnProperty('code')
        r['error']['message'].should.equal(
          JSONRPC_ERRORS.INVALID_REQUEST.message
        )
        r['error']['code'].should.equal(JSONRPC_ERRORS.INVALID_REQUEST.code)

        r['jsonrpc'].should.equal('2.0')
      })
    })
  }


async () => {
      const batchRequest = Array.from(
        defaultSupportedMethods
      ).map((method, id) => ({ jsonrpc: '2.0', id, method }))
      const result: AxiosResponse = await request(client, batchRequest)

      result.status.should.equal(200)
      const results = result.data

      results.forEach((r, id) => {
        r.should.haveOwnProperty('id')
        r['id'].should.equal(id)

        r.should.haveOwnProperty('jsonrpc')
        r['jsonrpc'].should.equal('2.0')

        r.should.haveOwnProperty('result')
        r['result'].should.equal(dummyResponse)
      })
    }


(method, id) => ({ jsonrpc: '2.0', id, method })


(r, id) => {
        r.should.haveOwnProperty('id')
        r['id'].should.equal(id)

        r.should.haveOwnProperty('jsonrpc')
        r['jsonrpc'].should.equal('2.0')

        r.should.haveOwnProperty('result')
        r['result'].should.equal(dummyResponse)
      }


async () => {
      const result: AxiosResponse = await request(client, getBadPayloads())

      result.status.should.equal(200)
      const results = result.data

      results.forEach((r) => {
        r.should.haveOwnProperty('id')
        r.should.haveOwnProperty('jsonrpc')
        r.should.haveOwnProperty('error')

        r['jsonrpc'].should.equal('2.0')
      })
    }


(r) => {
        r.should.haveOwnProperty('id')
        r.should.haveOwnProperty('jsonrpc')
        r.should.haveOwnProperty('error')

        r['jsonrpc'].should.equal('2.0')
      }


async () => {
      const batchOfBatches = Array.from(
        defaultSupportedMethods
      ).map((method, id) => [{ jsonrpc: '2.0', id, method }])
      const result: AxiosResponse = await request(client, batchOfBatches)

      result.status.should.equal(200)
      const results = result.data

      results.forEach((r) => {
        r.should.haveOwnProperty('id')
        r.should.haveOwnProperty('jsonrpc')
        r.should.haveOwnProperty('error')
        r['error'].should.haveOwnProperty('message')
        r['error'].should.haveOwnProperty('code')
        r['error']['message'].should.equal(
          JSONRPC_ERRORS.INVALID_REQUEST.message
        )
        r['error']['code'].should.equal(JSONRPC_ERRORS.INVALID_REQUEST.code)

        r['jsonrpc'].should.equal('2.0')
      })
    }


(method, id) => [{ jsonrpc: '2.0', id, method }]


(r) => {
        r.should.haveOwnProperty('id')
        r.should.haveOwnProperty('jsonrpc')
        r.should.haveOwnProperty('error')
        r['error'].should.haveOwnProperty('message')
        r['error'].should.haveOwnProperty('code')
        r['error']['message'].should.equal(
          JSONRPC_ERRORS.INVALID_REQUEST.message
        )
        r['error']['code'].should.equal(JSONRPC_ERRORS.INVALID_REQUEST.code)

        r['jsonrpc'].should.equal('2.0')
      }


class DummyFullnodeHandler implements FullnodeHandler {
  public async handleRequest(
    method: string,
    params: string[]
  ): Promise<string> {
    if (method === unsupportedMethod) {
      throw new UnsupportedMethodError()
    }
    if (method === revertMethod) {
      throw new RevertError()
    }
    return dummyResponse
  }
}


public async handleRequest(
    method: string,
    params: string[]
  ): Promise<string> {
    if (method === unsupportedMethod) {
      throw new UnsupportedMethodError()
    }
    if (method === revertMethod) {
      throw new RevertError()
    }
    return dummyResponse
  }


/** @docs-private */
export function OWL_LUXON_DATE_TIME_ADAPTER_OPTIONS_FACTORY(): OwlLuxonDateTimeAdapterOptions {
    return {
        useUtc: false,
        parseStrict: false,
    };
}


/** Creates an array and fills it with values. */
function range<T>(length: number, valueFunction: (index: number) => T): T[] {
    const valuesArray = Array(length);
    for (let i = 0; i < length; i++) {
        valuesArray[i] = valueFunction(i);
    }
    return valuesArray;
}


@Injectable()
export class LuxonDateTimeAdapter extends DateTimeAdapter<DateTime> {
    getDate(date: DateTime): number {
        throw new Error('Method not implemented.');
    }
    getTime(date: DateTime): number {
        throw new Error('Method not implemented.');
    }
    clone(date: DateTime): DateTime {
        return date;
    }

    constructor(
        @Optional()
        @Inject(OWL_DATE_TIME_LOCALE)
        private owlDateTimeLocale: string,
        @Optional()
        @Inject(OWL_LUXON_DATE_TIME_ADAPTER_OPTIONS)
        private options?: OwlLuxonDateTimeAdapterOptions
    ) {
        super();
        this.setLocale(owlDateTimeLocale || DateTime.now().locale);
    }

    public setLocale(locale: string) {
        super.setLocale(locale);
    }

    public getYear(date: DateTime): number {
        return date.year;
    }

    public getMonth(date: DateTime): number {
        return date.month;
    }

    public getDay(date: DateTime): number {
        return date.day;
    }

    public getHours(date: DateTime): number {
        return date.hour;
    }

    public getMinutes(date: DateTime): number {
        return date.minute;
    }

    public getSeconds(date: DateTime): number {
        return date.second;
    }

    public getNumDaysInMonth(date: DateTime): number {
        return date.daysInMonth;
    }

    public differenceInCalendarDays(
        dateLeft: DateTime,
        dateRight: DateTime
    ): number {
        return dateLeft.diff(dateRight, 'days').days;
    }

    public getYearName(date: DateTime): string {
        return date.toFormat('YYYY');
    }

    public getMonthNames(style: 'long' | 'short' | 'narrow'): string[] {
        return style === 'long' ? Info.months('long') : Info.months('short');
    }

    public getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] {
        if (style === 'long') {
            return Info.weekdays('long');
        }
        return Info.weekdays('short');
    }

    public getDateNames(): string[] {
      return [];
    }

    public toIso8601(date: DateTime): string {
        return date.toISO();
    }

    public isEqual(dateLeft: DateTime, dateRight: DateTime): boolean {
        if (dateLeft && dateRight) {
            return dateLeft === dateRight;
        }
        return dateLeft === dateRight;
    }

    public isSameDay(dateLeft: DateTime, dateRight: DateTime): boolean {
        if (dateLeft && dateRight) {
            return dateLeft.hasSame(dateRight, 'day');
        }

        return dateLeft === dateRight;
    }

    public isValid(date: DateTime): boolean {
        return date.isValid;
    }

    public invalid(): DateTime {
        return DateTime.invalid('');
    }

    public isDateInstance(obj: any): boolean {
        return DateTime.isDateTime(obj);
    }

    public addCalendarYears(date: DateTime, amount: number): DateTime {
        return date.plus({ years: amount });
    }

    public addCalendarMonths(date: DateTime, amount: number): DateTime {
        return date.plus({ months: amount });
    }

    public addCalendarDays(date: DateTime, amount: number): DateTime {
        return date.plus({ days: amount });
    }

    public setHours(date: DateTime, amount: number): DateTime {
        return date.set({ hour: amount });
    }

    public setMinutes(date: DateTime, amount: number): DateTime {
        return date.set({ minute: amount });
    }

    public setSeconds(date: DateTime, amount: number): DateTime {
        return date.set({ second: amount });
    }

    public createDate(year: number, month: number, date: number): DateTime;
    public createDate(
        year: number,
        month: number,
        date: number,
        hours: number = 0,
        minutes: number = 0,
        seconds: number = 0
    ): DateTime {
        if (month < 1 || month > 12) {
            throw Error(
                `Invalid month index "${month}". Month index has to be between 1 and 12.`
            );
        }

        if (date < 1) {
            throw Error(
                `Invalid date "${date}". Date has to be greater than 0.`
            );
        }

        if (hours < 0 || hours > 23) {
            throw Error(
                `Invalid hours "${hours}". Hours has to be between 0 and 23.`
            );
        }

        if (minutes < 0 || minutes > 59) {
            throw Error(
                `Invalid minutes "${minutes}". Minutes has to between 0 and 59.`
            );
        }

        if (seconds < 0 || seconds > 59) {
            throw Error(
                `Invalid seconds "${seconds}". Seconds has to be between 0 and 59.`
            );
        }

        const result = this.createDateTime({
            year,
            month,
            date,
            hours,
            minutes,
            seconds,
        }).setLocale(this.getLocale());

        // If the result isn't valid, the date must have been out of bounds for this month.
        if (!result.isValid) {
            throw Error(
                `Invalid date "${date}" for month with index "${month}".`
            );
        }

        return result;
    }

    public now(): DateTime {
        return DateTime.now();
    }

    public format(date: DateTime, displayFormat: any): string {
        if (!this.isValid(date)) {
            throw Error('LuxonDateTimeAdapter: Cannot format invalid date.');
        }
        return date.toFormat(displayFormat);
    }

    public parse(value: any, parseFormat: any): DateTime | null {
        if (value && value instanceof Date) {
            return DateTime.fromJSDate(value);
        }
        if (value && typeof value === 'string') {
            return DateTime.fromFormat(value, parseFormat);
        }
        return value
            ? this.createDateTime(value).setLocale(this.getLocale())
            : null;
    }

    get parseStrict() {
        return this.options && this.options.parseStrict;
    }

    /**
     * Returns the given value if given a valid DateTime or null. Deserializes valid ISO 8601 strings
     * (https://www.ietf.org/rfc/rfc3339.txt) and valid Date objects into valid DateTimes and empty
     * string into null. Returns an invalid date for all other values.
     */
    deserialize(value: any): DateTime | null {
        let date: DateTime | null;
        if (value instanceof Date) {
            date = DateTime.fromJSDate(value);
        }
        if (typeof value === 'string') {
            if (!value) {
                return null;
            }
            date = DateTime.fromISO(value);
        }
        if (date && this.isValid(date)) {
            return date;
        }
        return super.deserialize(value);
    }

    /** Creates a DateTime instance while respecting the current UTC settings. */
    private createDateTime(...args: any[]): DateTime {
        return this.options && this.options.useUtc
            ? DateTime.utc(...args)
            : DateTime.local(...args);
    }
}


/** Configurable options for {@see LuxonDateAdapter}. */
export interface OwlLuxonDateTimeAdapterOptions {
    /**
     * Turns the use of utc dates on or off.
     * Changing this will change how the DateTimePicker output value.
     * {@default false}
     */
    useUtc: boolean;

    /**
     * Turns the use of strict string parsing in luxon.
     * Changing this will change how the DateTimePicker interprets input.
     * {@default false}
     */
    parseStrict: boolean;
}


getDate(date: DateTime): number {
        throw new Error('Method not implemented.');
    }


getTime(date: DateTime): number {
        throw new Error('Method not implemented.');
    }


clone(date: DateTime): DateTime {
        return date;
    }


public setLocale(locale: string) {
        super.setLocale(locale);
    }


public getYear(date: DateTime): number {
        return date.year;
    }


public getMonth(date: DateTime): number {
        return date.month;
    }


public getDay(date: DateTime): number {
        return date.day;
    }


public getHours(date: DateTime): number {
        return date.hour;
    }


public getMinutes(date: DateTime): number {
        return date.minute;
    }


public getSeconds(date: DateTime): number {
        return date.second;
    }


public getNumDaysInMonth(date: DateTime): number {
        return date.daysInMonth;
    }


public differenceInCalendarDays(
        dateLeft: DateTime,
        dateRight: DateTime
    ): number {
        return dateLeft.diff(dateRight, 'days').days;
    }


public getYearName(date: DateTime): string {
        return date.toFormat('YYYY');
    }


public getMonthNames(style: 'long' | 'short' | 'narrow'): string[] {
        return style === 'long' ? Info.months('long') : Info.months('short');
    }


public getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] {
        if (style === 'long') {
            return Info.weekdays('long');
        }
        return Info.weekdays('short');
    }


public getDateNames(): string[] {
      return [];
    }


public toIso8601(date: DateTime): string {
        return date.toISO();
    }


public isEqual(dateLeft: DateTime, dateRight: DateTime): boolean {
        if (dateLeft && dateRight) {
            return dateLeft === dateRight;
        }
        return dateLeft === dateRight;
    }


public isSameDay(dateLeft: DateTime, dateRight: DateTime): boolean {
        if (dateLeft && dateRight) {
            return dateLeft.hasSame(dateRight, 'day');
        }

        return dateLeft === dateRight;
    }


public isValid(date: DateTime): boolean {
        return date.isValid;
    }


public invalid(): DateTime {
        return DateTime.invalid('');
    }


public isDateInstance(obj: any): boolean {
        return DateTime.isDateTime(obj);
    }


public addCalendarYears(date: DateTime, amount: number): DateTime {
        return date.plus({ years: amount });
    }


public addCalendarMonths(date: DateTime, amount: number): DateTime {
        return date.plus({ months: amount });
    }


public addCalendarDays(date: DateTime, amount: number): DateTime {
        return date.plus({ days: amount });
    }


public setHours(date: DateTime, amount: number): DateTime {
        return date.set({ hour: amount });
    }


public setMinutes(date: DateTime, amount: number): DateTime {
        return date.set({ minute: amount });
    }


public setSeconds(date: DateTime, amount: number): DateTime {
        return date.set({ second: amount });
    }


public createDate(year: number, month: number, date: number): DateTime;


public createDate(
        year: number,
        month: number,
        date: number,
        hours: number = 0,
        minutes: number = 0,
        seconds: number = 0
    ): DateTime {
        if (month < 1 || month > 12) {
            throw Error(
                `Invalid month index "${month}". Month index has to be between 1 and 12.`
            );
        }

        if (date < 1) {
            throw Error(
                `Invalid date "${date}". Date has to be greater than 0.`
            );
        }

        if (hours < 0 || hours > 23) {
            throw Error(
                `Invalid hours "${hours}". Hours has to be between 0 and 23.`
            );
        }

        if (minutes < 0 || minutes > 59) {
            throw Error(
                `Invalid minutes "${minutes}". Minutes has to between 0 and 59.`
            );
        }

        if (seconds < 0 || seconds > 59) {
            throw Error(
                `Invalid seconds "${seconds}". Seconds has to be between 0 and 59.`
            );
        }

        const result = this.createDateTime({
            year,
            month,
            date,
            hours,
            minutes,
            seconds,
        }).setLocale(this.getLocale());

        // If the result isn't valid, the date must have been out of bounds for this month.
        if (!result.isValid) {
            throw Error(
                `Invalid date "${date}" for month with index "${month}".`
            );
        }

        return result;
    }


public now(): DateTime {
        return DateTime.now();
    }


public format(date: DateTime, displayFormat: any): string {
        if (!this.isValid(date)) {
            throw Error('LuxonDateTimeAdapter: Cannot format invalid date.');
        }
        return date.toFormat(displayFormat);
    }


public parse(value: any, parseFormat: any): DateTime | null {
        if (value && value instanceof Date) {
            return DateTime.fromJSDate(value);
        }
        if (value && typeof value === 'string') {
            return DateTime.fromFormat(value, parseFormat);
        }
        return value
            ? this.createDateTime(value).setLocale(this.getLocale())
            : null;
    }


/**
     * Returns the given value if given a valid DateTime or null. Deserializes valid ISO 8601 strings
     * (https://www.ietf.org/rfc/rfc3339.txt) and valid Date objects into valid DateTimes and empty
     * string into null. Returns an invalid date for all other values.
     */
deserialize(value: any): DateTime | null {
        let date: DateTime | null;
        if (value instanceof Date) {
            date = DateTime.fromJSDate(value);
        }
        if (typeof value === 'string') {
            if (!value) {
                return null;
            }
            date = DateTime.fromISO(value);
        }
        if (date && this.isValid(date)) {
            return date;
        }
        return super.deserialize(value);
    }


/** Creates a DateTime instance while respecting the current UTC settings. */
private createDateTime(...args: any[]): DateTime {
        return this.options && this.options.useUtc
            ? DateTime.utc(...args)
            : DateTime.local(...args);
    }


(width: number, height: number) => {
  var canvas = document.createElement('canvas');
  canvas.width = width;
  canvas.height = height;
  canvas.oncontextmenu = () => false;
  canvas.onselectstart = () => false;
  return canvas;
}


(canvas: HTMLCanvasElement) => {
  const context = canvas.getContext('2d') as any;
  const devicePixelRatio = window.devicePixelRatio || 1;
  const backingStorePixelRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio
    || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio
    || context.backingStorePixelRatio || 1;
  return devicePixelRatio / backingStorePixelRatio;
}


(imagePath: string) => {
  const image = textures[imagePath];
  if (image) {
    return image;
  }
  const image2 = textures[imagePath] = new Image();
  image2.src = imagePath;
  return image2;
}


(time: number) => {
        this.frameRequestId = _requestAnimationFrame(loop);
        this.draw();
      }


/**
   * Continuously updates the render canvas on the `requestAnimationFrame` event.
   * @method run
   * @param {render} this
   */
public run() {
    if (_requestAnimationFrame !== undefined) {
      const loop = (time: number) => {
        this.frameRequestId = _requestAnimationFrame(loop);
        this.draw();
      };
      loop(1);//
    }
  }


/**
   * Ends execution of `Render.run` on the given `render`, by canceling the animation frame request event loop.
   * @method stop
   * @param {render} render
   */
public stop() {
    if (_cancelAnimationFrame !== undefined) {
      _cancelAnimationFrame(this.frameRequestId);
    }
  }


/**
   * Sets the pixel ratio of the renderer and updates the canvas.
   * To automatically detect the correct ratio, pass the string `'auto'` for `pixelRatio`.
   * @method setPixelRatio
   * @param {render} this
   * @param {number} pixelRatio
   */
private setPixelRatio(pixelRatio?: number) {
    var options = this.options;
    var canvas = this.canvas;

    if (pixelRatio === undefined) {
      pixelRatio = _getPixelRatio(canvas);
    }

    options.pixelRatio = pixelRatio;
    canvas.setAttribute('data-pixel-ratio', `${pixelRatio}`);
    canvas.width = options.width * pixelRatio;
    canvas.height = options.height * pixelRatio;
    canvas.style.width = options.width + 'px';
    canvas.style.height = options.height + 'px';
  }


/**
   * Positions and sizes the viewport around the given object bounds.
   * Objects must have at least one of the following properties:
   * - `object.bounds`
   * - `object.position`
   * - `object.min` and `object.max`
   * - `object.x` and `object.y`
   * @method lookAt
   * @param {render} this
   * @param {object[]} objects
   * @param {vector} [padding]
   * @param {bool} [center=true]
   */
public lookAt(objects: any[], padding?: Vector, center = true) {
    objects = Array.isArray(objects) ? objects : [objects];
    if (padding === undefined) {
      padding = new Vector();
    }

    // find bounds of all objects
    var bounds = new Bounds(Infinity, Infinity, -Infinity, -Infinity);

    for (var i = 0; i < objects.length; i += 1) {
      var object = objects[i],
        min = object.bounds ? object.bounds.min : (object.min || object.position || object),
        max = object.bounds ? object.bounds.max : (object.max || object.position || object);

      if (min && max) {
        if (min.x < bounds.min.x)
          bounds.min.x = min.x;

        if (max.x > bounds.max.x)
          bounds.max.x = max.x;

        if (min.y < bounds.min.y)
          bounds.min.y = min.y;

        if (max.y > bounds.max.y)
          bounds.max.y = max.y;
      }
    }

    // find ratios
    var width = (bounds.max.x - bounds.min.x) + 2 * padding.x,
      height = (bounds.max.y - bounds.min.y) + 2 * padding.y,
      viewHeight = this.canvas.height,
      viewWidth = this.canvas.width,
      outerRatio = viewWidth / viewHeight,
      innerRatio = width / height,
      scaleX = 1,
      scaleY = 1;

    // find scale factor
    if (innerRatio > outerRatio) {
      scaleY = innerRatio / outerRatio;
    } else {
      scaleX = outerRatio / innerRatio;
    }

    // enable bounds
    this.options.hasBounds = true;

    // position and size
    this.bounds.min.x = bounds.min.x;
    this.bounds.max.x = bounds.min.x + width * scaleX;
    this.bounds.min.y = bounds.min.y;
    this.bounds.max.y = bounds.min.y + height * scaleY;

    // center
    if (center) {
      this.bounds.min.x += width * 0.5 - (width * scaleX) * 0.5;
      this.bounds.max.x += width * 0.5 - (width * scaleX) * 0.5;
      this.bounds.min.y += height * 0.5 - (height * scaleY) * 0.5;
      this.bounds.max.y += height * 0.5 - (height * scaleY) * 0.5;
    }

    // padding
    this.bounds.min.x -= padding.x;
    this.bounds.max.x -= padding.x;
    this.bounds.min.y -= padding.y;
    this.bounds.max.y -= padding.y;

    // update mouse
    if (this.mouse) {
      this.mouse.setScale(new Vector(
        (this.bounds.max.x - this.bounds.min.x) / this.canvas.width,
        (this.bounds.max.y - this.bounds.min.y) / this.canvas.height
      ));
      this.mouse.setOffset(this.bounds.min);
    }
  }


/**
   * Applies viewport transforms based on `render.bounds` to a render context.
   * @method startViewTransform
   * @param {render} this
   */
public startViewTransform() {
    var boundsWidth = this.bounds.max.x - this.bounds.min.x,
      boundsHeight = this.bounds.max.y - this.bounds.min.y,
      boundsScaleX = boundsWidth / this.options.width,
      boundsScaleY = boundsHeight / this.options.height;

    this.context.setTransform(
      this.options.pixelRatio / boundsScaleX, 0, 0,
      this.options.pixelRatio / boundsScaleY, 0, 0
    );

    this.context.translate(-this.bounds.min.x, -this.bounds.min.y);
  }


/**
   * Resets all transforms on the render context.
   * @method endViewTransform
   * @param {render} render
   */
public endViewTransform() {
    this.context.setTransform(this.options.pixelRatio, 0, 0, this.options.pixelRatio, 0, 0);
  }


public draw() {
    const engine = this.engine;
    const world = engine.world;
    const canvas = this.canvas;
    const context = this.context;
    const options = this.options;
    const allBodies = world.allBodies();
    const allConstraints = world.allConstraints();
    const background = options.wireframes ? options.wireframeBackground : options.background;
    var bodies: Body[] = [];
    var constraints: Constraint[] = [];

    var event = {
      timestamp: engine.timing.timestamp
    }

    Events.trigger(this, 'beforeRender', event);

    // apply background if it has changed
    if (this.currentBackground !== background) {
      this.applyBackground(background);
    }

    // clear the canvas with a transparent fill, to allow the canvas background to show
    context.globalCompositeOperation = 'source-in';
    context.fillStyle = "transparent";
    context.fillRect(0, 0, canvas.width, canvas.height);
    context.globalCompositeOperation = 'source-over';

    // handle bounds
    if (options.hasBounds) {
      // filter out bodies that are not in view
      for (var i = 0; i < allBodies.length; i++) {
        var body = allBodies[i];
        if (Bounds.overlaps(body.bounds, this.bounds))
          bodies.push(body);
      }

      // filter out constraints that are not in view
      for (var i = 0; i < allConstraints.length; i++) {
        var constraint = allConstraints[i],
          bodyA = constraint.bodyA,
          bodyB = constraint.bodyB,
          pointAWorld = constraint.pointA,
          pointBWorld = constraint.pointB;

        if (bodyA) pointAWorld = Vector.add(bodyA.position, constraint.pointA!);
        if (bodyB) pointBWorld = Vector.add(bodyB.position, constraint.pointB!);

        if (!pointAWorld || !pointBWorld)
          continue;

        if (Bounds.contains(this.bounds, pointAWorld) || Bounds.contains(this.bounds, pointBWorld))
          constraints.push(constraint);
      }

      // transform the view
      this.startViewTransform();

      // update mouse
      if (this.mouse) {
        this.mouse.setScale(new Vector(
          (this.bounds.max.x - this.bounds.min.x) / this.options.width,
          (this.bounds.max.y - this.bounds.min.y) / this.options.height
        ));
        this.mouse.setOffset(this.bounds.min);
      }
    } else {
      constraints = allConstraints;
      bodies = allBodies;

      if (this.options.pixelRatio !== 1) {
        this.context.setTransform(this.options.pixelRatio, 0, 0, this.options.pixelRatio, 0, 0);
      }
    }

    if (!options.wireframes || (engine.enableSleeping && options.showSleeping)) {
      // fully featured rendering of bodies
      this.bodies(bodies, context);
    } else {
      if (options.showConvexHulls)
        this.bodyConvexHulls(bodies, context);

      // optimised method for wireframes only
      this.bodyWireframes(bodies, context);
    }

    if (options.showBounds)
      this.bodyBounds(bodies, context);

    if (options.showAxes || options.showAngleIndicator)
      this.bodyAxes(bodies, context);

    if (options.showPositions)
      this.bodyPositions(bodies, context);

    if (options.showVelocity)
      this.bodyVelocity(bodies, context);

    if (options.showIds)
      this.bodyIds(bodies, context);

    if (options.showSeparations)
      this.separations(engine.pairs.list, context);

    if (options.showCollisions)
      this.collisions(engine.pairs.list, context);

    if (options.showVertexNumbers)
      this.vertexNumbers(bodies, context);

    if (options.showMousePosition)
      this.mousePosition(this.mouse, context);

    Render.constraints(constraints, context);

    if (options.showBroadphase && engine.broadphase instanceof Grid)
      this.grid(engine.broadphase, context);

    if (options.showDebug)
      this.debug(context);

    if (options.hasBounds) {
      // revert view transforms
      this.endViewTransform();
    }

    Events.trigger(this, 'afterRender', event);
  }


/**
  * Applies the background to the canvas using CSS.
  * @method applyBackground
  * @private
  * @param {render} render
  * @param {string} background
  */
private applyBackground(background: string) {
    var cssBackground = background;
    if (/(jpg|gif|png)$/.test(background))
      cssBackground = 'url(' + background + ')';
    this.canvas.style.background = cssBackground;
    this.canvas.style.backgroundSize = "contain";
    this.currentBackground = background;
  }


/**
   * Description
   * @private
   * @method debug
   * @param {render} this
   * @param {RenderingContext} context
   */
private debug(context: CanvasRenderingContext2D) {
    var c = context,
      engine = this.engine,
      world = engine.world,
      // metrics = engine.metrics,
      options = this.options,
      bodies = world.allBodies(),
      space = "    ";

    if (engine.timing.timestamp - (this.debugTimestamp) >= 500) {
      var text = "";

      // if (metrics.timing) {
      //   text += "fps: " + Math.round(metrics.timing.fps) + space;
      // }

      // // @if DEBUG
      // if (metrics.extended) {
      //   if (metrics.timing) {
      //     text += "delta: " + metrics.timing.delta.toFixed(3) + space;
      //     text += "correction: " + metrics.timing.correction.toFixed(3) + space;
      //   }

      //   text += "bodies: " + bodies.length + space;

      //   if (engine.broadphase.controller === Grid)
      //     text += "buckets: " + metrics.buckets + space;

      //   text += "\n";

      //   text += "collisions: " + metrics.collisions + space;
      //   text += "pairs: " + engine.pairs.list.length + space;
      //   text += "broad: " + metrics.broadEff + space;
      //   text += "mid: " + metrics.midEff + space;
      //   text += "narrow: " + metrics.narrowEff + space;
      // }
      // @endif

      this.debugString = text;
      this.debugTimestamp = engine.timing.timestamp;
    }

    if (this.debugString) {
      c.font = "12px Arial";

      if (options.wireframes) {
        c.fillStyle = 'rgba(255,255,255,0.5)';
      } else {
        c.fillStyle = 'rgba(0,0,0,0.5)';
      }

      var split = this.debugString.split('\n');

      for (var i = 0; i < split.length; i++) {
        c.fillText(split[i], 50, 50 + i * 18);
      }
    }
  }


/**
   * Description
   * @private
   * @method constraints
   * @param {constraint[]} constraints
   * @param {RenderingContext} context
   */
private static constraints(constraints: Constraint[], context: CanvasRenderingContext2D) {
    var c = context;

    for (var i = 0; i < constraints.length; i++) {
      var constraint = constraints[i];

      if (!constraint.visible || !constraint.pointA || !constraint.pointB)
        continue;

      var bodyA = constraint.bodyA,
        bodyB = constraint.bodyB,
        start,
        end = Vector.Null;

      if (bodyA) {
        start = Vector.add(bodyA.position, constraint.pointA);
      } else {
        start = constraint.pointA;
      }

      if (constraint.renderType === 'pin') {
        c.beginPath();
        c.arc(start.x, start.y, 3, 0, 2 * Math.PI);
        c.closePath();
      } else {
        if (bodyB) {
          end = Vector.add(bodyB.position, constraint.pointB);
        } else {
          end = constraint.pointB;
        }

        c.beginPath();
        c.moveTo(start.x, start.y);

        if (constraint.renderType === 'spring') {
          var delta = Vector.sub(end, start),
            normal = Vector.perp(Vector.normalise(delta)),
            coils = Math.ceil(Common.clamp(constraint.length / 5, 12, 20)),
            offset;

          for (var j = 1; j < coils; j += 1) {
            offset = j % 2 === 0 ? 1 : -1;

            c.lineTo(
              start.x + delta.x * (j / coils) + normal.x * offset * 4,
              start.y + delta.y * (j / coils) + normal.y * offset * 4
            );
          }
        }

        c.lineTo(end.x, end.y);
      }

      if (constraint.lineWidth) {
        c.lineWidth = constraint.lineWidth;
        c.strokeStyle = constraint.strokeStyle;
        c.stroke();
      }

      if (constraint.anchors) {
        c.fillStyle = constraint.strokeStyle;
        c.beginPath();
        c.arc(start.x, start.y, 3, 0, 2 * Math.PI);
        c.arc(end.x, end.y, 3, 0, 2 * Math.PI);
        c.closePath();
        c.fill();
      }
    }
  }


/**
   * Description
   * @private
   * @method bodyShadows
   * @param {render} render
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private bodyShadows(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context,
      engine = this.engine;

    for (var i = 0; i < bodies.length; i++) {
      var body = bodies[i];

      if (!body.visible)
        continue;

      if (body.circleRadius) {
        c.beginPath();
        c.arc(body.position.x, body.position.y, body.circleRadius, 0, 2 * Math.PI);
        c.closePath();
      } else {
        c.beginPath();
        c.moveTo(body.vertices[0].x, body.vertices[0].y);
        for (var j = 1; j < body.vertices.length; j++) {
          c.lineTo(body.vertices[j].x, body.vertices[j].y);
        }
        c.closePath();
      }

      var distanceX = body.position.x - this.options.width * 0.5,
        distanceY = body.position.y - this.options.height * 0.2,
        distance = Math.abs(distanceX) + Math.abs(distanceY);

      c.shadowColor = 'rgba(0,0,0,0.15)';
      c.shadowOffsetX = 0.05 * distanceX;
      c.shadowOffsetY = 0.05 * distanceY;
      c.shadowBlur = 1 + 12 * Math.min(1, distance / 1000);

      c.fill();

      // FIXME
      // c.shadowColor = null;
      // c.shadowOffsetX = null;
      // c.shadowOffsetY = null;
      // c.shadowBlur = null;
    }
  }


/**
   * Description
   * @private
   * @method bodies
   * @param {render} render
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private bodies(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context,
      engine = this.engine,
      options = this.options,
      showInternalEdges = options.showInternalEdges || !options.wireframes,
      body,
      part,
      i,
      k;

    for (i = 0; i < bodies.length; i++) {
      body = bodies[i];

      if (!body.visible)
        continue;

      // handle compound parts
      for (k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
        part = body.parts[k];

        if (!part.visible)
          continue;

        if (options.showSleeping && body.isSleeping) {
          c.globalAlpha = 0.5 * part.opacity;
        } else if (part.opacity !== 1) {
          c.globalAlpha = part.opacity;
        }

        if (part.texture && !options.wireframes) {
          // part sprite
          const texture = _getTexture(part.texture);

          c.translate(part.position.x, part.position.y);
          c.rotate(part.angle);

          c.drawImage(
            texture,
            texture.width * -part.xOffset * part.xScale,
            texture.height * -part.yOffset * part.yScale,
            texture.width * part.xScale,
            texture.height * part.yScale
          );

          // revert translation, hopefully faster than save / restore
          c.rotate(-part.angle);
          c.translate(-part.position.x, -part.position.y);
        } else {
          // part polygon
          if (part.circleRadius) {
            c.beginPath();
            c.arc(part.position.x, part.position.y, part.circleRadius, 0, 2 * Math.PI);
          } else {
            c.beginPath();
            c.moveTo(part.vertices[0].x, part.vertices[0].y);

            for (var j = 1; j < part.vertices.length; j++) {
              if (!part.vertices[j - 1].isInternal || showInternalEdges) {
                c.lineTo(part.vertices[j].x, part.vertices[j].y);
              } else {
                c.moveTo(part.vertices[j].x, part.vertices[j].y);
              }

              if (part.vertices[j].isInternal && !showInternalEdges) {
                c.moveTo(part.vertices[(j + 1) % part.vertices.length].x, part.vertices[(j + 1) % part.vertices.length].y);
              }
            }

            c.lineTo(part.vertices[0].x, part.vertices[0].y);
            c.closePath();
          }

          if (!options.wireframes) {
            c.fillStyle = part.fillStyle;

            if (part.lineWidth) {
              c.lineWidth = part.lineWidth;
              c.strokeStyle = part.strokeStyle;
              c.stroke();
            }

            c.fill();
          } else {
            c.lineWidth = 1;
            c.strokeStyle = '#bbb';
            c.stroke();
          }
        }

        c.globalAlpha = 1;
      }
    }
  }


/**
   * Optimised method for drawing body wireframes in one pass
   * @private
   * @method bodyWireframes
   * @param {render} this
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private bodyWireframes(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context,
      showInternalEdges = this.options.showInternalEdges,
      body,
      part,
      i,
      j,
      k;

    c.beginPath();

    // render all bodies
    for (i = 0; i < bodies.length; i++) {
      body = bodies[i];

      if (!body.visible)
        continue;

      // handle compound parts
      for (k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
        part = body.parts[k];

        c.moveTo(part.vertices[0].x, part.vertices[0].y);

        for (j = 1; j < part.vertices.length; j++) {
          if (!part.vertices[j - 1].isInternal || showInternalEdges) {
            c.lineTo(part.vertices[j].x, part.vertices[j].y);
          } else {
            c.moveTo(part.vertices[j].x, part.vertices[j].y);
          }

          if (part.vertices[j].isInternal && !showInternalEdges) {
            c.moveTo(part.vertices[(j + 1) % part.vertices.length].x, part.vertices[(j + 1) % part.vertices.length].y);
          }
        }

        c.lineTo(part.vertices[0].x, part.vertices[0].y);
      }
    }

    c.lineWidth = 1;
    c.strokeStyle = '#bbb';
    c.stroke();
  }


/**
   * Optimised method for drawing body convex hull wireframes in one pass
   * @private
   * @method bodyConvexHulls
   * @param {render} this
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private bodyConvexHulls(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context,
      body,
      part,
      i,
      j,
      k;

    c.beginPath();

    // render convex hulls
    for (i = 0; i < bodies.length; i++) {
      body = bodies[i];

      if (!body.visible || body.parts.length === 1)
        continue;

      c.moveTo(body.vertices[0].x, body.vertices[0].y);

      for (j = 1; j < body.vertices.length; j++) {
        c.lineTo(body.vertices[j].x, body.vertices[j].y);
      }

      c.lineTo(body.vertices[0].x, body.vertices[0].y);
    }

    c.lineWidth = 1;
    c.strokeStyle = 'rgba(255,255,255,0.2)';
    c.stroke();
  }


/**
   * Renders body vertex numbers.
   * @private
   * @method vertexNumbers
   * @param {render} render
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private vertexNumbers(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context,
      i,
      j,
      k;

    for (i = 0; i < bodies.length; i++) {
      var parts = bodies[i].parts;
      for (k = parts.length > 1 ? 1 : 0; k < parts.length; k++) {
        var part = parts[k];
        for (j = 0; j < part.vertices.length; j++) {
          c.fillStyle = 'rgba(255,255,255,0.2)';
          c.fillText(i + '_' + j, part.position.x + (part.vertices[j].x - part.position.x) * 0.8, part.position.y + (part.vertices[j].y - part.position.y) * 0.8);
        }
      }
    }
  }


/**
   * Renders mouse position.
   * @private
   * @method mousePosition
   * @param {render} render
   * @param {mouse} mouse
   * @param {RenderingContext} context
   */
private mousePosition(mouse: any, context: CanvasRenderingContext2D) {
    var c = context;
    c.fillStyle = 'rgba(255,255,255,0.8)';
    c.fillText(mouse.position.x + '  ' + mouse.position.y, mouse.position.x + 5, mouse.position.y - 5);
  }


/**
   * Draws body bounds
   * @private
   * @method bodyBounds
   * @param {render} render
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private bodyBounds(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context,
      engine = this.engine,
      options = this.options;

    c.beginPath();

    for (var i = 0; i < bodies.length; i++) {
      var body = bodies[i];

      if (body.visible) {
        var parts = bodies[i].parts;
        for (var j = parts.length > 1 ? 1 : 0; j < parts.length; j++) {
          var part = parts[j];
          c.rect(part.bounds.min.x, part.bounds.min.y, part.bounds.max.x - part.bounds.min.x, part.bounds.max.y - part.bounds.min.y);
        }
      }
    }

    if (options.wireframes) {
      c.strokeStyle = 'rgba(255,255,255,0.08)';
    } else {
      c.strokeStyle = 'rgba(0,0,0,0.1)';
    }

    c.lineWidth = 1;
    c.stroke();
  }


/**
   * Draws body angle indicators and axes
   * @private
   * @method bodyAxes
   * @param {render} render
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private bodyAxes(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context,
      engine = this.engine,
      options = this.options,
      part,
      i,
      j,
      k;

    c.beginPath();

    for (i = 0; i < bodies.length; i++) {
      var body = bodies[i],
        parts = body.parts;

      if (!body.visible)
        continue;

      if (options.showAxes) {
        // render all axes
        for (j = parts.length > 1 ? 1 : 0; j < parts.length; j++) {
          part = parts[j];
          for (k = 0; k < part.axes.length; k++) {
            var axis = part.axes[k];
            c.moveTo(part.position.x, part.position.y);
            c.lineTo(part.position.x + axis.x * 20, part.position.y + axis.y * 20);
          }
        }
      } else {
        for (j = parts.length > 1 ? 1 : 0; j < parts.length; j++) {
          part = parts[j];
          for (k = 0; k < part.axes.length; k++) {
            // render a single axis indicator
            c.moveTo(part.position.x, part.position.y);
            c.lineTo((part.vertices[0].x + part.vertices[part.vertices.length - 1].x) / 2,
              (part.vertices[0].y + part.vertices[part.vertices.length - 1].y) / 2);
          }
        }
      }
    }

    if (options.wireframes) {
      c.strokeStyle = 'indianred';
      c.lineWidth = 1;
    } else {
      c.strokeStyle = 'rgba(255, 255, 255, 0.4)';
      c.globalCompositeOperation = 'overlay';
      c.lineWidth = 2;
    }

    c.stroke();
    c.globalCompositeOperation = 'source-over';
  }


/**
   * Draws body positions
   * @private
   * @method bodyPositions
   * @param {render} render
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private bodyPositions(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context,
      engine = this.engine,
      options = this.options,
      body,
      part,
      i,
      k;

    c.beginPath();

    // render current positions
    for (i = 0; i < bodies.length; i++) {
      body = bodies[i];

      if (!body.visible)
        continue;

      // handle compound parts
      for (k = 0; k < body.parts.length; k++) {
        part = body.parts[k];
        c.arc(part.position.x, part.position.y, 3, 0, 2 * Math.PI, false);
        c.closePath();
      }
    }

    if (options.wireframes) {
      c.fillStyle = 'indianred';
    } else {
      c.fillStyle = 'rgba(0,0,0,0.5)';
    }
    c.fill();

    c.beginPath();

    // render previous positions
    for (i = 0; i < bodies.length; i++) {
      body = bodies[i];
      if (body.visible) {
        c.arc(body.positionPrev.x, body.positionPrev.y, 2, 0, 2 * Math.PI, false);
        c.closePath();
      }
    }

    c.fillStyle = 'rgba(255,165,0,0.8)';
    c.fill();
  }


/**
   * Draws body velocity
   * @private
   * @method bodyVelocity
   * @param {render} this
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private bodyVelocity(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context;

    c.beginPath();

    for (var i = 0; i < bodies.length; i++) {
      var body = bodies[i];

      if (!body.visible)
        continue;

      c.moveTo(body.position.x, body.position.y);
      c.lineTo(body.position.x + (body.position.x - body.positionPrev.x) * 2, body.position.y + (body.position.y - body.positionPrev.y) * 2);
    }

    c.lineWidth = 3;
    c.strokeStyle = 'cornflowerblue';
    c.stroke();
  }


/**
   * Draws body ids
   * @private
   * @method bodyIds
   * @param {render} this
   * @param {body[]} bodies
   * @param {RenderingContext} context
   */
private bodyIds(bodies: Body[], context: CanvasRenderingContext2D) {
    var c = context,
      i,
      j;

    for (i = 0; i < bodies.length; i++) {
      if (!bodies[i].visible)
        continue;

      var parts = bodies[i].parts;
      for (j = parts.length > 1 ? 1 : 0; j < parts.length; j++) {
        var part = parts[j];
        c.font = "12px Arial";
        c.fillStyle = 'rgba(255,255,255,0.5)';
        c.fillText(`${part.id}`, part.position.x + 10, part.position.y - 10);
      }
    }
  }


/**
   * Description
   * @private
   * @method collisions
   * @param {render} render
   * @param {pair[]} pairs
   * @param {RenderingContext} context
   */
private collisions(pairs: Pair[], context: CanvasRenderingContext2D) {
    var c = context,
      options = this.options,
      pair,
      collision,
      corrected,
      bodyA,
      bodyB,
      i,
      j;

    c.beginPath();

    // render collision positions
    for (i = 0; i < pairs.length; i++) {
      pair = pairs[i];

      if (!pair.isActive)
        continue;

      collision = pair.collision;
      for (j = 0; j < pair.activeContacts.length; j++) {
        var contact = pair.activeContacts[j],
          vertex = contact.vertex;
        c.rect(vertex.x - 1.5, vertex.y - 1.5, 3.5, 3.5);
      }
    }

    if (options.wireframes) {
      c.fillStyle = 'rgba(255,255,255,0.7)';
    } else {
      c.fillStyle = 'orange';
    }
    c.fill();

    c.beginPath();

    // render collision normals
    for (i = 0; i < pairs.length; i++) {
      pair = pairs[i];

      if (!pair.isActive)
        continue;

      collision = pair.collision!;

      if (pair.activeContacts.length > 0) {
        var normalPosX = pair.activeContacts[0].vertex.x,
          normalPosY = pair.activeContacts[0].vertex.y;

        if (pair.activeContacts.length === 2) {
          normalPosX = (pair.activeContacts[0].vertex.x + pair.activeContacts[1].vertex.x) / 2;
          normalPosY = (pair.activeContacts[0].vertex.y + pair.activeContacts[1].vertex.y) / 2;
        }

        if (collision.bodyB === collision.supports[0].body || collision.bodyA.isStatic === true) {
          c.moveTo(normalPosX - collision.normal.x * 8, normalPosY - collision.normal.y * 8);
        } else {
          c.moveTo(normalPosX + collision.normal.x * 8, normalPosY + collision.normal.y * 8);
        }

        c.lineTo(normalPosX, normalPosY);
      }
    }

    if (options.wireframes) {
      c.strokeStyle = 'rgba(255,165,0,0.7)';
    } else {
      c.strokeStyle = 'orange';
    }

    c.lineWidth = 1;
    c.stroke();
  }


/**
   * Description
   * @private
   * @method separations
   * @param {render} render
   * @param {pair[]} pairs
   * @param {RenderingContext} context
   */
private separations(pairs: Pair[], context: CanvasRenderingContext2D) {
    var c = context,
      options = this.options,
      pair,
      collision,
      corrected,
      bodyA,
      bodyB,
      i,
      j;

    c.beginPath();

    // render separations
    for (i = 0; i < pairs.length; i++) {
      pair = pairs[i];

      if (!pair.isActive)
        continue;

      collision = pair.collision!;
      bodyA = collision.bodyA;
      bodyB = collision.bodyB;

      var k = 1;

      if (!bodyB.isStatic && !bodyA.isStatic) k = 0.5;
      if (bodyB.isStatic) k = 0;

      c.moveTo(bodyB.position.x, bodyB.position.y);
      c.lineTo(bodyB.position.x - collision.penetration.x * k, bodyB.position.y - collision.penetration.y * k);

      k = 1;

      if (!bodyB.isStatic && !bodyA.isStatic) k = 0.5;
      if (bodyA.isStatic) k = 0;

      c.moveTo(bodyA.position.x, bodyA.position.y);
      c.lineTo(bodyA.position.x + collision.penetration.x * k, bodyA.position.y + collision.penetration.y * k);
    }

    if (options.wireframes) {
      c.strokeStyle = 'rgba(255,165,0,0.5)';
    } else {
      c.strokeStyle = 'orange';
    }
    c.stroke();
  }


/**
   * Description
   * @private
   * @method grid
   * @param {render} this
   * @param {grid} grid
   * @param {RenderingContext} context
   */
private grid(grid: Grid, context: CanvasRenderingContext2D) {
    var c = context,
      options = this.options;

    if (options.wireframes) {
      c.strokeStyle = 'rgba(255,180,0,0.1)';
    } else {
      c.strokeStyle = 'rgba(255,180,0,0.5)';
    }

    c.beginPath();

    var bucketKeys = Common.keys(grid.buckets);

    for (var i = 0; i < bucketKeys.length; i++) {
      var bucketId = bucketKeys[i];

      if (grid.buckets[bucketId].length < 2)
        continue;

      var region = bucketId.split(/C|R/);
      c.rect(0.5 + parseInt(region[1], 10) * grid.bucketWidth,
        0.5 + parseInt(region[2], 10) * grid.bucketHeight,
        grid.bucketWidth,
        grid.bucketHeight);
    }

    c.lineWidth = 1;
    c.stroke();
  }


/**
   * Description
   * @private
   * @method inspector
   * @param {inspector} inspector
   * @param {RenderingContext} context
   */
private inspector(inspector: any, context: CanvasRenderingContext2D) {
    var engine = inspector.engine,
      selected = inspector.selected,
      render = inspector.render,
      options = render.options,
      bounds;

    if (options.hasBounds) {
      var boundsWidth = render.bounds.max.x - render.bounds.min.x,
        boundsHeight = render.bounds.max.y - render.bounds.min.y,
        boundsScaleX = boundsWidth / render.options.width,
        boundsScaleY = boundsHeight / render.options.height;

      context.scale(1 / boundsScaleX, 1 / boundsScaleY);
      context.translate(-render.bounds.min.x, -render.bounds.min.y);
    }

    for (var i = 0; i < selected.length; i++) {
      var item = selected[i].data;

      context.translate(0.5, 0.5);
      context.lineWidth = 1;
      context.strokeStyle = 'rgba(255,165,0,0.9)';
      context.setLineDash([1, 2]);

      switch (item.type) {

        case 'body':

          // render body selections
          bounds = item.bounds;
          context.beginPath();
          context.rect(Math.floor(bounds.min.x - 3), Math.floor(bounds.min.y - 3),
            Math.floor(bounds.max.x - bounds.min.x + 6), Math.floor(bounds.max.y - bounds.min.y + 6));
          context.closePath();
          context.stroke();

          break;

        case 'constraint':

          // render constraint selections
          var point = item.pointA;
          if (item.bodyA)
            point = item.pointB;
          context.beginPath();
          context.arc(point.x, point.y, 10, 0, 2 * Math.PI);
          context.closePath();
          context.stroke();

          break;

      }

      context.setLineDash([]);
      context.translate(-0.5, -0.5);
    }

    // render selection region
    if (inspector.selectStart !== null) {
      context.translate(0.5, 0.5);
      context.lineWidth = 1;
      context.strokeStyle = 'rgba(255,165,0,0.6)';
      context.fillStyle = 'rgba(255,165,0,0.1)';
      bounds = inspector.selectBounds;
      context.beginPath();
      context.rect(Math.floor(bounds.min.x), Math.floor(bounds.min.y),
        Math.floor(bounds.max.x - bounds.min.x), Math.floor(bounds.max.y - bounds.min.y));
      context.closePath();
      context.stroke();
      context.fill();
      context.translate(-0.5, -0.5);
    }

    if (options.hasBounds)
      context.setTransform(1, 0, 0, 1, 0, 0);
  }


() => (
  <div>
    <Label>I


export function getFamily(runtime: string | undefined): SamLambdaRuntimeFamily {
    switch (runtime) {
        case 'python3.7':
        case 'python3.6':
        case 'python2.7':
        case 'python':
            return SamLambdaRuntimeFamily.Python
        case 'nodejs8.10':
        case 'nodejs10.x':
        case 'nodejs':
            return SamLambdaRuntimeFamily.NodeJS
        case 'dotnetcore2.1':
        case 'dotnetcore':
        case 'dotnet':
            return SamLambdaRuntimeFamily.DotNetCore
        default:
            throw new Error(`Unrecognized runtime: '${runtime}'`)
    }
}


function getSortableCompareText(runtime: SamLambdaRuntime): string {
    return runtimeCompareText.get(runtime) || runtime.toString()
}


export function compareSamLambdaRuntime(a: SamLambdaRuntime, b: SamLambdaRuntime): number {
    return getSortableCompareText(a).localeCompare(getSortableCompareText(b))
}


export enum SamLambdaRuntimeFamily {
    Python,
    NodeJS,
    DotNetCore
}


// TODO: Can we dynamically determine the available runtimes? We could theoretically parse the
// output of `sam init --help`, but that's a hack.
export type SamLambdaRuntime = 'python3.7' | 'python3.6' | 'python2.7' | 'nodejs8.10' | 'nodejs10.x' | 'dotnetcore2.1'


() => {
      this._elementRef.nativeElement.appendChild(this._img);
			this._loaded(true);
	  }


@Component({
  selector: 'preload-image',
  templateUrl: 'preload-image.html'
})
export class PreloadImage implements OnChanges {
	_src: string = '';
	_ratio: { w: number, h: number };
  _img: any;

	constructor(public _elementRef: ElementRef, public _renderer: Renderer) {
    this._img = new Image();
  }

	@Input() alt: string;

  @Input() title: string;

	@Input() set src(val: string) {
    this._src = isPresent(val) ? val : '';
  }

	@Input() set ratio(ratio: { w: number, h: number }){
		this._ratio = ratio || null;
  }

  ngOnChanges(changes: { [propName: string]: SimpleChange }) {
    let ratio_height = (this._ratio.h / this._ratio.w * 100)+"%";
		// Conserve aspect ratio (see: http://stackoverflow.com/a/10441480/1116959)
		this._renderer.setElementStyle(this._elementRef.nativeElement, 'padding-bottom', ratio_height);

    this._update();
    // console.log("CHANGES preload-image", this._src);
    // console.log(changes['src'].isFirstChange());
  }

	_update() {
	  if (isPresent(this.alt)) {
	    this._img.alt = this.alt;
	  }
	  if (isPresent(this.title)) {
	    this._img.title = this.title;
	  }

	  this._img.addEventListener('load', () => {
      this._elementRef.nativeElement.appendChild(this._img);
			this._loaded(true);
	  });

	  this._img.src = this._src;

	  this._loaded(false);
	}

	_loaded(isLoaded: boolean) {
    this._elementRef.nativeElement.classList[isLoaded ? 'add' : 'remove']('img-loaded');
  }
}


ngOnChanges(changes: { [propName: string]: SimpleChange }) {
    let ratio_height = (this._ratio.h / this._ratio.w * 100)+"%";
		// Conserve aspect ratio (see: http://stackoverflow.com/a/10441480/1116959)
		this._renderer.setElementStyle(this._elementRef.nativeElement, 'padding-bottom', ratio_height);

    this._update();
    // console.log("CHANGES preload-image", this._src);
    // console.log(changes['src'].isFirstChange());
  }


_update() {
	  if (isPresent(this.alt)) {
	    this._img.alt = this.alt;
	  }
	  if (isPresent(this.title)) {
	    this._img.title = this.title;
	  }

	  this._img.addEventListener('load', () => {
      this._elementRef.nativeElement.appendChild(this._img);
			this._loaded(true);
	  });

	  this._img.src = this._src;

	  this._loaded(false);
	}


_loaded(isLoaded: boolean) {
    this._elementRef.nativeElement.classList[isLoaded ? 'add' : 'remove']('img-loaded');
  }


(): JSX.Element => (
	<DemoContainer title="String Item">
		<PropertyItemString onChange


() => {
  const [orphanages, setOrphanages] = useState<Orphanage[]>([]);
  useEffect(() => {
    (async () => {
      const { data } = await api.get<Orphanage[]>('/orphanages');
      setOrphanages(data);
    })();
  }, []);

  return (
    <Container>

Escolha um orfanato no mapa

Muitas crianças estão esperando a sua visita :)

          Mogi Mirim
          São Paulo

      <MapContainer>
        <Map center={[-22.4432173, -46.8148575]} zoom={15}>
          <TileLayer url="https://a.tile.openstreetmap.org/{z}/{x}/{y}.png" />
          {orphanages.map(orphanage => (
            <Marker
              key={orphanage.id}
              position={[orphanage.latitude, orphanage.longitude]}
              icon={mapIcon}
            >
              <Popup closeButton={false} maxWidth={240} minWidth={240}>
                {orphanage.name}
                <Link to={`/orphanages/${orphanage.id}`}>
                  <FiArrowRight size={20} color="#FFF" />
                </Link>
              </Popup>
            </Marker>


() => {
    (async () => {
      const { data } = await api.get<Orphanage[]>('/orphanages');
      setOrphanages(data);
    })();
  }


async () => {
      const { data } = await api.get<Orphanage[]>('/orphanages');
      setOrphanages(data);
    }


orphanage => (
            <Marker
              key={orphanage.id}


interface Orphanage {
  id: number;
  name: string;
  latitude: number;
  longitude: number;
}


async () => {
    try {
        const PORT = process.env.SERVER_PORT || 5000;
        const app = await NestFactory.create(AppModule);
        app.enableCors();
        await app.listen(PORT, () => {
            console.log(`server started on PORT: ${PORT}`);
        })
    } catch (error) {
        console.log(error);

    }
}


() => {
            console.log(`server started on PORT: ${PORT}`);
        }


export class OpenZeppelinExtensionAdapter implements IExtensionAdapter {
  private extensionInfo: any;
  private extension: Extension<any> | undefined;

  constructor() {
    this.extensionInfo = Constants.externalExtensions[Constants.coreSdk.openZeppelin];
    this.extension = extensions.getExtension(this.extensionInfo.name);
  }

  public async validateExtension(): Promise<void> {
    if (!this.extension) {
      throw new Error(Constants.errorMessageStrings.ExtensionNotInstalled(this.extensionInfo.name));
    }

    const version = this.extension.packageJSON.version;
    if (!semver.eq(version, this.extensionInfo.supportedVersion)) {
      throw new Error(Constants.informationMessage.unsupportedVersionOfExternalExtension(
        this.extensionInfo.name, version, this.extensionInfo.supportedVersion));
    }

    if (!this.extension.isActive) {
      await this.extension.activate();
    }
  }

  public async build(): Promise<void> {
    return commands.executeCommand(this.extensionInfo.commands.buildContracts);
  }

  public async deploy(): Promise<void> {
    return commands.executeCommand(this.extensionInfo.commands.deployContracts);
  }
}


public async validateExtension(): Promise<void> {
    if (!this.extension) {
      throw new Error(Constants.errorMessageStrings.ExtensionNotInstalled(this.extensionInfo.name));
    }

    const version = this.extension.packageJSON.version;
    if (!semver.eq(version, this.extensionInfo.supportedVersion)) {
      throw new Error(Constants.informationMessage.unsupportedVersionOfExternalExtension(
        this.extensionInfo.name, version, this.extensionInfo.supportedVersion));
    }

    if (!this.extension.isActive) {
      await this.extension.activate();
    }
  }


public async build(): Promise<void> {
    return commands.executeCommand(this.extensionInfo.commands.buildContracts);
  }


public async deploy(): Promise<void> {
    return commands.executeCommand(this.extensionInfo.commands.deployContracts);
  }


export declare function maskSchema(schema: string): string;


export declare function mapScalarValues(obj: any, mapper: (value: any) => any): {};


/**
     * Cloud Logging API
     *
     * Writes log entries and manages your Cloud Logging configuration. The table entries below are presented in alphabetical order, not in order of common use. For explanations of the concepts found in the table entries, read the documentation at https://cloud.google.com/logging/docs.
     *
     * @example
     * const {google} = require('googleapis');
     * const logging = google.logging('v2');
     *
     * @namespace logging
     * @type {Function}
     * @version v2
     * @variation v2
     * @param {object=} options Options for Logging
     */
export class Logging {
        context: APIRequestContext;
        billingAccounts: Resource$Billingaccounts;
        entries: Resource$Entries;
        exclusions: Resource$Exclusions;
        folders: Resource$Folders;
        locations: Resource$Locations;
        logs: Resource$Logs;
        monitoredResourceDescriptors: Resource$Monitoredresourcedescriptors;
        organizations: Resource$Organizations;
        projects: Resource$Projects;
        sinks: Resource$Sinks;
        v2: Resource$V2;
        constructor(options: GlobalOptions, google?: GoogleConfigurable);
    }


export class Resource$Billingaccounts {
        context: APIRequestContext;
        buckets: Resource$Billingaccounts$Buckets;
        exclusions: Resource$Billingaccounts$Exclusions;
        locations: Resource$Billingaccounts$Locations;
        logs: Resource$Billingaccounts$Logs;
        sinks: Resource$Billingaccounts$Sinks;
        constructor(context: APIRequestContext);
    }


export class Resource$Billingaccounts$Buckets {
        context: APIRequestContext;
        constructor(context: APIRequestContext);
        /**
         * logging.billingAccounts.buckets.get
         * @desc Gets a bucket (Beta).
         * @example
         * // Before running the sample:
         * // - Enable the API at:
         * //   https://console.developers.google.com/apis/api/logging.googleapis.com
         * // - Login into gcloud by running:
         * //   `$ gcloud auth application-default login`
         * // - Install the npm module by running:
         * //   `$ npm install googleapis`
         *
         * const {google} = require('googleapis');
         * const logging = google.logging('v2');
         *
         * async function main() {
         *   const auth = new google.auth.GoogleAuth({
         *     // Scopes can be specified either as an array or as a single, space-delimited string.
         *     scopes: [
         *       'https://www.googleapis.com/auth/cloud-platform',
         *       'https://www.googleapis.com/auth/cloud-platform.read-only',
         *       'https://www.googleapis.com/auth/logging.admin',
         *       'https://www.googleapis.com/auth/logging.read',
         *     ],
         *   });
         *
         *   // Acquire an auth client, and bind it to all future calls
         *   const authClient = await auth.getClient();
         *   google.options('auth', authClient);
         *
         *   // Do the magic
         *   const res = await logging.billingAccounts.buckets.get({
         *     // Required. The resource name of the bucket:
         *     // "projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]"
         *     // "organizations/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]"
         *     // "billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]"
         *     // "folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]"
         *     // Example: "projects/my-project-id/locations/my-location/buckets/my-bucket-id".
         *     name: 'billingAccounts/my-billingAccount/buckets/my-bucket',
         *   });
         *   console.log(res.data);
         *
         *   // Example response
         *   // {
         *   //   "createTime": "my_createTime",
         *   //   "description": "my_description",
         *   //   "lifecycleState": "my_lifecycleState",
         *   //   "name": "my_name",
         *   //   "retentionDays": 0,
         *   //   "updateTime": "my_updateTime"
         *   // }
         * }
         *
         * main().catch(e => {
         *   console.error(e);
         *   throw e;
         * });
         *
         * @alias logging.billingAccounts.buckets.get
         * @memberOf! ()
         *
         * @param {object} params Parameters for request
         * @param {string} params.name Required. The resource name of the bucket: "projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]" "organizations/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]" "billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]" "folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]" Example: "projects/my-project-id/locations/my-location/buckets/my-bucket-id".
         * @param {object} [options] Optionally override request options, such as `url`, `method`, and `encoding`.
         * @param {callback} callback The callback that handles the response.
         * @return {object} Request object
         */
        get(params: Params$Resource$Billingaccounts$Buckets$Get, options: StreamMethodOptions): GaxiosPromise<Readable>;
        get(params?: Params$Resource$Billingaccounts$Buckets$Get, options?: MethodOptions): GaxiosPromise<Schema$LogBucket>;
        get(params: Params$Resource$Billingaccounts$Buckets$Get, options: StreamMethodOptions | BodyResponseCallback<Readable>, callback: BodyResponseCallback<Readable>): void;
        get(params: Params$Resource$Billingaccounts$Buckets$Get, options: MethodOptions | BodyResponseCallback<Schema$LogBucket>, callback: BodyResponseCallback<Schema$LogBucket>): void;
        get(params: Params$Resource$Billingaccounts$Buckets$Get, callback: BodyResponseCallback<Schema$LogBucket>): void;
        get(callback: BodyResponseCallback<Schema$LogBucket>): void;
    }


export class Resource$Billingaccounts$Locations {
        context: APIRequestContext;
        buckets: Resource$Billingaccounts$Locations$Buckets;
        constructor(context: APIRequestContext);
    }