<commit_before>/**
 * Copyright 2018 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {ActivityPort} from '../components/activities';
import {
  AlreadySubscribedResponse,
  EntitlementsResponse,
  SkuSelectedResponse,
} from '../proto/api_messages';
import {ClientConfig} from '../model/client-config';
import {ConfiguredRuntime} from './runtime';
import {ContributionsFlow} from './contributions-flow';
import {PageConfig} from '../model/page-config';
import {PayStartFlow} from './pay-flow';
import {ProductType} from '../api/subscriptions';

describes.realWin('ContributionsFlow', {}, (env) => {
  let win;
  let contributionsFlow;
  let runtime;
  let activitiesMock;
  let callbacksMock;
  let pageConfig;
  let port;
  let messageMap;

  beforeEach(() => {
    win = env.win;
    messageMap = {};
    pageConfig = new PageConfig('pub1:label1');
    runtime = new ConfiguredRuntime(win, pageConfig);
    activitiesMock = sandbox.mock(runtime.activities());
    callbacksMock = sandbox.mock(runtime.callbacks());
    contributionsFlow = new ContributionsFlow(runtime, {'isClosable': true});
    port = new ActivityPort();
    port.onResizeRequest = () => {};
    port.whenReady = () => Promise.resolve();
    port.acceptResult = () => Promise.resolve();
    sandbox.stub(port, 'on').callsFake(function (ctor, cb) {
      const messageType = new ctor();
      const messageLabel = messageType.label();
      messageMap[messageLabel] = cb;
    });
  });

  afterEach(() => {
    activitiesMock.verify();
    callbacksMock.verify();
  });

  it('has valid ContributionsFlow constructed with a list', async () => {
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionsiframe?_=_',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'other',
          skus: null,
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('allows non-closable dialogs', async () => {
    const isClosable = false;
    contributionsFlow = new ContributionsFlow(runtime, {
      isClosable,
      list: 'other',
    });
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionsiframe?_=_',
        {
          isClosable,
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'other',
          skus: null,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('has valid ContributionsFlow constructed with skus', async () => {
    contributionsFlow = new ContributionsFlow(runtime, {
      skus: ['sku1', 'sku2'],
    });
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionsiframe?_=_',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'default',
          skus: ['sku1', 'sku2'],
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('sends an empty EntitlementsResponse to show "no contribution found" toast on Activity iFrame view', async () => {
    contributionsFlow = new ContributionsFlow(runtime, {
      skus: ['sku1', 'sku2'],
    });

    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionsiframe?_=_',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'default',
          skus: ['sku1', 'sku2'],
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    // ContributionsFlow needs to start first in order to have a valid ActivityIframeView
    await contributionsFlow.start();

    const activityIframeView =
      await contributionsFlow.activityIframeViewPromise_;
    const activityIframeViewMock = sandbox.mock(activityIframeView);
    activityIframeViewMock
      .expects('execute')
      .withExactArgs(new EntitlementsResponse())
      .once();

    await contributionsFlow.showNoEntitlementFoundToast();

    activityIframeViewMock.verify();
  });

  it('has valid ContributionsFlow constructed, routed to the new contributions iframe', async () => {
    sandbox
      .stub(runtime.clientConfigManager(), 'getClientConfig')
      .resolves(new ClientConfig({useUpdatedOfferFlows: true}));
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionoffersiframe?_=_',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'other',
          skus: null,
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('constructs valid ContributionsFlow with forced language', async () => {
    sandbox
      .stub(runtime.clientConfigManager(), 'getClientConfig')
      .resolves(
        new ClientConfig(
          /* autoPromptConfig */ undefined,
          /* paySwgVersion */ undefined,
          /* useUpdatedOfferFlows */ true
        )
      );
    sandbox
      .stub(runtime.clientConfigManager(), 'shouldForceLangInIframes')
      .returns(true);
    sandbox.stub(runtime.clientConfigManager(), 'getLanguage').returns('fr-CA');
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionoffersiframe?_=_&hl=fr-CA',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          productType: ProductType.UI_CONTRIBUTION,
          list: 'other',
          skus: null,
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('start should not show contributions if predicates disable', async () => {
    sandbox.stub(runtime.clientConfigManager(), 'getClientConfig').resolves(
      new ClientConfig({
        useUpdatedOfferFlows: true,
        uiPredicates: {canDisplayAutoPrompt: false},
      })
    );
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    callbacksMock.expects('triggerFlowStarted').never();

    await contributionsFlow.start();
  });

  it('start should show contributions if predicates enable', async () => {
    sandbox.stub(runtime.clientConfigManager(), 'getClientConfig').resolves(
      new ClientConfig({
        useUpdatedOfferFlows: true,
        uiPredicates: {canDisplayAutoPrompt: true},
      })
    );
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    callbacksMock.expects('triggerFlowStarted').once();

    activitiesMock.expects('openIframe').resolves(port);

    await contributionsFlow.start();
  });

  it('activates pay, login', async () => {
    const payStub = sandbox.stub(PayStartFlow.prototype, 'start');
    const loginStub = sandbox.stub(runtime.callbacks(), 'triggerLoginRequest');
    const nativeStub = sandbox.stub(
      runtime.callbacks(),
      'triggerSubscribeRequest'
    );
    activitiesMock.expects('openIframe').resolves(port);

    await contributionsFlow.start();
    let callback = messageMap['SkuSelectedResponse'];
    expect(callback).to.not.be.null;
    // Unrelated message.
    expect(payStub).to.not.be.called;
    expect(loginStub).to.not.be.called;
    expect(nativeStub).to.not.be.called;
    const skuSelected = new SkuSelectedResponse();
    skuSelected.setSku('sku1');
    // Pay message.
    callback(skuSelected);
    expect(payStub).to.be.calledOnce;
    expect(loginStub).to.not.be.called;
    expect(nativeStub).to.not.be.called;
    // Login message.
    callback = messageMap['AlreadySubscribedResponse'];
    const response = new AlreadySubscribedResponse();
    response.setSubscriberOrMember(true);
    response.setLinkRequested(false);
    callback(response);
    expect(loginStub).to.be.calledOnce.calledWithExactly({
      linkRequested: false,
    });
    expect(payStub).to.be.calledOnce; // Didn't change.
    expect(nativeStub).to.not.be.called;
  });

  it('activates login with linking', async () => {
    const loginStub = sandbox.stub(runtime.callbacks(), 'triggerLoginRequest');
    activitiesMock.expects('openIframe').resolves(port);

    await contributionsFlow.start();
    const response = new AlreadySubscribedResponse();
    response.setSubscriberOrMember(true);
    response.setLinkRequested(true);
    const callback = messageMap[response.label()];
    callback(response);
    expect(loginStub).to.be.calledOnce.calledWithExactly({
      linkRequested: true,
    });
  });
});
<commit_msg>Fix test after merge.<commit_after>/**
 * Copyright 2018 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {ActivityPort} from '../components/activities';
import {
  AlreadySubscribedResponse,
  EntitlementsResponse,
  SkuSelectedResponse,
} from '../proto/api_messages';
import {ClientConfig} from '../model/client-config';
import {ConfiguredRuntime} from './runtime';
import {ContributionsFlow} from './contributions-flow';
import {PageConfig} from '../model/page-config';
import {PayStartFlow} from './pay-flow';
import {ProductType} from '../api/subscriptions';

describes.realWin('ContributionsFlow', {}, (env) => {
  let win;
  let contributionsFlow;
  let runtime;
  let activitiesMock;
  let callbacksMock;
  let pageConfig;
  let port;
  let messageMap;

  beforeEach(() => {
    win = env.win;
    messageMap = {};
    pageConfig = new PageConfig('pub1:label1');
    runtime = new ConfiguredRuntime(win, pageConfig);
    activitiesMock = sandbox.mock(runtime.activities());
    callbacksMock = sandbox.mock(runtime.callbacks());
    contributionsFlow = new ContributionsFlow(runtime, {'isClosable': true});
    port = new ActivityPort();
    port.onResizeRequest = () => {};
    port.whenReady = () => Promise.resolve();
    port.acceptResult = () => Promise.resolve();
    sandbox.stub(port, 'on').callsFake(function (ctor, cb) {
      const messageType = new ctor();
      const messageLabel = messageType.label();
      messageMap[messageLabel] = cb;
    });
  });

  afterEach(() => {
    activitiesMock.verify();
    callbacksMock.verify();
  });

  it('has valid ContributionsFlow constructed with a list', async () => {
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionsiframe?_=_',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'other',
          skus: null,
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('allows non-closable dialogs', async () => {
    const isClosable = false;
    contributionsFlow = new ContributionsFlow(runtime, {
      isClosable,
      list: 'other',
    });
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionsiframe?_=_',
        {
          isClosable,
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'other',
          skus: null,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('has valid ContributionsFlow constructed with skus', async () => {
    contributionsFlow = new ContributionsFlow(runtime, {
      skus: ['sku1', 'sku2'],
    });
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionsiframe?_=_',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'default',
          skus: ['sku1', 'sku2'],
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('sends an empty EntitlementsResponse to show "no contribution found" toast on Activity iFrame view', async () => {
    contributionsFlow = new ContributionsFlow(runtime, {
      skus: ['sku1', 'sku2'],
    });

    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionsiframe?_=_',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'default',
          skus: ['sku1', 'sku2'],
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    // ContributionsFlow needs to start first in order to have a valid ActivityIframeView
    await contributionsFlow.start();

    const activityIframeView =
      await contributionsFlow.activityIframeViewPromise_;
    const activityIframeViewMock = sandbox.mock(activityIframeView);
    activityIframeViewMock
      .expects('execute')
      .withExactArgs(new EntitlementsResponse())
      .once();

    await contributionsFlow.showNoEntitlementFoundToast();

    activityIframeViewMock.verify();
  });

  it('has valid ContributionsFlow constructed, routed to the new contributions iframe', async () => {
    sandbox
      .stub(runtime.clientConfigManager(), 'getClientConfig')
      .resolves(new ClientConfig({useUpdatedOfferFlows: true}));
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionoffersiframe?_=_',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          'productType': ProductType.UI_CONTRIBUTION,
          list: 'other',
          skus: null,
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('constructs valid ContributionsFlow with forced language', async () => {
    const clientConfigManager = runtime.clientConfigManager();
    sandbox
      .stub(clientConfigManager, 'getClientConfig')
      .resolves(new ClientConfig({useUpdatedOfferFlows: true}));
    sandbox.stub(clientConfigManager, 'shouldForceLangInIframes').returns(true);
    sandbox.stub(clientConfigManager, 'getLanguage').returns('fr-CA');
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    activitiesMock
      .expects('openIframe')
      .withExactArgs(
        sandbox.match((arg) => arg.tagName == 'IFRAME'),
        '$frontend$/swg/_/ui/v1/contributionoffersiframe?_=_&hl=fr-CA',
        {
          _client: 'SwG $internalRuntimeVersion$',
          publicationId: 'pub1',
          productId: 'pub1:label1',
          productType: ProductType.UI_CONTRIBUTION,
          list: 'other',
          skus: null,
          isClosable: true,
          supportsEventManager: true,
        }
      )
      .resolves(port);
    await contributionsFlow.start();
  });

  it('start should not show contributions if predicates disable', async () => {
    sandbox.stub(runtime.clientConfigManager(), 'getClientConfig').resolves(
      new ClientConfig({
        useUpdatedOfferFlows: true,
        uiPredicates: {canDisplayAutoPrompt: false},
      })
    );
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    callbacksMock.expects('triggerFlowStarted').never();

    await contributionsFlow.start();
  });

  it('start should show contributions if predicates enable', async () => {
    sandbox.stub(runtime.clientConfigManager(), 'getClientConfig').resolves(
      new ClientConfig({
        useUpdatedOfferFlows: true,
        uiPredicates: {canDisplayAutoPrompt: true},
      })
    );
    contributionsFlow = new ContributionsFlow(runtime, {list: 'other'});
    callbacksMock.expects('triggerFlowStarted').once();

    activitiesMock.expects('openIframe').resolves(port);

    await contributionsFlow.start();
  });

  it('activates pay, login', async () => {
    const payStub = sandbox.stub(PayStartFlow.prototype, 'start');
    const loginStub = sandbox.stub(runtime.callbacks(), 'triggerLoginRequest');
    const nativeStub = sandbox.stub(
      runtime.callbacks(),
      'triggerSubscribeRequest'
    );
    activitiesMock.expects('openIframe').resolves(port);

    await contributionsFlow.start();
    let callback = messageMap['SkuSelectedResponse'];
    expect(callback).to.not.be.null;
    // Unrelated message.
    expect(payStub).to.not.be.called;
    expect(loginStub).to.not.be.called;
    expect(nativeStub).to.not.be.called;
    const skuSelected = new SkuSelectedResponse();
    skuSelected.setSku('sku1');
    // Pay message.
    callback(skuSelected);
    expect(payStub).to.be.calledOnce;
    expect(loginStub).to.not.be.called;
    expect(nativeStub).to.not.be.called;
    // Login message.
    callback = messageMap['AlreadySubscribedResponse'];
    const response = new AlreadySubscribedResponse();
    response.setSubscriberOrMember(true);
    response.setLinkRequested(false);
    callback(response);
    expect(loginStub).to.be.calledOnce.calledWithExactly({
      linkRequested: false,
    });
    expect(payStub).to.be.calledOnce; // Didn't change.
    expect(nativeStub).to.not.be.called;
  });

  it('activates login with linking', async () => {
    const loginStub = sandbox.stub(runtime.callbacks(), 'triggerLoginRequest');
    activitiesMock.expects('openIframe').resolves(port);

    await contributionsFlow.start();
    const response = new AlreadySubscribedResponse();
    response.setSubscriberOrMember(true);
    response.setLinkRequested(true);
    const callback = messageMap[response.label()];
    callback(response);
    expect(loginStub).to.be.calledOnce.calledWithExactly({
      linkRequested: true,
    });
  });
});
<|endoftext|>


<commit_before>'use strict'

const crypto = require('crypto');
const resolve = require('path').resolve;
const fs = require('fs');
const suffixTest = /\.json$/;
const root = resolve(process.argv[2] || '.') + '/';
const env_config = require(root + 'flow.json');
const path = root + 'composition/';
const states = {};
const files = fs.readdirSync(path);
const rdf_syntax = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
const hashids = {};
const envs = {};
const temp_index = {};

function write (subject, predicate, object) {
    process.stdout.write(subject + ' <' + predicate + '> ' + object + ' .\n');
}

function getMethodIri (module, method) {

    // git+https://github.com/ + module + .git
    let owner = 'jillix';
    switch (module) {
        case 'flow-view':
        case 'flow-auth':
        case 'flow-streams':
        case 'flow-visualizer':
        case 'flow-compress':
            owner = 'adioo';
            break;
        case 'flow-keypress':
        case 'flow-ace':
            owner = 'danandrei';
            break;
    }

    // TODO method descriptor
    return '<' + owner + '/' + module + '?' + method + '>';
}

function parseHandler (state, handler) {
    handler = handler.split('/');
    let parsed = {
        type: handler[0][0]
    };
    handler[0] = handler[0].substr(1);

    if (handler.length > 1) {
        parsed.state = handler[0];
        parsed.path = handler[1];
    } else {
        parsed.state = state;
        parsed.path = handler[0]
    }

    if (parsed.type !== '>') {
        parsed.path = getMethodIri(states[parsed.state].module, parsed.path);
    } else {
        parsed.path = '_:' + crypto.createHash('md5').update(parsed.state + parsed.path).digest('hex');
    }

    return parsed;
}

function UID (len) {
    len = len || 23;
    for (var i = 0, random = ''; i < len; ++i) {
        random += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[0 | Math.random() * 62];
    }
    return random;
}

function getHash (string) {
    let hash = '_:' + crypto.createHash('md5').update(string).digest('hex');
    if (!hashids[hash]) {
        hashids[hash] = 1;
        write(hash, rdf_syntax + 'string', '"' + string.replace(/"/g, '\\"') + '"');
    }

    return hash;
}

// create network triple
const network_id = '_:' + crypto.createHash('md5').update(env_config.network).digest('hex');

// network name (Service)
write(
    network_id,
    'http://schema.org/name',
    getHash(env_config.network)
);

// network type
write(
    network_id,
    rdf_syntax + 'type',
    '<http://schema.jillix.net/vocab/Network>'
);

// Convert composition files to triples
files.forEach(file => {

    if (!suffixTest.test(file)) {
        return;
    }

    try {
        let state = JSON.parse(fs.readFileSync(path + file));
        states[state.name] = state;
    } catch (error) {
        throw new Error(path + file + '\n' + error);
    }
});

// create env objects
if (env_config) {

    if (env_config.environments) {
        env_config.environments.forEach(env => {
            envs[env.name] = getHash(env.name);

            // environment name
            write(
                envs[env.name],
                'http://schema.org/name',
                envs[env.name]
            );

            // environment vars
            write(
                envs[env.name],
                'http://schema.jillix.net/vocab/vars',
                getHash(JSON.stringify(env.vars))
            );

            // environment type
            write(
                envs[env.name],
                rdf_syntax + 'type',
                '<http://schema.jillix.net/vocab/Environment>'
            );
        });
    }

    if (env_config.entrypoints) {
        env_config.entrypoints.forEach(ep => {

            const entrypoint_id = '_:' + UID(8);

            // network entrypoint
            write(
                network_id,
                'http://schema.jillix.net/vocab/entrypoint',
                entrypoint_id
            );

            // entrypoint name
            write(
                entrypoint_id,
                'http://schema.org/name',
                getHash(ep.name)
            );

            // entrypoint type
            write(
                entrypoint_id,
                rdf_syntax + 'type',
                '<http://schema.jillix.net/vocab/Entrypoint>'
            );

            // entrypoint environment
            if (ep.env) {
                ep.env.forEach(env => {
                    if (envs[env]) {
                        write(
                            entrypoint_id,
                            'http://schema.jillix.net/vocab/environment',
                            envs[env]
                        );
                    }
                });
            }

            // entrypoint sequence
            write(
                entrypoint_id,
                'http://schema.jillix.net/vocab/sequence',
                '_:' + crypto.createHash('md5').update(ep.emit.replace('/', '')).digest('hex')
            );
        });
    }
}

for (let name in states) {
    let state = states[name];

    if (!state.flow) {
        continue;
    }

    for (let sequence in state.flow) {
        let sequence_id = '_:' + crypto.createHash('md5').update(state.name + sequence).digest('hex');

        // name
        write(
            sequence_id,
            'http://schema.org/name',
            getHash(state.name + '-' + sequence)
        );

        // type
        write(
            sequence_id,
            rdf_syntax + 'type',
            '<http://schema.jillix.net/vocab/Sequence>'
        );

        // roles
        if (state.roles) {
            for (let role in state.roles) {
                write(
                    sequence_id,
                    'http://schema.jillix.net/vocab/role',
                    getHash(role)
                );
            }
        }

        // end event
        if (state.flow[sequence].e) {
            write(
                sequence_id,
                'http://schema.jillix.net/vocab/onEnd',
                '_:' + crypto.createHash('md5').update(state.flow[sequence].e).digest('hex')
            );
        }

        // error event
        if (state.flow[sequence].r) {
            write(
                sequence_id,
                'http://schema.jillix.net/vocab/onError',
                '_:' + crypto.createHash('md5').update(state.flow[sequence].r).digest('hex')
            );
        }

        // sequence
        if (state.flow[sequence].d) {
            let previous;
            state.flow[sequence].d.forEach((handler, index) => {

                let handler_id = '_:' + crypto.createHash('md5').update(UID(8)).digest('hex');
                let args;

                if (typeof handler === 'string') {
                    handler = handler;
                    args = null;
                } else {
                    args = handler[1];
                    handler = handler[0];
                }

                handler = parseHandler(state.name, handler);

                // type
                write(
                    handler_id,
                    rdf_syntax + 'type',
                    '<http://schema.jillix.net/vocab/Handler>'
                );

                // method args
                if (args) {
                    args = JSON.stringify(args);

                    // potential emits from args
                    let potential_emits = args.match(/\{FLOW\:([^\}]+)\}/g);
                    let emits = [];
                    if (potential_emits) {
                        potential_emits.forEach(emit => {
                            let replace = emit;
                            emit = emit.slice(6, -1).replace('/', '');
                            emit = '_:' + crypto.createHash('md5').update(emit).digest('hex');
                            args = args.replace(replace, emit);
                            emits.push(emit);
                        });
                    }

                    let args_id = getHash(args);
                    write(
                        handler_id,
                        'http://schema.jillix.net/vocab/args',
                        args_id
                    );

                    emits.forEach(emit => {
                        let key = args_id + emit;
                        if (!temp_index[key]) {
                            temp_index[key] = 1;
                            write(
                                args_id,
                                'http://schema.jillix.net/vocab/emit',
                                emit
                            );
                        }
                    });
                }

                // state
                write(
                    handler_id,
                    'http://schema.jillix.net/vocab/state',
                    getHash(handler.state)
                );

                switch (handler.type) {
                    case '.':
                    case ':':
                        // method (data)
                        write(
                            handler_id,
                            'http://schema.jillix.net/vocab/data',
                            handler.path
                        );
                        break;
                    case '*':
                        // method (stream)
                        write(
                            handler_id,
                            'http://schema.jillix.net/vocab/stream',
                            handler.path
                        );
                        break;

                    case '>':
                        // emit
                        write(
                            handler_id,
                            'http://schema.jillix.net/vocab/emit',
                            handler.path
                        );
                        break;
                }

                // next
                write(
                    index === 0 ? sequence_id : previous,
                    'http://schema.jillix.net/vocab/next',
                    handler_id
                );

                previous = handler_id;

                // link back to sequence (owner)
                write(
                    sequence_id,
                    'http://schema.jillix.net/vocab/handler',
                    handler_id
                );
            });
        }
    }
}
<commit_msg>add Environment and Arguments types<commit_after>'use strict'

const crypto = require('crypto');
const resolve = require('path').resolve;
const fs = require('fs');
const suffixTest = /\.json$/;
const root = resolve(process.argv[2] || '.') + '/';
const env_config = require(root + 'flow.json');
const path = root + 'composition/';
const states = {};
const files = fs.readdirSync(path);
const rdf_syntax = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
const hashids = {};
const hashlbs = {};
const envs = {};
const temp_index = {};

function write (subject, predicate, object) {
    process.stdout.write(subject + ' <' + predicate + '> ' + object + ' .\n');
}

function getMethodIri (module, method) {

    // git+https://github.com/ + module + .git
    let owner = 'jillix';
    switch (module) {
        case 'flow-view':
        case 'flow-auth':
        case 'flow-streams':
        case 'flow-visualizer':
        case 'flow-compress':
            owner = 'adioo';
            break;
        case 'flow-keypress':
        case 'flow-ace':
            owner = 'danandrei';
            break;
    }

    // TODO method descriptor
    return '<' + owner + '/' + module + '?' + method + '>';
}

function parseHandler (state, handler) {
    handler = handler.split('/');
    let parsed = {
        type: handler[0][0]
    };
    handler[0] = handler[0].substr(1);

    if (handler.length > 1) {
        parsed.state = handler[0];
        parsed.path = handler[1];
    } else {
        parsed.state = state;
        parsed.path = handler[0]
    }

    if (parsed.type !== '>') {
        parsed.path = getMethodIri(states[parsed.state].module, parsed.path);
    } else {
        parsed.path = '_:' + crypto.createHash('md5').update(parsed.state + parsed.path).digest('hex');
    }

    return parsed;
}

function UID (len) {
    len = len || 23;
    let i = 0, random = '';
    for (; i < len; ++i) {
        random += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[0 | Math.random() * 62];
    }
    return '_:' + crypto.createHash('md5').update(random).digest('hex');
}

function getHash (string) {
    let hash = '_:' + crypto.createHash('md5').update(string).digest('hex');
    if (!hashids[hash]) {
        hashids[hash] = 1;
        write(hash, rdf_syntax + 'string', '"' + string.replace(/"/g, '\\"') + '"');
    }

    return hash;
}

// create network triple
const network_id = '_:' + crypto.createHash('md5').update(env_config.network).digest('hex');

// network name (Service)
write(
    network_id,
    'http://schema.org/name',
    getHash(env_config.network)
);

// network type
write(
    network_id,
    rdf_syntax + 'type',
    '<http://schema.jillix.net/vocab/Network>'
);

// Convert composition files to triples
files.forEach(file => {

    if (!suffixTest.test(file)) {
        return;
    }

    try {
        let state = JSON.parse(fs.readFileSync(path + file));
        states[state.name] = state;
    } catch (error) {
        throw new Error(path + file + '\n' + error);
    }
});

// create env objects
if (env_config) {

    if (env_config.environments) {
        env_config.environments.forEach(env => {
            envs[env.name] = getHash(env.name);

            // environment name
            write(
                envs[env.name],
                'http://schema.org/name',
                envs[env.name]
            );

            // environment vars
            write(
                envs[env.name],
                'http://schema.jillix.net/vocab/json',
                getHash(JSON.stringify(env.vars))
            );

            // environment type
            write(
                envs[env.name],
                rdf_syntax + 'type',
                '<http://schema.jillix.net/vocab/Environment>'
            );
        });
    }

    if (env_config.entrypoints) {
        env_config.entrypoints.forEach(ep => {

            const entrypoint_id = UID();

            // network entrypoint
            write(
                network_id,
                'http://schema.jillix.net/vocab/entrypoint',
                entrypoint_id
            );

            // entrypoint name
            write(
                entrypoint_id,
                'http://schema.org/name',
                getHash(ep.name)
            );

            // entrypoint type
            write(
                entrypoint_id,
                rdf_syntax + 'type',
                '<http://schema.jillix.net/vocab/Entrypoint>'
            );

            // entrypoint environment
            if (ep.env) {
                ep.env.forEach(env => {
                    if (envs[env]) {
                        write(
                            entrypoint_id,
                            'http://schema.jillix.net/vocab/environment',
                            envs[env]
                        );
                    }
                });
            }

            // entrypoint sequence
            write(
                entrypoint_id,
                'http://schema.jillix.net/vocab/sequence',
                '_:' + crypto.createHash('md5').update(ep.emit.replace('/', '')).digest('hex')
            );
        });
    }
}

for (let name in states) {
    let state = states[name];

    if (!state.flow) {
        continue;
    }

    for (let sequence in state.flow) {
        let sequence_id = '_:' + crypto.createHash('md5').update(state.name + sequence).digest('hex');

        // name
        write(
            sequence_id,
            'http://schema.org/name',
            getHash(state.name + '-' + sequence)
        );

        // type
        write(
            sequence_id,
            rdf_syntax + 'type',
            '<http://schema.jillix.net/vocab/Sequence>'
        );

        // roles
        if (state.roles) {
            for (let role in state.roles) {
                write(
                    sequence_id,
                    'http://schema.jillix.net/vocab/role',
                    getHash(role)
                );
            }
        }

        // end event
        if (state.flow[sequence].e) {
            write(
                sequence_id,
                'http://schema.jillix.net/vocab/onEnd',
                '_:' + crypto.createHash('md5').update(state.flow[sequence].e.replace('/', '')).digest('hex')
            );
        }

        // error event
        if (state.flow[sequence].r) {
            write(
                sequence_id,
                'http://schema.jillix.net/vocab/onError',
                '_:' + crypto.createHash('md5').update(state.flow[sequence].r.replace('/', '')).digest('hex')
            );
        }

        // sequence
        if (state.flow[sequence].d) {
            let previous;
            state.flow[sequence].d.forEach((handler, index) => {

                let handler_id = UID();
                let args;

                if (typeof handler === 'string') {
                    handler = handler;
                    args = null;
                } else {
                    args = handler[1];
                    handler = handler[0];
                }

                handler = parseHandler(state.name, handler);

                // type
                write(
                    handler_id,
                    rdf_syntax + 'type',
                    '<http://schema.jillix.net/vocab/Handler>'
                );

                // method args
                if (args) {
                    args = JSON.stringify(args);

                    // potential emits from args
                    let potential_emits = args.match(/\{FLOW\:([^\}]+)\}/g);
                    let emits = [];
                    if (potential_emits) {
                        potential_emits.forEach(emit => {
                            let replace = emit;
                            emit = emit.slice(6, -1).replace('/', '');
                            emit = '_:' + crypto.createHash('md5').update(emit).digest('hex');
                            args = args.replace(replace, emit);
                            emits.push(emit);
                        });
                    }

                    let args_hid = getHash(args);
                    let args_uid = UID();
                    let args_lbl = "Args";

                    // handler args connection
                    write(
                        handler_id,
                        'http://schema.jillix.net/vocab/args',
                        args_uid
                    );

                    // arg object
                    write(
                        args_uid,
                        'http://schema.jillix.net/vocab/json',
                        args_hid
                    );

                    // args type
                    write(
                        args_uid,
                        rdf_syntax + 'type',
                        '<http://schema.jillix.net/vocab/Arguments>'
                    );

                    // args name
                    write(
                        args_uid,
                        'http://schema.org/name',
                        getHash('Args')
                    );

                    emits.forEach(emit => {
                        let key = args_uid + emit;
                        if (!temp_index[key]) {
                            temp_index[key] = 1;
                            write(
                                args_uid,
                                'http://schema.jillix.net/vocab/emit',
                                emit
                            );
                        }
                    });
                }

                // state
                write(
                    handler_id,
                    'http://schema.jillix.net/vocab/state',
                    getHash(handler.state, [handler.state])
                );

                switch (handler.type) {
                    case '.':
                    case ':':
                        // method (data)
                        write(
                            handler_id,
                            'http://schema.jillix.net/vocab/data',
                            handler.path
                        );
                        break;
                    case '*':
                        // method (stream)
                        write(
                            handler_id,
                            'http://schema.jillix.net/vocab/stream',
                            handler.path
                        );
                        break;

                    case '>':
                        // emit
                        write(
                            handler_id,
                            'http://schema.jillix.net/vocab/emit',
                            handler.path
                        );
                        break;
                }

                // next
                write(
                    index === 0 ? sequence_id : previous,
                    'http://schema.jillix.net/vocab/next',
                    handler_id
                );

                previous = handler_id;

                // link back to sequence (owner)
                write(
                    sequence_id,
                    'http://schema.jillix.net/vocab/handler',
                    handler_id
                );
            });
        }
    }
}
<|endoftext|>


<commit_before>/*global require:true */
var Countree = require('../Countree.js');

/*
 ======== A Handy Little Nodeunit Reference ========
 https://github.com/caolan/nodeunit

 Test methods:
 test.expect(numAssertions)
 test.done()
 Test assertions:
 test.ok(value, [message])
 test.equal(actual, expected, [message])
 test.notEqual(actual, expected, [message])
 test.deepEqual(actual, expected, [message])
 test.notDeepEqual(actual, expected, [message])
 test.strictEqual(actual, expected, [message])
 test.notStrictEqual(actual, expected, [message])
 test.throws(block, [error], [message])
 test.doesNotThrow(block, [error], [message])
 test.ifError(value)
 */

function createCountResultUpdatedToMilliseconds(milliseconds) {
    var countResult = new Countree.CountResult();
    countResult.update(milliseconds);
    return countResult;
}

exports['CountResult'] = {
    /*setUp: function (done) {
        done();
    },*/
    'milliseconds to timeObject converting: valid': function (test) {
        var countResult = createCountResultUpdatedToMilliseconds(446582010);

        test.expect(6);
        test.equal(countResult.getMillisecondsLeft(), 446582010);
        test.equal(countResult.getAsTimeObject().milliseconds, 10);
        test.equal(countResult.getAsTimeObject().seconds, 2);
        test.equal(countResult.getAsTimeObject().minutes, 3);
        test.equal(countResult.getAsTimeObject().hours, 4);
        test.equal(countResult.getAsTimeObject().days, 5);
        test.done();
    },
    'milliseconds to timeObject converting: invalid': function (test) {
        var countResult = createCountResultUpdatedToMilliseconds(446582010);

        test.expect(6);
        test.notEqual(countResult.overallMillisecondsLeft, 446582011, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().milliseconds, 11, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().seconds, 21, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().minutes, 31, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().hours, 41, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().days, 51, 'should be invalid');
        test.done();
    },
    'milliseconds to doubleDigit timeObject strings: valid': function (test) {
        var countResult = createCountResultUpdatedToMilliseconds(446582010);

        test.expect(4);
        test.equal(countResult.getAsTimeObject().getMillisecondsAsTripleDigitString(), '010');
        test.equal(countResult.getAsTimeObject().getSecondsAsDoubleDigitString(), '02');
        test.equal(countResult.getAsTimeObject().getMinutesAsDoubleDigitString(), '03');
        test.equal(countResult.getAsTimeObject().getHoursAsDoubleDigitString(), '04');
        test.done();
    },
    'milliseconds to doubleDigit timeObject strings: invalid': function (test) {
        var countResult = createCountResultUpdatedToMilliseconds(446582010);

        test.expect(4);
        test.notEqual(countResult.getAsTimeObject().getMillisecondsAsTripleDigitString(), '011', 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().getSecondsAsDoubleDigitString(), '2', 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().getMinutesAsDoubleDigitString(), '04', 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().getHoursAsDoubleDigitString(), '5', 'should be invalid');
        test.done();
    }
};
<commit_msg>Fix test<commit_after>/*global require:true */
var Countree = require('../Countree.js');

/*
 ======== A Handy Little Nodeunit Reference ========
 https://github.com/caolan/nodeunit

 Test methods:
 test.expect(numAssertions)
 test.done()
 Test assertions:
 test.ok(value, [message])
 test.equal(actual, expected, [message])
 test.notEqual(actual, expected, [message])
 test.deepEqual(actual, expected, [message])
 test.notDeepEqual(actual, expected, [message])
 test.strictEqual(actual, expected, [message])
 test.notStrictEqual(actual, expected, [message])
 test.throws(block, [error], [message])
 test.doesNotThrow(block, [error], [message])
 test.ifError(value)
 */

function createCountResultUpdatedToMilliseconds(milliseconds) {
    var countResult = new Countree.CountResult();
    countResult.update(milliseconds);
    return countResult;
}

exports['CountResult'] = {
    /*setUp: function (done) {
        done();
    },*/
    'milliseconds to timeObject converting: valid': function (test) {
        var countResult = createCountResultUpdatedToMilliseconds(446582010);

        test.expect(6);
        test.equal(countResult.getMillisecondsLeft(), 446582010);
        test.equal(countResult.getAsTimeObject().milliseconds, 10);
        test.equal(countResult.getAsTimeObject().seconds, 2);
        test.equal(countResult.getAsTimeObject().minutes, 3);
        test.equal(countResult.getAsTimeObject().hours, 4);
        test.equal(countResult.getAsTimeObject().days, 5);
        test.done();
    },
    'milliseconds to timeObject converting: invalid': function (test) {
        var countResult = createCountResultUpdatedToMilliseconds(446582010);

        test.expect(6);
        test.notEqual(countResult.getMillisecondsLeft(), 446582011, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().milliseconds, 11, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().seconds, 21, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().minutes, 31, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().hours, 41, 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().days, 51, 'should be invalid');
        test.done();
    },
    'milliseconds to doubleDigit timeObject strings: valid': function (test) {
        var countResult = createCountResultUpdatedToMilliseconds(446582010);

        test.expect(4);
        test.equal(countResult.getAsTimeObject().getMillisecondsAsTripleDigitString(), '010');
        test.equal(countResult.getAsTimeObject().getSecondsAsDoubleDigitString(), '02');
        test.equal(countResult.getAsTimeObject().getMinutesAsDoubleDigitString(), '03');
        test.equal(countResult.getAsTimeObject().getHoursAsDoubleDigitString(), '04');
        test.done();
    },
    'milliseconds to doubleDigit timeObject strings: invalid': function (test) {
        var countResult = createCountResultUpdatedToMilliseconds(446582010);

        test.expect(4);
        test.notEqual(countResult.getAsTimeObject().getMillisecondsAsTripleDigitString(), '011', 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().getSecondsAsDoubleDigitString(), '2', 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().getMinutesAsDoubleDigitString(), '04', 'should be invalid');
        test.notEqual(countResult.getAsTimeObject().getHoursAsDoubleDigitString(), '5', 'should be invalid');
        test.done();
    }
};
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */

// Provides control sap.m.ObjectAttribute.
sap.ui.define(['jquery.sap.global', './library', 'sap/ui/core/Control'],
	function(jQuery, library, Control) {
	"use strict";

	/**
	 * Constructor for a new ObjectAttribute.
	 *
	 * @param {string} [sId] ID for the new control, generated automatically if no ID is given
	 * @param {object} [mSettings] Initial settings for the new control
	 *
	 * @class
	 * The ObjectAttribute control displays a text field that can be normal or active. The ObjectAttribute fires a press event when the user selects active text.
	 * @extends sap.ui.core.Control
	 * @version ${version}
	 *
	 * @constructor
	 * @public
	 * @since 1.12
	 * @alias sap.m.ObjectAttribute
	 * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
	 */
	var ObjectAttribute = Control.extend("sap.m.ObjectAttribute", /** @lends sap.m.ObjectAttribute.prototype */ { metadata : {

		library : "sap.m",
		properties : {

			/**
			 * Defines the ObjectAttribute title.
			 */
			title : {type : "string", group : "Misc", defaultValue : null},

			/**
			 * Defines the ObjectAttribute text.
			 */
			text : {type : "string", group : "Misc", defaultValue : null},

			/**
			 * Indicates if the ObjectAttribute text is selectable for the user.
			 *
			 * Note: As of version 1.48, only the value of the text property becomes active (styled and acts like a link) as opposed to both the title and text in the previous versions. If you set this property to true, you have to also set the text property.
			 */
			active : {type : "boolean", group : "Misc", defaultValue : null},

			/**
			 * Determines the direction of the text, not including the title.
			 * Available options for the text direction are LTR (left-to-right) and RTL (right-to-left). By default the control inherits the text direction from its parent control.
			 */
			textDirection : {type : "sap.ui.core.TextDirection", group : "Appearance", defaultValue : sap.ui.core.TextDirection.Inherit}
		},
		aggregations : {

			/**
			 * When the aggregation is set, it replaces the text, active and textDirection properties. This also ignores the press event. The provided control is displayed as an active link.
			 * Note: It will only allow sap.m.Text and sap.m.Link controls.
			 */
			customContent : {type : "sap.ui.core.Control", multiple : false},

			/**
			 * Text control to display title and text property.
			 */
			_textControl : {type : "sap.ui.core.Control", multiple : false, visibility : "hidden"}
		},
		events : {

			/**
			 * Fires when the user clicks on active text.
			 */
			press : {
				parameters : {

					/**
					 * DOM reference of the ObjectAttribute's text to be used for positioning.
					 */
					domRef : {type : "string"}
				}
			}
		}
	}});

	ObjectAttribute.MAX_LINES = {
		SINGLE_LINE: 1,
		MULTI_LINE: 2
	};

	/**
	 *  Initializes member variables.
	 *
	 * @private
	 */
	ObjectAttribute.prototype.init = function() {
		this.setAggregation('_textControl', new sap.m.Text());
	};

	/**
	 * Delivers text control with updated title, text and maxLines properties.
	 *
	 * @private
	 */
	ObjectAttribute.prototype._getUpdatedTextControl = function() {
		var oAttrAggregation = this.getAggregation('customContent') || this.getAggregation('_textControl'),
			sTitle = this.getTitle(),
			sText = this.getAggregation('customContent') ? this.getAggregation('customContent').getText() : this.getText(),
			sTextDir = this.getTextDirection(),
			oParent = this.getParent(),
			bPageRTL = sap.ui.getCore().getConfiguration().getRTL(),
			iMaxLines = ObjectAttribute.MAX_LINES.MULTI_LINE,
			bWrap = true,
			oppositeDirectionMarker = '';

		if (sTextDir === sap.ui.core.TextDirection.LTR && bPageRTL) {
			oppositeDirectionMarker = '\u200e';
		}
		if (sTextDir === sap.ui.core.TextDirection.RTL && !bPageRTL) {
			oppositeDirectionMarker = '\u200f';
		}
		sText = oppositeDirectionMarker + sText + oppositeDirectionMarker;
		if (sTitle) {
			sText = sTitle + ": " + sText;
		}
		oAttrAggregation.setProperty('text', sText, true);

		//if attribute is used inside responsive ObjectHeader or in ObjectListItem - only 1 line
		if (oParent instanceof sap.m.ObjectListItem) {
			bWrap = false;
			iMaxLines = ObjectAttribute.MAX_LINES.SINGLE_LINE;
		}

		this._setControlWrapping(oAttrAggregation, bWrap, iMaxLines);

		return oAttrAggregation;
	};

	/**
	 * Sets the appropriate property to the customContent aggregation.
	 *
	 * @private
	 */
	ObjectAttribute.prototype._setControlWrapping = function(oAttrAggregation, bWrap, iMaxLines) {
		if (oAttrAggregation instanceof sap.m.Link) {
			oAttrAggregation.setProperty('wrapping', bWrap, true);
		}
		if (oAttrAggregation instanceof sap.m.Text) {
			oAttrAggregation.setProperty('maxLines', iMaxLines, true);
		}
	};

	/**
	 * @private
	 */
	ObjectAttribute.prototype.ontap = function(oEvent) {
		//event should only be fired if the click is on the text
		if (this._isSimulatedLink() && (oEvent.target.id != this.getId())) {
			this.firePress({
				domRef : this.getDomRef()
			});
		}
	};

	/**
	 * @private
	 */
	ObjectAttribute.prototype.onsapenter = function(oEvent) {
		if (this._isSimulatedLink()) {
			this.firePress({
				domRef : this.getDomRef()
			});

			// mark the event that it is handled by the control
			oEvent.setMarked();
		}
	};

	/**
	 * @private
	 */
	ObjectAttribute.prototype.onsapspace = function(oEvent) {
		this.onsapenter(oEvent);
	};

	/**
	 * Checks if ObjectAttribute is empty.
	 *
	 * @private
	 * @returns {boolean} true if ObjectAttribute's text is empty or only consists of whitespaces
	 */
	ObjectAttribute.prototype._isEmpty = function() {
		if (this.getAggregation('customContent') && !(this.getAggregation('customContent') instanceof sap.m.Link || this.getAggregation('customContent') instanceof sap.m.Text)) {
			jQuery.sap.log.warning("Only sap.m.Link or sap.m.Text are allowed in \"sap.m.ObjectAttribute.customContent\" aggregation");
			return true;
		}

		return !(this.getText().trim() || this.getTitle().trim());
	};

	/**
	 * Called when the control is touched.
	 *
	 * @private
	 */
	ObjectAttribute.prototype.ontouchstart = function(oEvent) {
		if (this._isSimulatedLink()) {
			// for control who need to know if they should handle events from the ObjectAttribute control
			oEvent.originalEvent._sapui_handledByControl = true;
		}
	};

	/**
	 * Defines to which DOM reference the Popup should be docked.
	 *
	 * @protected
	 * @return {DomNode} The DOM reference that Popup should dock to
	 */
	ObjectAttribute.prototype.getPopupAnchorDomRef = function() {
		return this.getDomRef("text");
	};

	ObjectAttribute.prototype._isSimulatedLink = function () {
		return (this.getActive() && this.getText() !== "") && !this.getAggregation('customContent');
	};

	return ObjectAttribute;

}, /* bExport= */ true);
<commit_msg>[INTERNAL] sap.m.ObjectAttribute: Added a note in the about section<commit_after>/*!
 * ${copyright}
 */

// Provides control sap.m.ObjectAttribute.
sap.ui.define(['jquery.sap.global', './library', 'sap/ui/core/Control'],
	function(jQuery, library, Control) {
	"use strict";

	/**
	 * Constructor for a new ObjectAttribute.
	 *
	 * @param {string} [sId] ID for the new control, generated automatically if no ID is given
	 * @param {object} [mSettings] Initial settings for the new control
	 *
	 * @class
	 * The ObjectAttribute control displays a text field that can be normal or active.
	 * The ObjectAttribute fires a press event when the user chooses the active text.
	 *
	 * Note: If property active is set to true, only the value of the
	 * text property is styled and acts as a link. In this case the text
	 * property must also be set, as otherwise there will be no link displayed for the user.
	 * @extends sap.ui.core.Control
	 * @version ${version}
	 *
	 * @constructor
	 * @public
	 * @since 1.12
	 * @alias sap.m.ObjectAttribute
	 * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
	 */
	var ObjectAttribute = Control.extend("sap.m.ObjectAttribute", /** @lends sap.m.ObjectAttribute.prototype */ { metadata : {

		library : "sap.m",
		properties : {

			/**
			 * Defines the ObjectAttribute title.
			 */
			title : {type : "string", group : "Misc", defaultValue : null},

			/**
			 * Defines the ObjectAttribute text.
			 */
			text : {type : "string", group : "Misc", defaultValue : null},

			/**
			 * Indicates if the ObjectAttribute text is selectable for the user.
			 *
			 * Note: As of version 1.48, only the value of the text property becomes active (styled and acts like a link) as opposed to both the title and text in the previous versions. If you set this property to true, you have to also set the text property.
			 */
			active : {type : "boolean", group : "Misc", defaultValue : null},

			/**
			 * Determines the direction of the text, not including the title.
			 * Available options for the text direction are LTR (left-to-right) and RTL (right-to-left). By default the control inherits the text direction from its parent control.
			 */
			textDirection : {type : "sap.ui.core.TextDirection", group : "Appearance", defaultValue : sap.ui.core.TextDirection.Inherit}
		},
		aggregations : {

			/**
			 * When the aggregation is set, it replaces the text, active and textDirection properties. This also ignores the press event. The provided control is displayed as an active link.
			 * Note: It will only allow sap.m.Text and sap.m.Link controls.
			 */
			customContent : {type : "sap.ui.core.Control", multiple : false},

			/**
			 * Text control to display title and text property.
			 */
			_textControl : {type : "sap.ui.core.Control", multiple : false, visibility : "hidden"}
		},
		events : {

			/**
			 * Fires when the user clicks on active text.
			 */
			press : {
				parameters : {

					/**
					 * DOM reference of the ObjectAttribute's text to be used for positioning.
					 */
					domRef : {type : "string"}
				}
			}
		}
	}});

	ObjectAttribute.MAX_LINES = {
		SINGLE_LINE: 1,
		MULTI_LINE: 2
	};

	/**
	 *  Initializes member variables.
	 *
	 * @private
	 */
	ObjectAttribute.prototype.init = function() {
		this.setAggregation('_textControl', new sap.m.Text());
	};

	/**
	 * Delivers text control with updated title, text and maxLines properties.
	 *
	 * @private
	 */
	ObjectAttribute.prototype._getUpdatedTextControl = function() {
		var oAttrAggregation = this.getAggregation('customContent') || this.getAggregation('_textControl'),
			sTitle = this.getTitle(),
			sText = this.getAggregation('customContent') ? this.getAggregation('customContent').getText() : this.getText(),
			sTextDir = this.getTextDirection(),
			oParent = this.getParent(),
			bPageRTL = sap.ui.getCore().getConfiguration().getRTL(),
			iMaxLines = ObjectAttribute.MAX_LINES.MULTI_LINE,
			bWrap = true,
			oppositeDirectionMarker = '';

		if (sTextDir === sap.ui.core.TextDirection.LTR && bPageRTL) {
			oppositeDirectionMarker = '\u200e';
		}
		if (sTextDir === sap.ui.core.TextDirection.RTL && !bPageRTL) {
			oppositeDirectionMarker = '\u200f';
		}
		sText = oppositeDirectionMarker + sText + oppositeDirectionMarker;
		if (sTitle) {
			sText = sTitle + ": " + sText;
		}
		oAttrAggregation.setProperty('text', sText, true);

		//if attribute is used inside responsive ObjectHeader or in ObjectListItem - only 1 line
		if (oParent instanceof sap.m.ObjectListItem) {
			bWrap = false;
			iMaxLines = ObjectAttribute.MAX_LINES.SINGLE_LINE;
		}

		this._setControlWrapping(oAttrAggregation, bWrap, iMaxLines);

		return oAttrAggregation;
	};

	/**
	 * Sets the appropriate property to the customContent aggregation.
	 *
	 * @private
	 */
	ObjectAttribute.prototype._setControlWrapping = function(oAttrAggregation, bWrap, iMaxLines) {
		if (oAttrAggregation instanceof sap.m.Link) {
			oAttrAggregation.setProperty('wrapping', bWrap, true);
		}
		if (oAttrAggregation instanceof sap.m.Text) {
			oAttrAggregation.setProperty('maxLines', iMaxLines, true);
		}
	};

	/**
	 * @private
	 */
	ObjectAttribute.prototype.ontap = function(oEvent) {
		//event should only be fired if the click is on the text
		if (this._isSimulatedLink() && (oEvent.target.id != this.getId())) {
			this.firePress({
				domRef : this.getDomRef()
			});
		}
	};

	/**
	 * @private
	 */
	ObjectAttribute.prototype.onsapenter = function(oEvent) {
		if (this._isSimulatedLink()) {
			this.firePress({
				domRef : this.getDomRef()
			});

			// mark the event that it is handled by the control
			oEvent.setMarked();
		}
	};

	/**
	 * @private
	 */
	ObjectAttribute.prototype.onsapspace = function(oEvent) {
		this.onsapenter(oEvent);
	};

	/**
	 * Checks if ObjectAttribute is empty.
	 *
	 * @private
	 * @returns {boolean} true if ObjectAttribute's text is empty or only consists of whitespaces
	 */
	ObjectAttribute.prototype._isEmpty = function() {
		if (this.getAggregation('customContent') && !(this.getAggregation('customContent') instanceof sap.m.Link || this.getAggregation('customContent') instanceof sap.m.Text)) {
			jQuery.sap.log.warning("Only sap.m.Link or sap.m.Text are allowed in \"sap.m.ObjectAttribute.customContent\" aggregation");
			return true;
		}

		return !(this.getText().trim() || this.getTitle().trim());
	};

	/**
	 * Called when the control is touched.
	 *
	 * @private
	 */
	ObjectAttribute.prototype.ontouchstart = function(oEvent) {
		if (this._isSimulatedLink()) {
			// for control who need to know if they should handle events from the ObjectAttribute control
			oEvent.originalEvent._sapui_handledByControl = true;
		}
	};

	/**
	 * Defines to which DOM reference the Popup should be docked.
	 *
	 * @protected
	 * @return {DomNode} The DOM reference that Popup should dock to
	 */
	ObjectAttribute.prototype.getPopupAnchorDomRef = function() {
		return this.getDomRef("text");
	};

	ObjectAttribute.prototype._isSimulatedLink = function () {
		return (this.getActive() && this.getText() !== "") && !this.getAggregation('customContent');
	};

	return ObjectAttribute;

}, /* bExport= */ true);
<|endoftext|>


<commit_before>/* eslint-env mocha */

describe('DynamicLayer', function () {

    var container;
    var map;
    var center = new maptalks.Coordinate(118.846825, 32.046534);

    beforeEach(function () {
        container = document.createElement('div');
        container.style.width = '256px';
        container.style.height = '256px';
        document.body.appendChild(container);
        var option = {
            center: center,
            zoom: 14
        };
        map = new maptalks.Map(container, option);
        var tile = new maptalks.TileLayer('tile', {
            urlTemplate: '/resources/tile.png',
            subdomains: [1, 2, 3]
        });
        map.setBaseLayer(tile);
    });

    afterEach(function () {
        // document.body.innerHTML = '';
    });

    it('test', function () {
        var dynamic = new maptalks.DynamicLayer('d', {
            baseUrl: 'http://localhost:11216/map',
            mapdb: 'sdb',
            layers: [{
                name: 'country_point',
                condition: '',
                fields: ['name', 'kind'],
                style: {
                    'filter': ['==', '$type', 'Point'],
                    'symbol': {
                        'markerFile': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgaGVpZ2h0PSI0NiIgd2lkdGg9IjMyIiB2aWV3Qm94PSIwIDAgMTYgMjMiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiID48ZGVmcz48L2RlZnM+IDxwYXRoICBmaWxsPSIjREUzMzMzIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTTggMjNsMCAwIDAgMCAwIDAgMCAwIDAgMGMtNCwtNSAtOCwtMTAgLTgsLTE0IDAsLTUgNCwtOSA4LC05bDAgMCAwIDBjNCwwIDgsNCA4LDkgMCw0IC00LDkgLTgsMTR6IE01LDkgYTMsMyAwLDEsMCwwLC0wLjlaIj48L3BhdGg+IDwvc3ZnPg=='
                    }
                }
            }]
        });
        map.addLayer(dynamic);
    });

});
<commit_msg>update DynamicLayerSpec<commit_after>/* eslint-env mocha */

describe('DynamicLayer', function () {

    var container;
    var map;
    var p1 = [110.582514, 27.87486003];
    var p2 = [110.6846798, 28.00622502];
    var c = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2];
    var center = new maptalks.Coordinate(c);

    beforeEach(function () {
        container = document.createElement('div');
        container.style.width = '512px';
        container.style.height = '512px';
        document.body.appendChild(container);
        var option = {
            center: center,
            zoom: 9
        };
        map = new maptalks.Map(container, option);
        var tile = new maptalks.TileLayer('tile', {
            renderer: 'dom',
            urlTemplate: 'http://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png',
            subdomains: ['a', 'b', 'c']
        });
        map.setBaseLayer(tile);
    });

    afterEach(function () {
        // document.body.innerHTML = '';
    });

    it('test', function () {
        var dynamic = new maptalks.DynamicLayer('d', {
            renderer: 'dom',
            baseUrl: 'http://localhost:11216/map',
            mapdb: 'pg92_chinamap_maptalks',
            // resultCRS: maptalks.CRS.createProj4('+proj=longlat +datum=GCJ02'),
            resultCRS: 'GCJ02',
            layers: [{
                name: 'country_point',
                condition: 'name like \'%乡\'',
                fields: ['name', 'kind'],
                style: {
                    'filter': ['==', '$type', 'Point'],
                    'symbol': {
                        'markerFile': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgaGVpZ2h0PSI0NiIgd2lkdGg9IjMyIiB2aWV3Qm94PSIwIDAgMTYgMjMiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiID48ZGVmcz48L2RlZnM+IDxwYXRoICBmaWxsPSIjREUzMzMzIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTTggMjNsMCAwIDAgMCAwIDAgMCAwIDAgMGMtNCwtNSAtOCwtMTAgLTgsLTE0IDAsLTUgNCwtOSA4LC05bDAgMCAwIDBjNCwwIDgsNCA4LDkgMCw0IC00LDkgLTgsMTR6IE01LDkgYTMsMyAwLDEsMCwwLC0wLjlaIj48L3BhdGg+IDwvc3ZnPg=='
                    }
                }
            }]
        });
        map.addLayer(dynamic);
    });

});
<|endoftext|>


<commit_before>import _ from 'lodash';
import graphlib, { Graph } from 'graphlib';

function initMap() {
  let map = _.chunk(_.fill(Array(20604), 0), 102);
  _.fill(map[0], -1);
  _.fill(map[201], -1);
  map = _.unzip(map);
  _.fill(map[0], -1);
  _.fill(map[101], -1);
  map = _.unzip(map);
  return map;
}

function initRooms(map, roomNum) {
  const rooms = [];
  let i = roomNum;
  while (i) {
    const x = _.random(70, 130);
    const y = _.random(30, 70);
    const goodPosition = _.every([map[x - 1][y - 1], map[x - 1][y], map[x - 1][y + 1],
                                  map[x][y - 1], map[x][y], map[x][y + 1],
                                  map[x + 1][y - 1], map[x + 1][y], map[x + 1][y + 1]],
                                  arg => arg === 0);
    if (goodPosition) {
      _.fill(map[x - 1], 1, y - 1, y + 2);
      _.fill(map[x], 1, y - 1, y + 2);
      _.fill(map[x + 1], 1, y - 1, y + 2);
      map[x - 1][0] = map[x - 1][101] = -1;
      map[x][0] = map[x][101] = -1;
      map[x + 1][0] = map[x + 1][101] = -1;

      i -= 1;

      rooms.push({
        start: {
          x: x - 1,
          y: y - 1,
        },
        end: {
          x: x + 1,
          y: y + 1,
        }
      });
    }
  }
  return rooms;
}

function findCellRoom(rooms, x, y) {
  let room = null;
  rooms.forEach((el, index) => {
    if (x >= el.start.x && x <= el.end.x && y >= el.start.y && y <= el.end.y) {
      room = index;
    }
  });
  return room;
}

function findNeighbours(map, rooms, id) {
  const startX = rooms[id].start.x;
  const endX = rooms[id].end.x;
  const startY = rooms[id].start.y;
  const endY = rooms[id].end.y;
  const neighbours = [];
  for (let i = startX; i <= endX; i += 1) {
    if (map[i][startY - 2]) {
      neighbours.push(findCellRoom(rooms, i, startY - 2));
    }
    if (map[i][endY + 2]) {
      neighbours.push(findCellRoom(rooms, i, endY + 2));
    }
  }
  for (let i = startY; i <= endY; i += 1) {
    if (map[startX - 2][i]) {
      neighbours.push(findCellRoom(rooms, startX - 2, i));
    }
    if (map[endX + 2][i]) {
      neighbours.push(findCellRoom(rooms, endX + 2, i));
    }
  }
  return _.uniq(neighbours);
}

function thereIsAPath(map, rooms) {
  const graph = new Graph({ directed: false });
  rooms.forEach((el, index) => {
    graph.setNode(index);
  });
  rooms.forEach((el, index) => {
    findNeighbours(map, rooms, index).forEach((el2) => {
      graph.setEdge(index, el2);
    });
  });
  return graphlib.alg.components(graph).length === 1;
}

function getBorder(map, rooms, id) {
  const startX = rooms[id].start.x;
  const endX = rooms[id].end.x;
  const startY = rooms[id].start.y;
  const endY = rooms[id].end.y;
  const border = [];
  const sub = [];
  let state = map[startX][startY - 2];
  let hasDoors = false;
  _.range(startX, endX + 1).forEach((el, index) => {
    if(map[startX + index][startY - 2] === 1) {
      if (map[startX + index][startY - 1]) {
        hasDoors = true;
      }
      sub.push({x: startX + index, y: startY - 1});
    } else {
      if (sub.length) {
        if (!hasDoors) {
          border.push(_.clone(sub));
        }
        hasDoors = false;
        sub.length = 0;
      }
    }
  });
  if (sub.length) {
    if (!hasDoors) {
      border.push(_.clone(sub));
    }
    hasDoors = false;
    sub.length = 0;
  }
  _.range(startX, endX + 1).forEach((el, index) => {
    if(map[startX + index][endY + 2] === 1) {
      if (map[startX + index][endY + 1]) {
        hasDoors = true;
      }
      sub.push({x: startX + index, y: endY + 1});
    } else {
      if (sub.length) {
        if (!hasDoors) {
          border.push(_.clone(sub));
        }
        hasDoors = false;
        sub.length = 0;
      }
    }
  });
  if (sub.length) {
    if (!hasDoors) {
      border.push(_.clone(sub));
    }
    hasDoors = false;
    sub.length = 0;
  }
  _.range(startY, endY + 1).forEach((el, index) => {
    if(map[startX - 2][startY + index] === 1) {
      if (map[startX - 1][startY + index]) {
        hasDoors = true;
      }
      sub.push({x: startX - 1, y: startY + index});
    } else {
      if (sub.length) {
        if (!hasDoors) {
          border.push(_.clone(sub));
        }
        hasDoors = false;
        sub.length = 0;
      }
    }
  });
  if (sub.length) {
    if (!hasDoors) {
      border.push(_.clone(sub));
    }
    hasDoors = false;
    sub.length = 0;
  }
  _.range(startY, endY + 1).forEach((el, index) => {
    if(map[endX + 2][startY + index] === 1) {
      if (map[endX + 1][startY + index]) {
        hasDoors = true;
      }
      sub.push({x: endX + 1, y: startY + index});
    } else {
      if (sub.length) {
        if (!hasDoors) {
          border.push(_.clone(sub));
        }
        hasDoors = false;
        sub.length = 0;
      }
    }
  });
  if (sub.length) {
    if (!hasDoors) {
      border.push(_.clone(sub));
    }
  }
  return border;
}

export default function createMap() {
  let map = initMap();
  const rooms = initRooms(map, 15);
  const cheatEslint = true;
  while (cheatEslint) {
    const randomRoomNum = _.random(14);
    const randomRoomStart = rooms[randomRoomNum].start;
    const randomRoomEnd = rooms[randomRoomNum].end;
    if (randomRoomEnd.x - randomRoomStart.x > 25 || randomRoomEnd.y - randomRoomStart.y > 25) {
      continue;
    }
    const growthMode = _.sample(['right', 'left', 'top', 'down']);
    switch (growthMode) {
    case 'right': {
      const neighbourColN = [];
      const pushNeighbours = (el) => {
        neighbourColN.push(map[randomRoomEnd.x + 2][randomRoomStart.y + el]);
      };
      _.range((randomRoomEnd.y - randomRoomStart.y) + 1).forEach(pushNeighbours);
      if (_.every(neighbourColN, el => el === 0)) {
        _.fill(map[randomRoomEnd.x + 1], 1, randomRoomStart.y, randomRoomEnd.y + 1);
        rooms[randomRoomNum].end.x += 1;
      }
      break;
    }
    case 'left': {
      const neighbourColP = [];
      const pushNeighbours = (el) => {
        neighbourColP.push(map[randomRoomStart.x - 2][randomRoomStart.y + el]);
      };
      _.range((randomRoomEnd.y - randomRoomStart.y) + 1).forEach(pushNeighbours);
      if (_.every(neighbourColP, el => el === 0)) {
        _.fill(map[randomRoomStart.x - 1], 1, randomRoomStart.y, randomRoomEnd.y + 1);
        rooms[randomRoomNum].start.x -= 1;
      }
      break;
    }
    case 'down': {
      const neighbourColU = [];
      const pushNeighbours = (el) => {
        neighbourColU.push(map[randomRoomStart.x + el][randomRoomEnd.y + 2]);
      };
      _.range((randomRoomEnd.x - randomRoomStart.x) + 1).forEach(pushNeighbours);
      if (_.every(neighbourColU, el => el === 0)) {
        map = _.unzip(map);
        _.fill(map[randomRoomEnd.y + 1], 1, randomRoomStart.x, randomRoomEnd.x + 1);
        map = _.unzip(map);
        rooms[randomRoomNum].end.y += 1;
      }
      break;
    }
    case 'top': {
      const neighbourColO = [];
      const pushNeighbours = (el) => {
        neighbourColO.push(map[randomRoomStart.x + el][randomRoomStart.y - 2]);
      };
      _.range((randomRoomEnd.x - randomRoomStart.x) + 1).forEach(pushNeighbours);
      if (_.every(neighbourColO, el => el === 0)) {
        map = _.unzip(map);
        _.fill(map[randomRoomStart.y - 1], 1, randomRoomStart.x, randomRoomEnd.x + 1);
        map = _.unzip(map);
        rooms[randomRoomNum].start.y -= 1;
      }
      break;
    }
    default:
      break;
    }
    if (thereIsAPath(map, rooms)) {
      break;
    }
  }
  rooms.forEach((el, index) => {
    const border = getBorder(map, rooms, index);
    if (border.length) {
      border.forEach((el) => {
        const choice = _.sample(el);
        map[choice.x][choice.y] = 1;
      })
    }
  });
  return map;
}
<commit_msg>Resolve eslint warnings<commit_after>import _ from 'lodash';
import graphlib, { Graph } from 'graphlib';

function initMap() {
  let map = _.chunk(_.fill(Array(20604), 0), 102);
  _.fill(map[0], -1);
  _.fill(map[201], -1);
  map = _.unzip(map);
  _.fill(map[0], -1);
  _.fill(map[101], -1);
  map = _.unzip(map);
  return map;
}

function initRooms(map, roomNum) {
  const rooms = [];
  let i = roomNum;
  while (i) {
    const x = _.random(70, 130);
    const y = _.random(30, 70);
    const goodPosition = _.every([map[x - 1][y - 1], map[x - 1][y], map[x - 1][y + 1],
                                  map[x][y - 1], map[x][y], map[x][y + 1],
                                  map[x + 1][y - 1], map[x + 1][y], map[x + 1][y + 1]],
                                  arg => arg === 0);
    if (goodPosition) {
      _.fill(map[x - 1], 1, y - 1, y + 2);
      _.fill(map[x], 1, y - 1, y + 2);
      _.fill(map[x + 1], 1, y - 1, y + 2);
      map[x - 1][0] = map[x - 1][101] = -1;
      map[x][0] = map[x][101] = -1;
      map[x + 1][0] = map[x + 1][101] = -1;

      i -= 1;

      rooms.push({
        start: {
          x: x - 1,
          y: y - 1,
        },
        end: {
          x: x + 1,
          y: y + 1,
        }
      });
    }
  }
  return rooms;
}

function findCellRoom(rooms, x, y) {
  let room = null;
  rooms.forEach((el, index) => {
    if (x >= el.start.x && x <= el.end.x && y >= el.start.y && y <= el.end.y) {
      room = index;
    }
  });
  return room;
}

function findNeighbours(map, rooms, id) {
  const startX = rooms[id].start.x;
  const endX = rooms[id].end.x;
  const startY = rooms[id].start.y;
  const endY = rooms[id].end.y;
  const neighbours = [];
  for (let i = startX; i <= endX; i += 1) {
    if (map[i][startY - 2]) {
      neighbours.push(findCellRoom(rooms, i, startY - 2));
    }
    if (map[i][endY + 2]) {
      neighbours.push(findCellRoom(rooms, i, endY + 2));
    }
  }
  for (let i = startY; i <= endY; i += 1) {
    if (map[startX - 2][i]) {
      neighbours.push(findCellRoom(rooms, startX - 2, i));
    }
    if (map[endX + 2][i]) {
      neighbours.push(findCellRoom(rooms, endX + 2, i));
    }
  }
  return _.uniq(neighbours);
}

function thereIsAPath(map, rooms) {
  const graph = new Graph({ directed: false });
  rooms.forEach((el, index) => {
    graph.setNode(index);
  });
  rooms.forEach((el, index) => {
    findNeighbours(map, rooms, index).forEach((el2) => {
      graph.setEdge(index, el2);
    });
  });
  return graphlib.alg.components(graph).length === 1;
}

function getBorder(map, rooms, id) {
  const startX = rooms[id].start.x;
  const endX = rooms[id].end.x;
  const startY = rooms[id].start.y;
  const endY = rooms[id].end.y;
  const border = [];
  const sub = [];
  let hasDoors = false;
  _.range(startX, endX + 1).forEach((el, index) => {
    if(map[startX + index][startY - 2] === 1) {
      if (map[startX + index][startY - 1]) {
        hasDoors = true;
      }
      sub.push({x: startX + index, y: startY - 1});
    } else {
      if (sub.length) {
        if (!hasDoors) {
          border.push(_.clone(sub));
        }
        hasDoors = false;
        sub.length = 0;
      }
    }
  });
  if (sub.length) {
    if (!hasDoors) {
      border.push(_.clone(sub));
    }
    hasDoors = false;
    sub.length = 0;
  }
  _.range(startX, endX + 1).forEach((el, index) => {
    if(map[startX + index][endY + 2] === 1) {
      if (map[startX + index][endY + 1]) {
        hasDoors = true;
      }
      sub.push({x: startX + index, y: endY + 1});
    } else {
      if (sub.length) {
        if (!hasDoors) {
          border.push(_.clone(sub));
        }
        hasDoors = false;
        sub.length = 0;
      }
    }
  });
  if (sub.length) {
    if (!hasDoors) {
      border.push(_.clone(sub));
    }
    hasDoors = false;
    sub.length = 0;
  }
  _.range(startY, endY + 1).forEach((el, index) => {
    if(map[startX - 2][startY + index] === 1) {
      if (map[startX - 1][startY + index]) {
        hasDoors = true;
      }
      sub.push({x: startX - 1, y: startY + index});
    } else {
      if (sub.length) {
        if (!hasDoors) {
          border.push(_.clone(sub));
        }
        hasDoors = false;
        sub.length = 0;
      }
    }
  });
  if (sub.length) {
    if (!hasDoors) {
      border.push(_.clone(sub));
    }
    hasDoors = false;
    sub.length = 0;
  }
  _.range(startY, endY + 1).forEach((el, index) => {
    if(map[endX + 2][startY + index] === 1) {
      if (map[endX + 1][startY + index]) {
        hasDoors = true;
      }
      sub.push({x: endX + 1, y: startY + index});
    } else {
      if (sub.length) {
        if (!hasDoors) {
          border.push(_.clone(sub));
        }
        hasDoors = false;
        sub.length = 0;
      }
    }
  });
  if (sub.length) {
    if (!hasDoors) {
      border.push(_.clone(sub));
    }
  }
  return border;
}

export default function createMap() {
  let map = initMap();
  const rooms = initRooms(map, 15);
  const cheatEslint = true;
  while (cheatEslint) {
    const randomRoomNum = _.random(14);
    const randomRoomStart = rooms[randomRoomNum].start;
    const randomRoomEnd = rooms[randomRoomNum].end;
    if (randomRoomEnd.x - randomRoomStart.x > 25 || randomRoomEnd.y - randomRoomStart.y > 25) {
      continue;
    }
    const growthMode = _.sample(['right', 'left', 'top', 'down']);
    switch (growthMode) {
    case 'right': {
      const neighbourColN = [];
      const pushNeighbours = (el) => {
        neighbourColN.push(map[randomRoomEnd.x + 2][randomRoomStart.y + el]);
      };
      _.range((randomRoomEnd.y - randomRoomStart.y) + 1).forEach(pushNeighbours);
      if (_.every(neighbourColN, el => el === 0)) {
        _.fill(map[randomRoomEnd.x + 1], 1, randomRoomStart.y, randomRoomEnd.y + 1);
        rooms[randomRoomNum].end.x += 1;
      }
      break;
    }
    case 'left': {
      const neighbourColP = [];
      const pushNeighbours = (el) => {
        neighbourColP.push(map[randomRoomStart.x - 2][randomRoomStart.y + el]);
      };
      _.range((randomRoomEnd.y - randomRoomStart.y) + 1).forEach(pushNeighbours);
      if (_.every(neighbourColP, el => el === 0)) {
        _.fill(map[randomRoomStart.x - 1], 1, randomRoomStart.y, randomRoomEnd.y + 1);
        rooms[randomRoomNum].start.x -= 1;
      }
      break;
    }
    case 'down': {
      const neighbourColU = [];
      const pushNeighbours = (el) => {
        neighbourColU.push(map[randomRoomStart.x + el][randomRoomEnd.y + 2]);
      };
      _.range((randomRoomEnd.x - randomRoomStart.x) + 1).forEach(pushNeighbours);
      if (_.every(neighbourColU, el => el === 0)) {
        map = _.unzip(map);
        _.fill(map[randomRoomEnd.y + 1], 1, randomRoomStart.x, randomRoomEnd.x + 1);
        map = _.unzip(map);
        rooms[randomRoomNum].end.y += 1;
      }
      break;
    }
    case 'top': {
      const neighbourColO = [];
      const pushNeighbours = (el) => {
        neighbourColO.push(map[randomRoomStart.x + el][randomRoomStart.y - 2]);
      };
      _.range((randomRoomEnd.x - randomRoomStart.x) + 1).forEach(pushNeighbours);
      if (_.every(neighbourColO, el => el === 0)) {
        map = _.unzip(map);
        _.fill(map[randomRoomStart.y - 1], 1, randomRoomStart.x, randomRoomEnd.x + 1);
        map = _.unzip(map);
        rooms[randomRoomNum].start.y -= 1;
      }
      break;
    }
    default:
      break;
    }
    if (thereIsAPath(map, rooms)) {
      break;
    }
  }
  rooms.forEach((el, index) => {
    const border = getBorder(map, rooms, index);
    if (border.length) {
      border.forEach((el) => {
        const choice = _.sample(el);
        map[choice.x][choice.y] = 1;
      })
    }
  });
  return map;
}
<|endoftext|>