<commit_before>var nodes = [];
nodes.push(require("./implementations/ExternalClass.js"));
nodes.push(require("./implementations/OwlClass.js"));
nodes.push(require("./implementations/OwlComplementOf.js"));
nodes.push(require("./implementations/OwlDeprecatedClass.js"));
nodes.push(require("./implementations/OwlEquivalentClass.js"));
nodes.push(require("./implementations/OwlIntersectionOf.js"));
nodes.push(require("./implementations/OwlNothing.js"));
nodes.push(require("./implementations/OwlThing.js"));
nodes.push(require("./implementations/OwlUnionOf.js"));
nodes.push(require("./implementations/RdfsClass.js"));
nodes.push(require("./implementations/RdfsDatatype.js"));
nodes.push(require("./implementations/RdfsLiteral.js"));
nodes.push(require("./implementations/RdfsResource.js"));

var map = d3.map(nodes, function (Prototype) {
	return new Prototype().type().toLowerCase();
});

module.exports = function () {
	return map;
};
<commit_msg>Don't change case when storing nodes by types<commit_after>var nodes = [];
nodes.push(require("./implementations/ExternalClass.js"));
nodes.push(require("./implementations/OwlClass.js"));
nodes.push(require("./implementations/OwlComplementOf.js"));
nodes.push(require("./implementations/OwlDeprecatedClass.js"));
nodes.push(require("./implementations/OwlEquivalentClass.js"));
nodes.push(require("./implementations/OwlIntersectionOf.js"));
nodes.push(require("./implementations/OwlNothing.js"));
nodes.push(require("./implementations/OwlThing.js"));
nodes.push(require("./implementations/OwlUnionOf.js"));
nodes.push(require("./implementations/RdfsClass.js"));
nodes.push(require("./implementations/RdfsDatatype.js"));
nodes.push(require("./implementations/RdfsLiteral.js"));
nodes.push(require("./implementations/RdfsResource.js"));

var map = d3.map(nodes, function (Prototype) {
	return new Prototype().type();
});

module.exports = function () {
	return map;
};
<|endoftext|>


<commit_before>import mixin from 'reactjs-mixin';
import {Link} from 'react-router';
/* eslint-disable no-unused-vars */
import React from 'react';
/* eslint-enable no-unused-vars */
import {StoreMixin} from 'mesosphere-shared-reactjs';

import AddRepositoryFormModal from '../../components/modals/AddRepositoryFormModal';
import CosmosPackagesStore from '../../stores/CosmosPackagesStore';
import FilterBar from '../../components/FilterBar';
import FilterInputText from '../../components/FilterInputText';
import Loader from '../../components/Loader';
import Page from '../../components/Page';
import RepositoriesTable from '../../components/RepositoriesTable';
import RequestErrorMsg from '../../components/RequestErrorMsg';

const RepositoriesBreadcrumbs = (addButton) => {
  const crumbs = [
    <Link to="settings/repositories" key={-1}>Respositories</Link>
  ];

  return <Page.Header.Breadcrumbs iconID="gear" breadcrumbs={crumbs} addButton={addButton} />;
};

const METHODS_TO_BIND = [
  'handleSearchStringChange',
  'handleCloseAddRepository',
  'handleOpenAddRepository'
];

class RepositoriesTab extends mixin(StoreMixin) {
  constructor() {
    super();

    this.state = {
      addRepositoryModalOpen: false,
      hasError: false,
      isLoading: true,
      searchString: ''
    };

    this.store_listeners = [
      {
        name: 'cosmosPackages',
        events: ['repositoriesSuccess', 'repositoriesError'],
        suppressUpdate: true
      }
    ];

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  componentDidMount() {
    super.componentDidMount(...arguments);
    CosmosPackagesStore.fetchRepositories();
  }

  handleCloseAddRepository() {
    this.setState({addRepositoryModalOpen: false});
  }

  handleOpenAddRepository() {
    this.setState({addRepositoryModalOpen: true});
  }

  onCosmosPackagesStoreRepositoriesError() {
    this.setState({hasError: true});
  }

  onCosmosPackagesStoreRepositoriesSuccess() {
    this.setState({hasError: false, isLoading: false});
  }

  handleSearchStringChange(searchString = '') {
    this.setState({searchString});
  }

  getErrorScreen() {
    return <RequestErrorMsg />;
  }

  getLoadingScreen() {
    return <Loader />;
  }

  getContent() {
    let {
      addRepositoryModalOpen,
      hasError,
      isLoading,
      searchString
    } = this.state;

    if (hasError) {
      return this.getErrorScreen();
    }

    if (isLoading) {
      return this.getLoadingScreen();
    }

    let repositories = CosmosPackagesStore.getRepositories()
      .filterItemsByText(searchString);

    return (

        <FilterBar rightAlignLastNChildren={1}>
          <FilterInputText
            className="flush-bottom"
            placeholder="Search"
            searchString={searchString}
            handleFilterChange={this.handleSearchStringChange} />
        </FilterBar>
        <RepositoriesTable repositories={repositories} filter={searchString} />
        <AddRepositoryFormModal
          numberOfRepositories={repositories.getItems().length}
          open={addRepositoryModalOpen}
          onClose={this.handleCloseAddRepository} />

    );
  }

  render() {
    return (
      <Page>
        <Page.Header
          addButton={{
            onItemSelect: this.handleOpenAddRepository,
            label: 'Add Repository'
          }}
          breadcrumbs={<RepositoriesBreadcrumbs />} />
        {this.getContent()}
      </Page>
    );
  }
}

RepositoriesTab.routeConfig = {
  label: 'Repositories',
  matches: /^\/settings\/repositories/
};

module.exports = RepositoriesTab;
<commit_msg>Fix typo in Repositories breadcrumbs<commit_after>import mixin from 'reactjs-mixin';
import {Link} from 'react-router';
/* eslint-disable no-unused-vars */
import React from 'react';
/* eslint-enable no-unused-vars */
import {StoreMixin} from 'mesosphere-shared-reactjs';

import AddRepositoryFormModal from '../../components/modals/AddRepositoryFormModal';
import CosmosPackagesStore from '../../stores/CosmosPackagesStore';
import FilterBar from '../../components/FilterBar';
import FilterInputText from '../../components/FilterInputText';
import Loader from '../../components/Loader';
import Page from '../../components/Page';
import RepositoriesTable from '../../components/RepositoriesTable';
import RequestErrorMsg from '../../components/RequestErrorMsg';

const RepositoriesBreadcrumbs = (addButton) => {
  const crumbs = [
    <Link to="settings/repositories" key={-1}>Repositories</Link>
  ];

  return <Page.Header.Breadcrumbs iconID="gear" breadcrumbs={crumbs} addButton={addButton} />;
};

const METHODS_TO_BIND = [
  'handleSearchStringChange',
  'handleCloseAddRepository',
  'handleOpenAddRepository'
];

class RepositoriesTab extends mixin(StoreMixin) {
  constructor() {
    super();

    this.state = {
      addRepositoryModalOpen: false,
      hasError: false,
      isLoading: true,
      searchString: ''
    };

    this.store_listeners = [
      {
        name: 'cosmosPackages',
        events: ['repositoriesSuccess', 'repositoriesError'],
        suppressUpdate: true
      }
    ];

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  componentDidMount() {
    super.componentDidMount(...arguments);
    CosmosPackagesStore.fetchRepositories();
  }

  handleCloseAddRepository() {
    this.setState({addRepositoryModalOpen: false});
  }

  handleOpenAddRepository() {
    this.setState({addRepositoryModalOpen: true});
  }

  onCosmosPackagesStoreRepositoriesError() {
    this.setState({hasError: true});
  }

  onCosmosPackagesStoreRepositoriesSuccess() {
    this.setState({hasError: false, isLoading: false});
  }

  handleSearchStringChange(searchString = '') {
    this.setState({searchString});
  }

  getErrorScreen() {
    return <RequestErrorMsg />;
  }

  getLoadingScreen() {
    return <Loader />;
  }

  getContent() {
    let {
      addRepositoryModalOpen,
      hasError,
      isLoading,
      searchString
    } = this.state;

    if (hasError) {
      return this.getErrorScreen();
    }

    if (isLoading) {
      return this.getLoadingScreen();
    }

    let repositories = CosmosPackagesStore.getRepositories()
      .filterItemsByText(searchString);

    return (

        <FilterBar rightAlignLastNChildren={1}>
          <FilterInputText
            className="flush-bottom"
            placeholder="Search"
            searchString={searchString}
            handleFilterChange={this.handleSearchStringChange} />
        </FilterBar>
        <RepositoriesTable repositories={repositories} filter={searchString} />
        <AddRepositoryFormModal
          numberOfRepositories={repositories.getItems().length}
          open={addRepositoryModalOpen}
          onClose={this.handleCloseAddRepository} />

    );
  }

  render() {
    return (
      <Page>
        <Page.Header
          addButton={{
            onItemSelect: this.handleOpenAddRepository,
            label: 'Add Repository'
          }}
          breadcrumbs={<RepositoriesBreadcrumbs />} />
        {this.getContent()}
      </Page>
    );
  }
}

RepositoriesTab.routeConfig = {
  label: 'Repositories',
  matches: /^\/settings\/repositories/
};

module.exports = RepositoriesTab;
<|endoftext|>


<commit_before>'use strict';

require('../test/browser');

var prettyHrtime = require('pretty-hrtime');

var update = require('../');
var fs = require('fs');
var text = document.createElement('div');
text.innerHTML = fs.readFileSync(__dirname + '/text.html', 'utf8');

var start = process.hrtime();

for (var i = 0; i < 1000; ++i) {
  update(text);
}

console.log(`duration: ${prettyHrtime(process.hrtime(start))} (1000 iterations)`);
<commit_msg>make benchmark run<commit_after>'use strict';

require('../test/browser');

window.getSelection = function () {
  return {
    getRangeAt: function () {
      return {};
    },
    removeAllRanges: function () {},
    addRange: function () {}
  };
};

document.createRange = function () {

};

var prettyHrtime = require('pretty-hrtime');

var update = require('../');
var fs = require('fs');
var text = document.createElement('div');
text.innerHTML = fs.readFileSync(__dirname + '/text.html', 'utf8');

var start = process.hrtime();

for (var i = 0; i < 1000; ++i) {
  update(text);
}

console.log(`duration: ${prettyHrtime(process.hrtime(start))} (1000 iterations)`);
<|endoftext|>


<commit_before>/**
 * Created by mgoria on 6/23/15.
 */

'use strict';

import AWS from 'aws-sdk';
import {AuthException} from './Exception/AuthException';
import {IdentityProviderTokenExpiredException} from './Exception/IdentityProviderTokenExpiredException';
import {DescribeIdentityException} from './Exception/DescribeIdentityException';
import {CredentialsManager} from './CredentialsManager';
import {Exception} from './Exception/Exception';
import {Exception as CoreException} from 'deep-core';
import {Security} from './Security';
import util from 'util';

/**
 * Security token holds details about logged user
 */
export class Token {
  /**
   * @param {String} identityPoolId
   */
  constructor(identityPoolId) {
    this._identityPoolId = identityPoolId;

    this._lambdaContext = null;
    this._user = null;
    this._credentials = null;
    this._identityMetadata = null;
    this._tokenExpiredCallback = null;

    this._identityProvider = null;
    this._userProvider = null;
    this._logService = null;

    this._credsManager = new CredentialsManager(identityPoolId);

    this._setupAwsCognitoConfig();
  }

  /**
   * Setup region for CognitoIdentity and CognitoSync services
   *
   * @private
   */
  _setupAwsCognitoConfig() {
    // @todo: set retries in a smarter way...
    AWS.config.maxRetries = 3;

    let cognitoRegion = Token.getRegionFromIdentityPoolId(this._identityPoolId);

    AWS.config.update({
      cognitoidentity: {region: cognitoRegion},
      cognitosync: {region: cognitoRegion},
    });
  }

  /**
   * @returns {IdentityProvider}
   */
  get identityProvider() {
    return this._identityProvider;
  }

  /**
   * @param {IdentityProvider} provider
   */
  set identityProvider(provider) {
    this._identityProvider = provider;
  }

  /**
   * @returns {Object}
   */
  get lambdaContext() {
    return this._lambdaContext;
  }

  /**
   * @param {Object} lambdaContext
   */
  set lambdaContext(lambdaContext) {
    this._lambdaContext = lambdaContext;
  }

  /**
   * @param {Object} logService
   */
  set logService(logService) {
    this._logService = logService;
  }

  /**
   * @param {Function} callback
   */
  loadCredentials(callback = () => {}) {
    let event = {
      service: 'deep-security',
      resourceType: 'Cognito',
      resourceId: this._identityPoolId,
      eventName: 'loadCredentials',
      eventId: Security.customEventId(this._identityPoolId),
      time: new Date().getTime(),
    };

    let proxyCallback = (error, credentials) => {
      // log event only after credentials are loaded to get identityId
      this._logService.rumLog(event);

      event = util._extend({}, event);
      event.payload = {error, credentials};

      this._logService.rumLog(event);

      callback(error, credentials);
    };

    // avoid refreshing or loading credentials for each request
    if (this.validCredentials(this.credentials)) {
      proxyCallback(null, this.credentials);
      return;
    }

    if (this.lambdaContext) {
      this._backendLoadCredentials(proxyCallback);
    } else {
      this._frontendLoadCredentials(proxyCallback);
    }
  }

  /**
   * @param {Function} callback
   * @private
   */
  _backendLoadCredentials(callback) {
    if (!this.lambdaContext) {
      throw new Exception('Call to _backendLoadCredentials method is not allowed from frontend context.');
    }

    this._credsManager.loadBackendCredentials(this.identityId, (error, credentials) => {
      if (error) {
        callback(error, null);
        return;
      }

      callback(null, this._credentials = credentials);
    });
  }

  /**
   * @param {Function} callback
   * @private
   */
  _frontendLoadCredentials(callback) {
    this._credentials = this._createCognitoIdentityCredentials();

    // set AWS credentials before loading credentials from cache coz amazon-cognito-js uses them
    AWS.config.credentials = this._credentials;

    // trying to load old credentials from cache or CognitoSync
    this._credsManager.loadFrontendCredentials((error, credentials) => {
      if (!error && credentials && this.validCredentials(credentials)) {
        callback(null, AWS.config.credentials = this._credentials = credentials);
      } else {
        this._refreshCredentials(this._credentials, callback);
      }
    });
  }

  /**
   * @param {AWS.CognitoIdentityCredentials} credentials
   * @param {Function} callback
   * @private
   */
  _refreshCredentials(credentials, callback) {
    if (!(credentials instanceof AWS.CognitoIdentityCredentials)) {
      let error = new AuthException(
        'Invalid credentials instance. Passed credentials must be an instance of AWS.CognitoIdentityCredentials.'
      );
      callback(error, null);
      return;
    }

    if (this.identityProvider && !this.identityProvider.isTokenValid()) {
      if (typeof this._tokenExpiredCallback === 'function') {
        this._tokenExpiredCallback(this.identityProvider);
      } else {
        let error = new IdentityProviderTokenExpiredException(
          this.identityProvider.name,
          this.identityProvider.tokenExpirationTime
        );

        callback(error, null);
      }
      return;
    }

    credentials.refresh((error) => {
      if (error) {
        callback(new AuthException(error), null);
        return;
      }

      // @todo - save credentials in background not to affect page load time
      this._credsManager.saveCredentials(credentials, (error, record) => {
        if (error) {
          callback(error, null);
          return;
        }

        callback(null, credentials);
      });
    });
  }

  /**
   * @returns {AWS.CognitoIdentityCredentials}
   * @private
   */
  _createCognitoIdentityCredentials() {
    let cognitoParams = {
      IdentityPoolId: this._identityPoolId,
    };

    if (this.identityProvider) {
      cognitoParams.Logins = {};
      cognitoParams.Logins[this.identityProvider.name] = this.identityProvider.userToken;
      cognitoParams.LoginId = this.identityProvider.userId;
    }

    let credentials = new AWS.CognitoIdentityCredentials(cognitoParams);

    credentials.toJSON = function() {
      return {
        expired: credentials.expired,
        expireTime: credentials.expireTime,
        accessKeyId: credentials.accessKeyId,
        secretAccessKey: credentials.secretAccessKey,
        sessionToken: credentials.sessionToken,
      };
    }.bind(credentials);

    return credentials;
  }

  /**
   * @param {String} identityPoolId
   * @returns {String}
   */
  static getRegionFromIdentityPoolId(identityPoolId) {
    return identityPoolId.split(':')[0];
  }

  /**
   * @returns {String}
   */
  get identityId() {
    let identityId = null;

    if (this.credentials) {
      if (this.credentials.identityId) {
        identityId = this.credentials.identityId;
      } else if (this.credentials.params && this.credentials.params.IdentityId) {
        // load IdentityId from localStorage cache
        identityId = this.credentials.params.IdentityId;
      } else if (this._credsManager.identityId) {
        identityId = this._credsManager.identityId;
      }
    } else if (this.lambdaContext) {
      identityId = this.lambdaContext.identity.cognitoIdentityId;
    }

    return identityId;
  }

  /**
   * @returns {Object}
   */
  get credentials() {
    return this._credentials;
  }

  /**
   * @param {Object} credentials
   * @returns {boolean}
   */
  validCredentials(credentials) {
    return credentials && this.getCredentialsExpireDateTime(credentials) > new Date();
  }

  /**
   * @param {Object} credentials
   * @returns {Date}
   */
  getCredentialsExpireDateTime(credentials) {
    let dateTime = null;

    if (credentials && credentials.hasOwnProperty('expireTime')) {
      dateTime = credentials.expireTime instanceof Date ?
        credentials.expireTime :
        new Date(credentials.expireTime);
    }

    return dateTime;
  }

  /**
   * @returns {Boolean}
   */
  get isAnonymous() {
    return (!this.identityProvider && !this.lambdaContext) || this._identityLogins.length <= 0;
  }

  /**
   * @param {UserProvider} userProvider
   */
  set userProvider(userProvider) {
    this._userProvider = userProvider;
  }

  /**
   * @param {Function} callback
   */
  getUser(callback) {
    if (this.lambdaContext) {
      this._describeIdentity(this.identityId, () => {
        this._loadUser(callback);
      });
    } else {
      this._loadUser(callback);
    }
  }

  /**
   * @param {Function} callback
   * @private
   */
  _loadUser(callback) {
    if (this.isAnonymous) {
      callback(null);
      return;
    }

    if (!this._user) {
      this._userProvider.loadUserByIdentityId(this.identityId, (user) => {
        this._user = user;

        callback(this._user);
      });

      return;
    }

    callback(this._user);
  }

  /**
   * @param {String} identityPoolId
   */
  static create(identityPoolId) {
    return new this(identityPoolId);
  }

  /**
   * @param {String} identityPoolId
   * @param {IdentityProvider} identityProvider
   */
  static createFromIdentityProvider(identityPoolId, identityProvider) {
    let token = new this(identityPoolId);
    token.identityProvider = identityProvider;

    return token;
  }

  /**
   * @param {String} identityPoolId
   * @param {Object} lambdaContext
   */
  static createFromLambdaContext(identityPoolId, lambdaContext) {
    let token = new this(identityPoolId);
    token.lambdaContext = lambdaContext;

    return token;
  }

  /**
   * @param {String} identityId
   * @param {Function} callback
   * @private
   */
  _describeIdentity(identityId, callback) {
    if (this._identityMetadata) {
      callback(this._identityMetadata);
      return;
    }

    let cognitoIdentity = new AWS.CognitoIdentity();

    cognitoIdentity.describeIdentity({IdentityId: identityId}, (error, data) => {
      if (error) {
        throw new DescribeIdentityException(identityId, error);
      }

      this._identityMetadata = data;

      callback(this._identityMetadata);
    });
  }

  /**
   * @returns {Array}
   * @private
   */
  get _identityLogins() {
    return this._identityMetadata && this._identityMetadata.hasOwnProperty('Logins') ?
      this._identityMetadata.Logins :
      [];
  }

  /**
   * @param {Function} callback
   * @returns {Token}
   */
  registerTokenExpiredCallback(callback) {
    if (typeof callback !== 'function') {
      throw new CoreException.InvalidArgumentException(callback, 'function');
    }

    this._tokenExpiredCallback = callback;

    return this;
  }

  /**
   * Removes identity credentials related cached stuff
   */
  destroy() {
    if (!(this._credentials instanceof AWS.CognitoIdentityCredentials)) {
      this._credentials = this._createCognitoIdentityCredentials();
    }

    this._credsManager.deleteCredentials();
    this._credentials.clearCachedId();

    this._credentials = null;
    this._credsManager = null;
  }
}
<commit_msg>Avoid logging user credentials<commit_after>/**
 * Created by mgoria on 6/23/15.
 */

'use strict';

import AWS from 'aws-sdk';
import {AuthException} from './Exception/AuthException';
import {IdentityProviderTokenExpiredException} from './Exception/IdentityProviderTokenExpiredException';
import {DescribeIdentityException} from './Exception/DescribeIdentityException';
import {CredentialsManager} from './CredentialsManager';
import {Exception} from './Exception/Exception';
import {Exception as CoreException} from 'deep-core';
import {Security} from './Security';
import util from 'util';

/**
 * Security token holds details about logged user
 */
export class Token {
  /**
   * @param {String} identityPoolId
   */
  constructor(identityPoolId) {
    this._identityPoolId = identityPoolId;

    this._lambdaContext = null;
    this._user = null;
    this._credentials = null;
    this._identityMetadata = null;
    this._tokenExpiredCallback = null;

    this._identityProvider = null;
    this._userProvider = null;
    this._logService = null;

    this._credsManager = new CredentialsManager(identityPoolId);

    this._setupAwsCognitoConfig();
  }

  /**
   * Setup region for CognitoIdentity and CognitoSync services
   *
   * @private
   */
  _setupAwsCognitoConfig() {
    // @todo: set retries in a smarter way...
    AWS.config.maxRetries = 3;

    let cognitoRegion = Token.getRegionFromIdentityPoolId(this._identityPoolId);

    AWS.config.update({
      cognitoidentity: {region: cognitoRegion},
      cognitosync: {region: cognitoRegion},
    });
  }

  /**
   * @returns {IdentityProvider}
   */
  get identityProvider() {
    return this._identityProvider;
  }

  /**
   * @param {IdentityProvider} provider
   */
  set identityProvider(provider) {
    this._identityProvider = provider;
  }

  /**
   * @returns {Object}
   */
  get lambdaContext() {
    return this._lambdaContext;
  }

  /**
   * @param {Object} lambdaContext
   */
  set lambdaContext(lambdaContext) {
    this._lambdaContext = lambdaContext;
  }

  /**
   * @param {Object} logService
   */
  set logService(logService) {
    this._logService = logService;
  }

  /**
   * @param {Function} callback
   */
  loadCredentials(callback = () => {}) {
    let event = {
      service: 'deep-security',
      resourceType: 'Cognito',
      resourceId: this._identityPoolId,
      eventName: 'loadCredentials',
      eventId: Security.customEventId(this._identityPoolId),
      time: new Date().getTime(),
    };

    let proxyCallback = (error, credentials) => {
      // log event only after credentials are loaded to get identityId
      this._logService.rumLog(event);

      event = util._extend({}, event);
      event.payload = {error, credentials: {}}; // avoid logging user credentials

      this._logService.rumLog(event);

      callback(error, credentials);
    };

    // avoid refreshing or loading credentials for each request
    if (this.validCredentials(this.credentials)) {
      proxyCallback(null, this.credentials);
      return;
    }

    if (this.lambdaContext) {
      this._backendLoadCredentials(proxyCallback);
    } else {
      this._frontendLoadCredentials(proxyCallback);
    }
  }

  /**
   * @param {Function} callback
   * @private
   */
  _backendLoadCredentials(callback) {
    if (!this.lambdaContext) {
      throw new Exception('Call to _backendLoadCredentials method is not allowed from frontend context.');
    }

    this._credsManager.loadBackendCredentials(this.identityId, (error, credentials) => {
      if (error) {
        callback(error, null);
        return;
      }

      callback(null, this._credentials = credentials);
    });
  }

  /**
   * @param {Function} callback
   * @private
   */
  _frontendLoadCredentials(callback) {
    this._credentials = this._createCognitoIdentityCredentials();

    // set AWS credentials before loading credentials from cache coz amazon-cognito-js uses them
    AWS.config.credentials = this._credentials;

    // trying to load old credentials from cache or CognitoSync
    this._credsManager.loadFrontendCredentials((error, credentials) => {
      if (!error && credentials && this.validCredentials(credentials)) {
        callback(null, AWS.config.credentials = this._credentials = credentials);
      } else {
        this._refreshCredentials(this._credentials, callback);
      }
    });
  }

  /**
   * @param {AWS.CognitoIdentityCredentials} credentials
   * @param {Function} callback
   * @private
   */
  _refreshCredentials(credentials, callback) {
    if (!(credentials instanceof AWS.CognitoIdentityCredentials)) {
      let error = new AuthException(
        'Invalid credentials instance. Passed credentials must be an instance of AWS.CognitoIdentityCredentials.'
      );
      callback(error, null);
      return;
    }

    if (this.identityProvider && !this.identityProvider.isTokenValid()) {
      if (typeof this._tokenExpiredCallback === 'function') {
        this._tokenExpiredCallback(this.identityProvider);
      } else {
        let error = new IdentityProviderTokenExpiredException(
          this.identityProvider.name,
          this.identityProvider.tokenExpirationTime
        );

        callback(error, null);
      }
      return;
    }

    credentials.refresh((error) => {
      if (error) {
        callback(new AuthException(error), null);
        return;
      }

      // @todo - save credentials in background not to affect page load time
      this._credsManager.saveCredentials(credentials, (error, record) => {
        if (error) {
          callback(error, null);
          return;
        }

        callback(null, credentials);
      });
    });
  }

  /**
   * @returns {AWS.CognitoIdentityCredentials}
   * @private
   */
  _createCognitoIdentityCredentials() {
    let cognitoParams = {
      IdentityPoolId: this._identityPoolId,
    };

    if (this.identityProvider) {
      cognitoParams.Logins = {};
      cognitoParams.Logins[this.identityProvider.name] = this.identityProvider.userToken;
      cognitoParams.LoginId = this.identityProvider.userId;
    }

    let credentials = new AWS.CognitoIdentityCredentials(cognitoParams);

    credentials.toJSON = function() {
      return {
        expired: credentials.expired,
        expireTime: credentials.expireTime,
        accessKeyId: credentials.accessKeyId,
        secretAccessKey: credentials.secretAccessKey,
        sessionToken: credentials.sessionToken,
      };
    }.bind(credentials);

    return credentials;
  }

  /**
   * @param {String} identityPoolId
   * @returns {String}
   */
  static getRegionFromIdentityPoolId(identityPoolId) {
    return identityPoolId.split(':')[0];
  }

  /**
   * @returns {String}
   */
  get identityId() {
    let identityId = null;

    if (this.credentials) {
      if (this.credentials.identityId) {
        identityId = this.credentials.identityId;
      } else if (this.credentials.params && this.credentials.params.IdentityId) {
        // load IdentityId from localStorage cache
        identityId = this.credentials.params.IdentityId;
      } else if (this._credsManager.identityId) {
        identityId = this._credsManager.identityId;
      }
    } else if (this.lambdaContext) {
      identityId = this.lambdaContext.identity.cognitoIdentityId;
    }

    return identityId;
  }

  /**
   * @returns {Object}
   */
  get credentials() {
    return this._credentials;
  }

  /**
   * @param {Object} credentials
   * @returns {boolean}
   */
  validCredentials(credentials) {
    return credentials && this.getCredentialsExpireDateTime(credentials) > new Date();
  }

  /**
   * @param {Object} credentials
   * @returns {Date}
   */
  getCredentialsExpireDateTime(credentials) {
    let dateTime = null;

    if (credentials && credentials.hasOwnProperty('expireTime')) {
      dateTime = credentials.expireTime instanceof Date ?
        credentials.expireTime :
        new Date(credentials.expireTime);
    }

    return dateTime;
  }

  /**
   * @returns {Boolean}
   */
  get isAnonymous() {
    return (!this.identityProvider && !this.lambdaContext) || this._identityLogins.length <= 0;
  }

  /**
   * @param {UserProvider} userProvider
   */
  set userProvider(userProvider) {
    this._userProvider = userProvider;
  }

  /**
   * @param {Function} callback
   */
  getUser(callback) {
    if (this.lambdaContext) {
      this._describeIdentity(this.identityId, () => {
        this._loadUser(callback);
      });
    } else {
      this._loadUser(callback);
    }
  }

  /**
   * @param {Function} callback
   * @private
   */
  _loadUser(callback) {
    if (this.isAnonymous) {
      callback(null);
      return;
    }

    if (!this._user) {
      this._userProvider.loadUserByIdentityId(this.identityId, (user) => {
        this._user = user;

        callback(this._user);
      });

      return;
    }

    callback(this._user);
  }

  /**
   * @param {String} identityPoolId
   */
  static create(identityPoolId) {
    return new this(identityPoolId);
  }

  /**
   * @param {String} identityPoolId
   * @param {IdentityProvider} identityProvider
   */
  static createFromIdentityProvider(identityPoolId, identityProvider) {
    let token = new this(identityPoolId);
    token.identityProvider = identityProvider;

    return token;
  }

  /**
   * @param {String} identityPoolId
   * @param {Object} lambdaContext
   */
  static createFromLambdaContext(identityPoolId, lambdaContext) {
    let token = new this(identityPoolId);
    token.lambdaContext = lambdaContext;

    return token;
  }

  /**
   * @param {String} identityId
   * @param {Function} callback
   * @private
   */
  _describeIdentity(identityId, callback) {
    if (this._identityMetadata) {
      callback(this._identityMetadata);
      return;
    }

    let cognitoIdentity = new AWS.CognitoIdentity();

    cognitoIdentity.describeIdentity({IdentityId: identityId}, (error, data) => {
      if (error) {
        throw new DescribeIdentityException(identityId, error);
      }

      this._identityMetadata = data;

      callback(this._identityMetadata);
    });
  }

  /**
   * @returns {Array}
   * @private
   */
  get _identityLogins() {
    return this._identityMetadata && this._identityMetadata.hasOwnProperty('Logins') ?
      this._identityMetadata.Logins :
      [];
  }

  /**
   * @param {Function} callback
   * @returns {Token}
   */
  registerTokenExpiredCallback(callback) {
    if (typeof callback !== 'function') {
      throw new CoreException.InvalidArgumentException(callback, 'function');
    }

    this._tokenExpiredCallback = callback;

    return this;
  }

  /**
   * Removes identity credentials related cached stuff
   */
  destroy() {
    if (!(this._credentials instanceof AWS.CognitoIdentityCredentials)) {
      this._credentials = this._createCognitoIdentityCredentials();
    }

    this._credsManager.deleteCredentials();
    this._credentials.clearCachedId();

    this._credentials = null;
    this._credsManager = null;
  }
}
<|endoftext|>


<commit_before>/**
 * 给任意元素添加`<a>`链接效果
 * 该指令适合在某些场景下临时使用，推荐直接使用`<u-link>`或者从`<m-router-item>`继承新组件
 * @modifier blank - 是否打开新的窗口
 */
export const href = {
    bind(el, binding) {
        el.dataset = el.dataset || {};
        el.dataset.href = binding.value;
        el.__hrefHandler = (e) => {
            if (binding.modifiers.blank)
                window.open(el.dataset.href);
            else
                location.href = el.dataset.href;
        };
        el.addEventListener('click', el.__hrefHandler);
    },
    update(el, binding) {
        el.dataset.href = binding.value;
    },
    unbind(el) {
        el.removeEventListener('mouseenter', el.__hrefHandler);
        delete el.__hrefHandler;
    },
};

/**
 * 给任意元素添加`<router-link>`的`to`效果
 * 该指令适合在某些场景下临时使用，推荐直接使用`<u-link>`或者从`<m-router-item>`继承新组件
 * @modifier replace - History 使用`push`方式还是`replace`方式
 */
export const to = {
    bind(el, binding, vnode) {
        el.dataset = el.dataset || {};
        el.dataset.to = binding.value;

        const $router = vnode.componentInstance.$router;
        if (!$router)
            return console.warn('[proto-ui] Use `v-to` but cannot find vue router.');

        el.__toHandler = (e) => {
            binding.modifiers.replace ? $router.replace(el.dataset.to) : $router.push(el.dataset.to);
        };
        el.addEventListener('click', el.__toHandler);
    },
    update(el, binding) {
        el.dataset.to = binding.value;
    },
    unbind(el) {
        if (el.__toHandler) {
            el.removeEventListener('mouseenter', el.__toHandler);
            delete el.__toHandler;
        }
    },
};
<commit_msg>:bug: EventName error<commit_after>/**
 * 给任意元素添加`<a>`链接效果
 * 该指令适合在某些场景下临时使用，推荐直接使用`<u-link>`或者从`<m-router-item>`继承新组件
 * @modifier blank - 是否打开新的窗口
 */
export const href = {
    bind(el, binding) {
        el.dataset = el.dataset || {};
        el.dataset.href = binding.value;
        el.__hrefHandler = (e) => {
            if (binding.modifiers.blank)
                window.open(el.dataset.href);
            else
                location.href = el.dataset.href;
        };
        el.addEventListener('click', el.__hrefHandler);
    },
    update(el, binding) {
        el.dataset.href = binding.value;
    },
    unbind(el) {
        el.removeEventListener('click', el.__hrefHandler);
        delete el.__hrefHandler;
    },
};

/**
 * 给任意元素添加`<router-link>`的`to`效果
 * 该指令适合在某些场景下临时使用，推荐直接使用`<u-link>`或者从`<m-router-item>`继承新组件
 * @modifier replace - History 使用`push`方式还是`replace`方式
 */
export const to = {
    bind(el, binding, vnode) {
        el.dataset = el.dataset || {};
        el.dataset.to = binding.value;

        const $router = vnode.componentInstance.$router;
        if (!$router)
            return console.warn('[proto-ui] Use `v-to` but cannot find vue router.');

        el.__toHandler = (e) => {
            binding.modifiers.replace ? $router.replace(el.dataset.to) : $router.push(el.dataset.to);
        };
        el.addEventListener('click', el.__toHandler);
    },
    update(el, binding) {
        el.dataset.to = binding.value;
    },
    unbind(el) {
        el.removeEventListener('click', el.__toHandler);
        delete el.__toHandler;
    },
};
<|endoftext|>


<commit_before>/**
 * @requires javelin-install
 *           javelin-dom
 *           javelin-vector
 *           javelin-util
 * @provides javelin-typeahead
 * @javelin
 */

/**
 * A typeahead is a UI component similar to a text input, except that it
 * suggests some set of results (like friends' names, common searches, or
 * repository paths) as the user types them. Familiar examples of this UI
 * include Google Suggest, the Facebook search box, and OS X's Spotlight
 * feature.
 *
 * To build a @{JX.Typeahead}, you need to do four things:
 *
 *  1. Construct it, passing some DOM nodes for it to attach to. See the
 *     constructor for more information.
 *  2. Attach a datasource by calling setDatasource() with a valid datasource,
 *     often a @{JX.TypeaheadPreloadedSource}.
 *  3. Configure any special options that you want.
 *  4. Call start().
 *
 * If you do this correctly, a dropdown menu should appear under the input as
 * the user types, suggesting matching results.
 *
 * @task build        Building a Typeahead
 * @task datasource   Configuring a Datasource
 * @task config       Configuring Options
 * @task start        Activating a Typeahead
 * @task control      Controlling Typeaheads from Javascript
 * @task internal     Internal Methods
 * @group control
 */
JX.install('Typeahead', {
  /**
   * Construct a new Typeahead on some "hardpoint". At a minimum, the hardpoint
   * should be a ##
## with "position: relative;" wrapped around a text
   * ##<input>##. The typeahead's dropdown suggestions will be appended to the
   * hardpoint in the DOM. Basically, this is the bare minimum requirement:
   *
   *   LANG=HTML
   *

   *     <input type="text" />
   *

   *
   * Then get a reference to the ##
## and pass it as 'hardpoint', and pass
   * the ##<input>## as 'control'. This will enhance your boring old
   * ##<input />## with amazing typeahead powers.
   *
   * On the Facebook/Tools stack, ##<javelin:typeahead-template />## can build
   * this for you.
   *
   * @param Node  "Hardpoint", basically an anchorpoint in the document which
   *              the typeahead can append its suggestion menu to.
   * @param Node? Actual ##<input />## to use; if not provided, the typeahead
   *              will just look for a (solitary) input inside the hardpoint.
   * @task build
   */
  construct : function(hardpoint, control) {
    this._hardpoint = hardpoint;
    this._control = control || JX.DOM.find(hardpoint, 'input');

    this._root = JX.$N(
      'div',
      {className: 'jx-typeahead-results'});
    this._display = [];

    this._listener = JX.DOM.listen(
      this._control,
      ['focus', 'blur', 'keypress', 'keydown', 'input'],
      null,
      JX.bind(this, this.handleEvent));

    JX.DOM.listen(
      this._root,
      ['mouseover', 'mouseout'],
      null,
      JX.bind(this, this._onmouse));

    JX.DOM.listen(
      this._root,
      'mousedown',
      'tag:a',
      JX.bind(this, function(e) {
        if (!e.isRightButton()) {
          this._choose(e.getNode('tag:a'));
        }
      }));

  },

  events : ['choose', 'query', 'start', 'change', 'show'],

  properties : {

    /**
     * Boolean. If true (default), the user is permitted to submit the typeahead
     * with a custom or empty selection. This is a good behavior if the
     * typeahead is attached to something like a search input, where the user
     * might type a freeform query or select from a list of suggestions.
     * However, sometimes you require a specific input (e.g., choosing which
     * user owns something), in which case you can prevent null selections.
     *
     * @task config
     */
    allowNullSelection : true
  },

  members : {
    _root : null,
    _control : null,
    _hardpoint : null,
    _listener : null,
    _value : null,
    _stop : false,
    _focus : -1,
    _focused : false,
    _placeholderVisible : false,
    _placeholder : null,
    _display : null,
    _datasource : null,
    _waitingListener : null,
    _readyListener : null,

    /**
     * Activate your properly configured typeahead. It won't do anything until
     * you call this method!
     *
     * @task start
     * @return void
     */
    start : function() {
      this.invoke('start');
      if (__DEV__) {
        if (!this._datasource) {
          throw new Error(
            "JX.Typeahead.start(): " +
            "No datasource configured. Create a datasource and call " +
            "setDatasource().");
        }
      }
      this.updatePlaceholder();
    },

    /**
     * Configure a datasource, which is where the Typeahead gets suggestions
     * from. See @{JX.TypeaheadDatasource} for more information. You must
     * provide exactly one datasource.
     *
     * @task datasource
     * @param JX.TypeaheadDatasource The datasource which the typeahead will
     *                               draw from.
     */
    setDatasource : function(datasource) {
      if (this._datasource) {
        this._datasource.unbindFromTypeahead();
        this._waitingListener.remove();
        this._readyListener.remove();
      }
      this._waitingListener = datasource.listen(
        'waiting',
        JX.bind(this, this.waitForResults)
      );
      this._readyListener = datasource.listen(
        'resultsready',
        JX.bind(this, this.showResults)
      );
      datasource.bindToTypeahead(this);
      this._datasource = datasource;
    },

    getDatasource : function() {
      return this._datasource;
    },

    /**
     * Override the <input /> selected in the constructor with some other input.
     * This is primarily useful when building a control on top of the typeahead,
     * like @{JX.Tokenizer}.
     *
     * @task config
     * @param node An <input /> node to use as the primary control.
     */
    setInputNode : function(input) {
      this._control = input;
      return this;
    },

    /**
     * Hide the typeahead's dropdown suggestion menu.
     *
     * @task control
     * @return void
     */
    hide : function() {
      this._changeFocus(Number.NEGATIVE_INFINITY);
      this._display = [];
      this._moused = false;
      JX.DOM.hide(this._root);
    },

    /**
     * Show a given result set in the typeahead's dropdown suggestion menu.
     * Normally, you don't call this method directly. Usually it gets called
     * in response to events from the datasource you have configured.
     *
     * @task   control
     * @param  list List of #### tags to show as suggestions/results.
     * @return void
     */
    showResults : function(results) {
      var obj = {show: results};
      var e = this.invoke('show', obj);
      this._display = obj.show;

      if (this._display.length && !e.getPrevented()) {
        JX.DOM.setContent(this._root, this._display);
        this._changeFocus(Number.NEGATIVE_INFINITY);
        var d = JX.Vector.getDim(this._hardpoint);
        d.x = 0;
        d.setPos(this._root);
        if (this._root.parentNode !== this._hardpoint) {
          this._hardpoint.appendChild(this._root);
        }
        JX.DOM.show(this._root);
      } else {
        this.hide();
      }
    },

    refresh : function() {
      if (this._stop) {
        return;
      }

      this._value = this._control.value;
      this.invoke('change', this._value);
    },
    /**
     * Show a "waiting for results" UI in place of the typeahead's dropdown
     * suggestion menu. NOTE: currently there's no such UI, lolol.
     *
     * @task control
     * @return void
     */
    waitForResults : function() {
      // TODO: Build some sort of fancy spinner or "..." type UI here to
      // visually indicate that we're waiting on the server.
      // Wait on the datasource 'complete' event for hiding the spinner.
      this.hide();
    },

    /**
     * @task internal
     */
    _onmouse : function(event) {
      this._moused = (event.getType() == 'mouseover');
      this._drawFocus();
    },

    /**
     * @task internal
     */
    _changeFocus : function(d) {
      var n = Math.min(Math.max(-1, this._focus + d), this._display.length - 1);
      if (!this.getAllowNullSelection()) {
        n = Math.max(0, n);
      }
      if (this._focus >= 0 && this._focus < this._display.length) {
        JX.DOM.alterClass(this._display[this._focus], 'focused', false);
      }
      this._focus = n;
      this._drawFocus();
      return true;
    },

    /**
     * @task internal
     */
    _drawFocus : function() {
      var f = this._display[this._focus];
      if (f) {
        JX.DOM.alterClass(f, 'focused', !this._moused);
      }
    },

    /**
     * @task internal
     */
    _choose : function(target) {
      var result = this.invoke('choose', target);
      if (result.getPrevented()) {
        return;
      }

      this._control.value = target.name;
      this.hide();
    },

    /**
     * @task control
     */
    clear : function() {
      this._control.value = '';
      this._value = '';
      this.hide();
    },

    /**
     * @task control
     */
    disable : function() {
      this._control.blur();
      this._control.disabled = true;
      this._stop = true;
    },

    /**
     * @task control
     */
    submit : function() {
      if (this._focus >= 0 && this._display[this._focus]) {
        this._choose(this._display[this._focus]);
        return true;
      } else {
        result = this.invoke('query', this._control.value);
        if (result.getPrevented()) {
          return true;
        }
      }
      return false;
    },

    setValue : function(value) {
      this._control.value = value;
    },

    getValue : function() {
      return this._control.value;
    },

    /**
     * @task internal
     */
    _update : function(event) {

      if (event.getType() == 'focus') {
        this._focused = true;
        this.updatePlaceholder();
      }

      var k = event.getSpecialKey();
      if (k && event.getType() == 'keydown') {
        switch (k) {
          case 'up':
            if (this._display.length && this._changeFocus(-1)) {
              event.prevent();
            }
            break;
          case 'down':
            if (this._display.length && this._changeFocus(1)) {
              event.prevent();
            }
            break;
          case 'return':
            if (this.submit()) {
              event.prevent();
              return;
            }
            break;
          case 'esc':
            if (this._display.length && this.getAllowNullSelection()) {
              this.hide();
              event.prevent();
            }
            break;
          case 'tab':
            // If the user tabs out of the field, don't refresh.
            return;
        }
      }

      // We need to defer because the keystroke won't be present in the input's
      // value field yet.
      setTimeout(JX.bind(this, function() {
        if (this._value == this._control.value) {
          // The typeahead value hasn't changed.
          return;
        }
        this.refresh();
      }), 0);
    },

    /**
     * This method is pretty much internal but @{JX.Tokenizer} needs access to
     * it for delegation. You might also need to delegate events here if you
     * build some kind of meta-control.
     *
     * Reacts to user events in accordance to configuration.
     *
     * @task internal
     * @param JX.Event User event, like a click or keypress.
     * @return void
     */
    handleEvent : function(e) {
      if (this._stop || e.getPrevented()) {
        return;
      }
      var type = e.getType();
      if (type == 'blur') {
        this._focused = false;
        this.updatePlaceholder();
        this.hide();
      } else {
        this._update(e);
      }
    },

    removeListener : function() {
      if (this._listener) {
        this._listener.remove();
      }
    },

    /**
     * Set a string to display in the control when it is not focused, like
     * "Type a user's name...". This string hints to the user how to use the
     * control.
     *
     * When the string is displayed, the input will have class
     * "jx-typeahead-placeholder".
     *
     * @param string Placeholder string, or null for no placeholder.
     * @return this
     *
     * @task config
     */
    setPlaceholder : function(string) {
      this._placeholder = string;
      this.updatePlaceholder();
      return this;
    },

    /**
     * Update the control to either show or hide the placeholder text as
     * necessary.
     *
     * @return void
     * @task internal
     */
    updatePlaceholder : function() {

      if (this._placeholderVisible) {
        // If the placeholder is visible, we want to hide if the control has
        // been focused or the placeholder has been removed.
        if (this._focused || !this._placeholder) {
          this._placeholderVisible = false;
          this._control.value = '';
        }
      } else if (!this._focused) {
        // If the placeholder is not visible, we want to show it if the control
        // has benen blurred.
        if (this._placeholder && !this._control.value) {
          this._placeholderVisible = true;
        }
      }

      if (this._placeholderVisible) {
        // We need to resist the Tokenizer wiping the input on blur.
        this._control.value = this._placeholder;
      }

      JX.DOM.alterClass(
        this._control,
        'jx-typeahead-placeholder',
        this._placeholderVisible);
    }
  }
});
<commit_msg>Allow non-result node to be displayed with the result nodes.<commit_after>/**
 * @requires javelin-install
 *           javelin-dom
 *           javelin-vector
 *           javelin-util
 * @provides javelin-typeahead
 * @javelin
 */

/**
 * A typeahead is a UI component similar to a text input, except that it
 * suggests some set of results (like friends' names, common searches, or
 * repository paths) as the user types them. Familiar examples of this UI
 * include Google Suggest, the Facebook search box, and OS X's Spotlight
 * feature.
 *
 * To build a @{JX.Typeahead}, you need to do four things:
 *
 *  1. Construct it, passing some DOM nodes for it to attach to. See the
 *     constructor for more information.
 *  2. Attach a datasource by calling setDatasource() with a valid datasource,
 *     often a @{JX.TypeaheadPreloadedSource}.
 *  3. Configure any special options that you want.
 *  4. Call start().
 *
 * If you do this correctly, a dropdown menu should appear under the input as
 * the user types, suggesting matching results.
 *
 * @task build        Building a Typeahead
 * @task datasource   Configuring a Datasource
 * @task config       Configuring Options
 * @task start        Activating a Typeahead
 * @task control      Controlling Typeaheads from Javascript
 * @task internal     Internal Methods
 * @group control
 */
JX.install('Typeahead', {
  /**
   * Construct a new Typeahead on some "hardpoint". At a minimum, the hardpoint
   * should be a ##
## with "position: relative;" wrapped around a text
   * ##<input>##. The typeahead's dropdown suggestions will be appended to the
   * hardpoint in the DOM. Basically, this is the bare minimum requirement:
   *
   *   LANG=HTML
   *

   *     <input type="text" />
   *

   *
   * Then get a reference to the ##
## and pass it as 'hardpoint', and pass
   * the ##<input>## as 'control'. This will enhance your boring old
   * ##<input />## with amazing typeahead powers.
   *
   * On the Facebook/Tools stack, ##<javelin:typeahead-template />## can build
   * this for you.
   *
   * @param Node  "Hardpoint", basically an anchorpoint in the document which
   *              the typeahead can append its suggestion menu to.
   * @param Node? Actual ##<input />## to use; if not provided, the typeahead
   *              will just look for a (solitary) input inside the hardpoint.
   * @task build
   */
  construct : function(hardpoint, control) {
    this._hardpoint = hardpoint;
    this._control = control || JX.DOM.find(hardpoint, 'input');

    this._root = JX.$N(
      'div',
      {className: 'jx-typeahead-results'});
    this._display = [];

    this._listener = JX.DOM.listen(
      this._control,
      ['focus', 'blur', 'keypress', 'keydown', 'input'],
      null,
      JX.bind(this, this.handleEvent));

    JX.DOM.listen(
      this._root,
      ['mouseover', 'mouseout'],
      null,
      JX.bind(this, this._onmouse));

    JX.DOM.listen(
      this._root,
      'mousedown',
      'tag:a',
      JX.bind(this, function(e) {
        if (!e.isRightButton()) {
          this._choose(e.getNode('tag:a'));
        }
      }));

  },

  events : ['choose', 'query', 'start', 'change', 'show'],

  properties : {

    /**
     * Boolean. If true (default), the user is permitted to submit the typeahead
     * with a custom or empty selection. This is a good behavior if the
     * typeahead is attached to something like a search input, where the user
     * might type a freeform query or select from a list of suggestions.
     * However, sometimes you require a specific input (e.g., choosing which
     * user owns something), in which case you can prevent null selections.
     *
     * @task config
     */
    allowNullSelection : true
  },

  members : {
    _root : null,
    _control : null,
    _hardpoint : null,
    _listener : null,
    _value : null,
    _stop : false,
    _focus : -1,
    _focused : false,
    _placeholderVisible : false,
    _placeholder : null,
    _display : null,
    _datasource : null,
    _waitingListener : null,
    _readyListener : null,

    /**
     * Activate your properly configured typeahead. It won't do anything until
     * you call this method!
     *
     * @task start
     * @return void
     */
    start : function() {
      this.invoke('start');
      if (__DEV__) {
        if (!this._datasource) {
          throw new Error(
            "JX.Typeahead.start(): " +
            "No datasource configured. Create a datasource and call " +
            "setDatasource().");
        }
      }
      this.updatePlaceholder();
    },

    /**
     * Configure a datasource, which is where the Typeahead gets suggestions
     * from. See @{JX.TypeaheadDatasource} for more information. You must
     * provide exactly one datasource.
     *
     * @task datasource
     * @param JX.TypeaheadDatasource The datasource which the typeahead will
     *                               draw from.
     */
    setDatasource : function(datasource) {
      if (this._datasource) {
        this._datasource.unbindFromTypeahead();
        this._waitingListener.remove();
        this._readyListener.remove();
      }
      this._waitingListener = datasource.listen(
        'waiting',
        JX.bind(this, this.waitForResults)
      );
      this._readyListener = datasource.listen(
        'resultsready',
        JX.bind(this, this.showResults)
      );
      datasource.bindToTypeahead(this);
      this._datasource = datasource;
    },

    getDatasource : function() {
      return this._datasource;
    },

    /**
     * Override the <input /> selected in the constructor with some other input.
     * This is primarily useful when building a control on top of the typeahead,
     * like @{JX.Tokenizer}.
     *
     * @task config
     * @param node An <input /> node to use as the primary control.
     */
    setInputNode : function(input) {
      this._control = input;
      return this;
    },

    /**
     * Hide the typeahead's dropdown suggestion menu.
     *
     * @task control
     * @return void
     */
    hide : function() {
      this._changeFocus(Number.NEGATIVE_INFINITY);
      this._display = [];
      this._moused = false;
      JX.DOM.hide(this._root);
    },

    /**
     * Show a given result set in the typeahead's dropdown suggestion menu.
     * Normally, you don't call this method directly. Usually it gets called
     * in response to events from the datasource you have configured.
     *
     * @task   control
     * @param  list List of #### tags to show as suggestions/results.
     * @return void
     */
    showResults : function(results) {
      var obj = {show: results};
      var e = this.invoke('show', obj);

      // Note that the results list may have been update by the "show" event
      // listener. Non-result node (e.g. divider or label) may have been
      // inserted.
      JX.DOM.setContent(this._root, results);
      this._display = JX.DOM.scry(this._root, 'a', 'typeahead-result');

      if (this._display.length && !e.getPrevented()) {
        this._changeFocus(Number.NEGATIVE_INFINITY);
        var d = JX.Vector.getDim(this._hardpoint);
        d.x = 0;
        d.setPos(this._root);
        if (this._root.parentNode !== this._hardpoint) {
          this._hardpoint.appendChild(this._root);
        }
        JX.DOM.show(this._root);
      } else {
        this.hide();
        JX.DOM.setContent(this._root, null);
      }
    },

    refresh : function() {
      if (this._stop) {
        return;
      }

      this._value = this._control.value;
      this.invoke('change', this._value);
    },
    /**
     * Show a "waiting for results" UI in place of the typeahead's dropdown
     * suggestion menu. NOTE: currently there's no such UI, lolol.
     *
     * @task control
     * @return void
     */
    waitForResults : function() {
      // TODO: Build some sort of fancy spinner or "..." type UI here to
      // visually indicate that we're waiting on the server.
      // Wait on the datasource 'complete' event for hiding the spinner.
      this.hide();
    },

    /**
     * @task internal
     */
    _onmouse : function(event) {
      this._moused = (event.getType() == 'mouseover');
      this._drawFocus();
    },

    /**
     * @task internal
     */
    _changeFocus : function(d) {
      var n = Math.min(Math.max(-1, this._focus + d), this._display.length - 1);
      if (!this.getAllowNullSelection()) {
        n = Math.max(0, n);
      }
      if (this._focus >= 0 && this._focus < this._display.length) {
        JX.DOM.alterClass(this._display[this._focus], 'focused', false);
      }
      this._focus = n;
      this._drawFocus();
      return true;
    },

    /**
     * @task internal
     */
    _drawFocus : function() {
      var f = this._display[this._focus];
      if (f) {
        JX.DOM.alterClass(f, 'focused', !this._moused);
      }
    },

    /**
     * @task internal
     */
    _choose : function(target) {
      var result = this.invoke('choose', target);
      if (result.getPrevented()) {
        return;
      }

      this._control.value = target.name;
      this.hide();
    },

    /**
     * @task control
     */
    clear : function() {
      this._control.value = '';
      this._value = '';
      this.hide();
    },

    /**
     * @task control
     */
    disable : function() {
      this._control.blur();
      this._control.disabled = true;
      this._stop = true;
    },

    /**
     * @task control
     */
    submit : function() {
      if (this._focus >= 0 && this._display[this._focus]) {
        this._choose(this._display[this._focus]);
        return true;
      } else {
        result = this.invoke('query', this._control.value);
        if (result.getPrevented()) {
          return true;
        }
      }
      return false;
    },

    setValue : function(value) {
      this._control.value = value;
    },

    getValue : function() {
      return this._control.value;
    },

    /**
     * @task internal
     */
    _update : function(event) {

      if (event.getType() == 'focus') {
        this._focused = true;
        this.updatePlaceholder();
      }

      var k = event.getSpecialKey();
      if (k && event.getType() == 'keydown') {
        switch (k) {
          case 'up':
            if (this._display.length && this._changeFocus(-1)) {
              event.prevent();
            }
            break;
          case 'down':
            if (this._display.length && this._changeFocus(1)) {
              event.prevent();
            }
            break;
          case 'return':
            if (this.submit()) {
              event.prevent();
              return;
            }
            break;
          case 'esc':
            if (this._display.length && this.getAllowNullSelection()) {
              this.hide();
              event.prevent();
            }
            break;
          case 'tab':
            // If the user tabs out of the field, don't refresh.
            return;
        }
      }

      // We need to defer because the keystroke won't be present in the input's
      // value field yet.
      setTimeout(JX.bind(this, function() {
        if (this._value == this._control.value) {
          // The typeahead value hasn't changed.
          return;
        }
        this.refresh();
      }), 0);
    },

    /**
     * This method is pretty much internal but @{JX.Tokenizer} needs access to
     * it for delegation. You might also need to delegate events here if you
     * build some kind of meta-control.
     *
     * Reacts to user events in accordance to configuration.
     *
     * @task internal
     * @param JX.Event User event, like a click or keypress.
     * @return void
     */
    handleEvent : function(e) {
      if (this._stop || e.getPrevented()) {
        return;
      }
      var type = e.getType();
      if (type == 'blur') {
        this._focused = false;
        this.updatePlaceholder();
        this.hide();
      } else {
        this._update(e);
      }
    },

    removeListener : function() {
      if (this._listener) {
        this._listener.remove();
      }
    },

    /**
     * Set a string to display in the control when it is not focused, like
     * "Type a user's name...". This string hints to the user how to use the
     * control.
     *
     * When the string is displayed, the input will have class
     * "jx-typeahead-placeholder".
     *
     * @param string Placeholder string, or null for no placeholder.
     * @return this
     *
     * @task config
     */
    setPlaceholder : function(string) {
      this._placeholder = string;
      this.updatePlaceholder();
      return this;
    },

    /**
     * Update the control to either show or hide the placeholder text as
     * necessary.
     *
     * @return void
     * @task internal
     */
    updatePlaceholder : function() {

      if (this._placeholderVisible) {
        // If the placeholder is visible, we want to hide if the control has
        // been focused or the placeholder has been removed.
        if (this._focused || !this._placeholder) {
          this._placeholderVisible = false;
          this._control.value = '';
        }
      } else if (!this._focused) {
        // If the placeholder is not visible, we want to show it if the control
        // has benen blurred.
        if (this._placeholder && !this._control.value) {
          this._placeholderVisible = true;
        }
      }

      if (this._placeholderVisible) {
        // We need to resist the Tokenizer wiping the input on blur.
        this._control.value = this._placeholder;
      }

      JX.DOM.alterClass(
        this._control,
        'jx-typeahead-placeholder',
        this._placeholderVisible);
    }
  }
});
<|endoftext|>


<commit_before>import _ from 'lodash'
import postcss from 'postcss'
import escapeClassName from '../util/escapeClassName'
import prefixSelector from '../util/prefixSelector'

function buildClassTable(css) {
  const classTable = {}

  css.walkRules(rule => {
    if (!_.has(classTable, rule.selector)) {
      classTable[rule.selector] = []
    }
    classTable[rule.selector].push(rule)
  })

  return classTable
}

function buildShadowTable(generatedUtilities) {
  const utilities = postcss.root()

  postcss.root({ nodes: generatedUtilities }).walkAtRules('variants', atRule => {
    utilities.append(atRule.clone().nodes)
  })

  return buildClassTable(utilities)
}

function normalizeClassName(className) {
  return `.${escapeClassName(_.trimStart(className, '.'))}`
}

function findClass(classToApply, classTable, onError) {
  const matches = _.get(classTable, classToApply, [])

  if (_.isEmpty(matches)) {
    return []
  }

  if (matches.length > 1) {
    // prettier-ignore
    throw onError(`\`@apply\` cannot be used with ${classToApply} because ${classToApply} is included in multiple rulesets.`)
  }

  const [match] = matches

  if (match.parent.type !== 'root') {
    // prettier-ignore
    throw onError(`\`@apply\` cannot be used with ${classToApply} because ${classToApply} is nested inside of an at-rule (@${match.parent.name}).`)
  }

  return match.clone().nodes
}

export default function(config, generatedUtilities) {
  return function(css) {
    const classLookup = buildClassTable(css)
    const shadowLookup = buildShadowTable(generatedUtilities)

    css.walkRules(rule => {
      rule.walkAtRules('apply', atRule => {
        const classesAndProperties = postcss.list.space(atRule.params)

        /*
         * Don't wreck CSSNext-style @apply rules:
         * http://cssnext.io/features/#custom-properties-set-apply
         *
         * These are deprecated in CSSNext but still playing it safe for now.
         * We might consider renaming this at-rule.
         */
        const [customProperties, classes] = _.partition(classesAndProperties, classOrProperty => {
          return _.startsWith(classOrProperty, '--')
        })

        const decls = _(classes)
          .reject(cssClass => cssClass === '!important')
          .flatMap(cssClass => {
            const classToApply = normalizeClassName(cssClass)
            const onError = message => {
              return atRule.error(message)
            }

            return _.reduce(
              [
                () => findClass(classToApply, classLookup, onError),
                () => findClass(classToApply, shadowLookup, onError),
                () =>
                  findClass(
                    prefixSelector(config.options.prefix, classToApply),
                    shadowLookup,
                    onError
                  ),
                () => {
                  // prettier-ignore
                  throw onError(`\`@apply\` cannot be used with \`${classToApply}\` because \`${classToApply}\` either cannot be found, or it's actual definition includes a pseudo-selector like :hover, :active, etc. If you're sure that \`${classToApply}\` exists, make sure that any \`@import\` statements are being properly processed *before* Tailwind CSS sees your CSS, as \`@apply\` can only be used for classes in the same CSS tree.`)
                },
              ],
              (classDecls, candidate) => (!_.isEmpty(classDecls) ? classDecls : candidate()),
              []
            )
          })
          .value()

        _.tap(_.last(classesAndProperties) === '!important', important => {
          decls.forEach(decl => (decl.important = important))
        })

        atRule.before(decls)

        atRule.params = customProperties.join(' ')

        if (_.isEmpty(customProperties)) {
          atRule.remove()
        }
      })
    })
  }
}
<commit_msg>Fix apostrophe in error<commit_after>import _ from 'lodash'
import postcss from 'postcss'
import escapeClassName from '../util/escapeClassName'
import prefixSelector from '../util/prefixSelector'

function buildClassTable(css) {
  const classTable = {}

  css.walkRules(rule => {
    if (!_.has(classTable, rule.selector)) {
      classTable[rule.selector] = []
    }
    classTable[rule.selector].push(rule)
  })

  return classTable
}

function buildShadowTable(generatedUtilities) {
  const utilities = postcss.root()

  postcss.root({ nodes: generatedUtilities }).walkAtRules('variants', atRule => {
    utilities.append(atRule.clone().nodes)
  })

  return buildClassTable(utilities)
}

function normalizeClassName(className) {
  return `.${escapeClassName(_.trimStart(className, '.'))}`
}

function findClass(classToApply, classTable, onError) {
  const matches = _.get(classTable, classToApply, [])

  if (_.isEmpty(matches)) {
    return []
  }

  if (matches.length > 1) {
    // prettier-ignore
    throw onError(`\`@apply\` cannot be used with ${classToApply} because ${classToApply} is included in multiple rulesets.`)
  }

  const [match] = matches

  if (match.parent.type !== 'root') {
    // prettier-ignore
    throw onError(`\`@apply\` cannot be used with ${classToApply} because ${classToApply} is nested inside of an at-rule (@${match.parent.name}).`)
  }

  return match.clone().nodes
}

export default function(config, generatedUtilities) {
  return function(css) {
    const classLookup = buildClassTable(css)
    const shadowLookup = buildShadowTable(generatedUtilities)

    css.walkRules(rule => {
      rule.walkAtRules('apply', atRule => {
        const classesAndProperties = postcss.list.space(atRule.params)

        /*
         * Don't wreck CSSNext-style @apply rules:
         * http://cssnext.io/features/#custom-properties-set-apply
         *
         * These are deprecated in CSSNext but still playing it safe for now.
         * We might consider renaming this at-rule.
         */
        const [customProperties, classes] = _.partition(classesAndProperties, classOrProperty => {
          return _.startsWith(classOrProperty, '--')
        })

        const decls = _(classes)
          .reject(cssClass => cssClass === '!important')
          .flatMap(cssClass => {
            const classToApply = normalizeClassName(cssClass)
            const onError = message => {
              return atRule.error(message)
            }

            return _.reduce(
              [
                () => findClass(classToApply, classLookup, onError),
                () => findClass(classToApply, shadowLookup, onError),
                () =>
                  findClass(
                    prefixSelector(config.options.prefix, classToApply),
                    shadowLookup,
                    onError
                  ),
                () => {
                  // prettier-ignore
                  throw onError(`\`@apply\` cannot be used with \`${classToApply}\` because \`${classToApply}\` either cannot be found, or its actual definition includes a pseudo-selector like :hover, :active, etc. If you're sure that \`${classToApply}\` exists, make sure that any \`@import\` statements are being properly processed *before* Tailwind CSS sees your CSS, as \`@apply\` can only be used for classes in the same CSS tree.`)
                },
              ],
              (classDecls, candidate) => (!_.isEmpty(classDecls) ? classDecls : candidate()),
              []
            )
          })
          .value()

        _.tap(_.last(classesAndProperties) === '!important', important => {
          decls.forEach(decl => (decl.important = important))
        })

        atRule.before(decls)

        atRule.params = customProperties.join(' ')

        if (_.isEmpty(customProperties)) {
          atRule.remove()
        }
      })
    })
  }
}
<|endoftext|>


<commit_before>var Class = require('../../utils/Class');
var File = require('../File');
var GetFastValue = require('../../utils/object/GetFastValue');
var GetURL = require('../GetURL');

//  Phaser.Loader.FileTypes.HTML5AudioFile

var HTML5AudioFile = new Class({

    Extends: File,

    initialize:

        function HTML5AudioFile (key, url, path, config, touchLocked)
        {
            this.touchLocked = touchLocked;

            var fileConfig = {
                type: 'audio',
                extension: GetFastValue(url, 'type', ''),
                key: key,
                url: GetFastValue(url, 'uri', url),
                path: path,
                config: config
            };

            File.call(this, fileConfig);
        },

    onLoad: function ()
    {
        this.callback(this, true);
    },

    onError: function (event)
    {
        for (var i = 0; i < this.data.length; i++)
        {
            var audio = this.data[i];
            audio.oncanplaythrough = null;
            audio.onerror = null;
        }

        this.callback(this, false);
    },

    onProgress: function (event)
    {
        var audio = event.target;
        audio.oncanplaythrough = null;
        audio.onerror = null;

        if(++this.filesLoaded === this.filesTotal)
        {
            this.onLoad();
        }

        this.percentComplete = Math.min((this.filesLoaded / this.filesTotal), 1);
    },

    //  Called by the Loader, starts the actual file downloading
    load: function (callback, baseURL)
    {
        this.callback = callback;

        this.data = [];

        var instances = (this.config && this.config.instances) || 1;

        this.filesTotal = instances;
        this.filesLoaded = 0;
        this.percentComplete = 0;

        var touchLocked = 'ontouchstart' in window;

        for(var i = 0; i < instances; i++)
        {
            var audio = new Audio();
            audio.dataset.name = this.key + ('0' + i).slice(-2); // Useful for debugging
            audio.dataset.used = 'false';

            if (!touchLocked)
            {
                audio.preload = 'auto';
                audio.oncanplaythrough = this.onProgress.bind(this);
                audio.onerror = this.onError.bind(this);
            }

            this.data.push(audio);
        }

        for (i = 0; i < this.data.length; i++)
        {
            audio = this.data[i];
            audio.src = GetURL(this, baseURL || '');

            if (!touchLocked)
            {
                audio.load();
            }
        }

        if (touchLocked)
        {
            setTimeout(function ()
            {
                this.filesLoaded = this.filesTotal;
                this.percentComplete = 1;
                this.onLoad();

            }.bind(this));
        }
    }

});

module.exports = HTML5AudioFile;
<commit_msg>Using object property instead of local var<commit_after>var Class = require('../../utils/Class');
var File = require('../File');
var GetFastValue = require('../../utils/object/GetFastValue');
var GetURL = require('../GetURL');

//  Phaser.Loader.FileTypes.HTML5AudioFile

var HTML5AudioFile = new Class({

    Extends: File,

    initialize:

        function HTML5AudioFile (key, url, path, config, touchLocked)
        {
            this.touchLocked = touchLocked;

            var fileConfig = {
                type: 'audio',
                extension: GetFastValue(url, 'type', ''),
                key: key,
                url: GetFastValue(url, 'uri', url),
                path: path,
                config: config
            };

            File.call(this, fileConfig);
        },

    onLoad: function ()
    {
        this.callback(this, true);
    },

    onError: function (event)
    {
        for (var i = 0; i < this.data.length; i++)
        {
            var audio = this.data[i];
            audio.oncanplaythrough = null;
            audio.onerror = null;
        }

        this.callback(this, false);
    },

    onProgress: function (event)
    {
        var audio = event.target;
        audio.oncanplaythrough = null;
        audio.onerror = null;

        if(++this.filesLoaded === this.filesTotal)
        {
            this.onLoad();
        }

        this.percentComplete = Math.min((this.filesLoaded / this.filesTotal), 1);
    },

    //  Called by the Loader, starts the actual file downloading
    load: function (callback, baseURL)
    {
        this.callback = callback;

        this.data = [];

        var instances = (this.config && this.config.instances) || 1;

        this.filesTotal = instances;
        this.filesLoaded = 0;
        this.percentComplete = 0;

        for(var i = 0; i < instances; i++)
        {
            var audio = new Audio();
            audio.dataset.name = this.key + ('0' + i).slice(-2); // Useful for debugging
            audio.dataset.used = 'false';

            if (!this.touchLocked)
            {
                audio.preload = 'auto';
                audio.oncanplaythrough = this.onProgress.bind(this);
                audio.onerror = this.onError.bind(this);
            }

            this.data.push(audio);
        }

        for (i = 0; i < this.data.length; i++)
        {
            audio = this.data[i];
            audio.src = GetURL(this, baseURL || '');

            if (!this.touchLocked)
            {
                audio.load();
            }
        }

        if (this.touchLocked)
        {
            setTimeout(function ()
            {
                this.filesLoaded = this.filesTotal;
                this.percentComplete = 1;
                this.onLoad();

            }.bind(this));
        }
    }

});

module.exports = HTML5AudioFile;
<|endoftext|>


<commit_before>/*
* This file is part of the easy framework.
*
* (c) Julien Sergent <sergent.julien@icloud.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

module.exports.entityManager = require( 'easy/test/entitymanager.mock' )
module.exports.request = require( 'easy/test/request.mock' )
module.exports.response = require( 'easy/test/response.mock' )
module.exports.fakeAsync = require( 'easy/test/fakeAsync.mock' )
module.exports.directory = require( 'easy/test/directory.mock' )
module.exports.logger = require( 'easy/test/logger.mock' )
module.exports.logWriter = require( 'easy/test/logwriter.mock' )
module.exports.logFileManager = require( 'easy/test/logfilemanager.mock' )
module.exports.tokenManager = require( 'easy/test/tokenmanager.mock' )
module.exports.authorization = require( 'easy/test/authorization.mock' )
<commit_msg>fix(paths): Rename test directory path into mocks<commit_after>/*
* This file is part of the easy framework.
*
* (c) Julien Sergent <sergent.julien@icloud.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

module.exports.entityManager = require( 'easy/mocks/entitymanager.mock' )
module.exports.request = require( 'easy/mocks/request.mock' )
module.exports.response = require( 'easy/mocks/response.mock' )
module.exports.fakeAsync = require( 'easy/mocks/fakeAsync.mock' )
module.exports.directory = require( 'easy/mocks/directory.mock' )
module.exports.logger = require( 'easy/mocks/logger.mock' )
module.exports.logWriter = require( 'easy/mocks/logwriter.mock' )
module.exports.logFileManager = require( 'easy/mocks/logfilemanager.mock' )
module.exports.tokenManager = require( 'easy/mocks/tokenmanager.mock' )
module.exports.authorization = require( 'easy/mocks/authorization.mock' )
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

var fs      = require('fs');
var path    = require('path');
var crypto  = require('crypto');
var _       = require('lodash');
var yaml    = require('js-yaml');
var DOMParser      = require('xmldom').DOMParser;
var ArgumentParser = require('argparse').ArgumentParser;

var svg_template =
    '<svg height="${height}px" width="${width}px"' +
    ' xmlns="http://www.w3.org/2000/svg">' + "\n" +
    '  <path d="${path}" transform="${transform}" />' + "\n" +
    '</svg>';

var parser = new ArgumentParser({
  version: require('./package.json').version,
  addHelp: true,
  description: 'Dump SVG font to separate glyphs'
});
parser.addArgument(
  [ '-c', '--config' ],
  {
    help: 'Font config file'
  }
);
parser.addArgument(
  [ '-i', '--src_font' ],
  {
    help: 'Source font path',
    required: true
  }
);
parser.addArgument(
  [ '-o', '--glyphs_dir' ],
  {
    help: 'Glyphs output folder',
    required: true
  }
);
parser.addArgument(
  [ '-d', '--diff_config' ],
  {
    help: 'Difference config output file'
  }
);
parser.addArgument(
  [ '-f', '--force' ],
  {
    help: 'Force override glyphs from config',
    action: 'storeTrue'
  }
);

var args = parser.parseArgs();

////////////////////////////////////////////////////////////////////////////////

function font_dump(data, callback) {

  var result = []
    , fontHorizAdvX
    , ascent
    , descent

    , glyphSize = 1000

    , doc;

  doc = (new DOMParser()).parseFromString(data, "application/xml");

  var font = doc.getElementsByTagName('font')[0];
  var fontFace = font.getElementsByTagName('font-face')[0];

  fontHorizAdvX = +font.getAttribute('horiz-adv-x');
  ascent        = +fontFace.getAttribute('ascent');
  descent       = -fontFace.getAttribute('descent');

  _.each(font.getElementsByTagName('glyph'), function(glyph) {
    var path = glyph.getAttribute('d');

    var unicode = glyph.getAttribute('unicode');

    if (!unicode) { return; }

    unicode = unicode.length === 1 ? unicode.charCodeAt(0) : unicode;

    var name = glyph.getAttribute('glyph-name') || ('glyph' + unicode);

    //
    // Rescale & Transform from scg fomt to svg image coordinates
    // !!! Transforms go in back order !!!
    //

    var width = glyph.getAttribute('horiz-adv-x') || fontHorizAdvX;
    var height = ascent + descent;
    var scale = glyphSize / height;

    // vertical mirror
    var transform = 'translate(0 ' + (glyphSize / 2) + ') scale(1 -1) translate(0 ' + (-glyphSize / 2) + ')';
    // scale
    transform += ' scale(' + scale + ')';
    // descent shift
    transform += ' translate(0 ' + descent + ')';

    // recalculate width & height
    width = width * scale;
    height = height * scale;

    result.push({
      path: path,
      transform: transform,
      unicode: unicode,
      name: name,
      width: width,
      height: height
    });
  });

  return result;
}

////////////////////////////////////////////////////////////////////////////////

var data, config, diff = [];

try {
  data = fs.readFileSync(args.src_font, 'utf-8');
} catch (e) {
  console.error('Can\'t read font file ' + args.src_font);
  process.exit(1);
}

try {
  config = yaml.load(fs.readFileSync(args.config, 'utf-8'));
} catch (e) {
  console.error('Can\'t read config file ' + args.config);
  process.exit(1);
}

var glyphs = font_dump(data);

glyphs.forEach(function(glyph) {

  var exists,
      glyph_out = {};

  // if got config from existing font, then write only missed files
  if (config) {
    exists = _.find(config.glyphs, function(element) {
      //console.log('---' + element.from + '---' + glyph.unicode)
      return (element.from || element.code) === glyph.unicode;
    });

    if (exists && !args.force) {
      console.log((glyph.unicode.toString(16)) + ' exists, skipping');
      return;
    }
  }

  glyph.svg = svg_template
    .replace('${path}', glyph.path)
    .replace('${transform}', glyph.transform)
    .replace('${width}', glyph.width)
    .replace('${height}', glyph.height);

  if (exists) {
    // glyph exists in config, but we forced dump

    fs.writeFileSync(path.join(args.glyphs_dir, (exists.file || exists.css) + '.svg'), glyph.svg);
    console.log((glyph.unicode.toString(16)) + ' - Found, but override forced');
    return;
  }

  // Completely new glyph

  glyph_out = {
    css: glyph.name,
    code: '0x' + glyph.unicode.toString(16),
    uid: crypto.randomBytes(16).toString('hex'),
    search: []
  };

  console.log((glyph.unicode.toString(16)) + ' - NEW glyph, writing...');

  fs.writeFile(path.join(args.glyphs_dir, glyph.name + '.svg'), glyph.svg);

  diff.push(glyph_out);

});

// Create config template for new glyphs, if option set
if (args.diff_config) {

  if (!diff.length) {
    console.log("No new glyphs, skip writing diff");
    return;
  }

  fs.writeFileSync(args.diff_config, yaml.dump({glyphs: diff}));
}
<commit_msg>updated svg template<commit_after>#!/usr/bin/env node

'use strict';

var fs      = require('fs');
var path    = require('path');
var crypto  = require('crypto');
var _       = require('lodash');
var yaml    = require('js-yaml');
var DOMParser      = require('xmldom').DOMParser;
var ArgumentParser = require('argparse').ArgumentParser;

var svg_template = _.template(
    '<svg height="<%= height %>" width="<%= width %>" xmlns="http://www.w3.org/2000/svg">' +
    '<path d="<%= path %>" transform="<%= transform %>"/>' +
    '</svg>'
  );

var parser = new ArgumentParser({
  version: require('./package.json').version,
  addHelp: true,
  description: 'Dump SVG font to separate glyphs'
});
parser.addArgument(
  [ '-c', '--config' ],
  {
    help: 'Font config file'
  }
);
parser.addArgument(
  [ '-i', '--src_font' ],
  {
    help: 'Source font path',
    required: true
  }
);
parser.addArgument(
  [ '-o', '--glyphs_dir' ],
  {
    help: 'Glyphs output folder',
    required: true
  }
);
parser.addArgument(
  [ '-d', '--diff_config' ],
  {
    help: 'Difference config output file'
  }
);
parser.addArgument(
  [ '-f', '--force' ],
  {
    help: 'Force override glyphs from config',
    action: 'storeTrue'
  }
);

var args = parser.parseArgs();

////////////////////////////////////////////////////////////////////////////////

function font_dump(data, callback) {

  var result = []
    , fontHorizAdvX
    , ascent
    , descent

    , glyphSize = 1000

    , doc;

  doc = (new DOMParser()).parseFromString(data, "application/xml");

  var font = doc.getElementsByTagName('font')[0];
  var fontFace = font.getElementsByTagName('font-face')[0];

  fontHorizAdvX = +font.getAttribute('horiz-adv-x');
  ascent        = +fontFace.getAttribute('ascent');
  descent       = -fontFace.getAttribute('descent');

  _.each(font.getElementsByTagName('glyph'), function(glyph) {
    var path = glyph.getAttribute('d');

    var unicode = glyph.getAttribute('unicode');

    if (!unicode) { return; }

    unicode = unicode.length === 1 ? unicode.charCodeAt(0) : unicode;

    var name = glyph.getAttribute('glyph-name') || ('glyph' + unicode);

    //
    // Rescale & Transform from scg fomt to svg image coordinates
    // !!! Transforms go in back order !!!
    //

    var width = glyph.getAttribute('horiz-adv-x') || fontHorizAdvX;
    var height = ascent + descent;
    var scale = glyphSize / height;

    // vertical mirror
    var transform = 'translate(0 ' + (glyphSize / 2) + ') scale(1 -1) translate(0 ' + (-glyphSize / 2) + ')';
    // scale
    transform += ' scale(' + scale + ')';
    // descent shift
    transform += ' translate(0 ' + descent + ')';

    // recalculate width & height
    width = width * scale;
    height = height * scale;

    result.push({
      path: path,
      transform: transform,
      unicode: unicode,
      name: name,
      width: width,
      height: height
    });
  });

  return result;
}

////////////////////////////////////////////////////////////////////////////////

var data, config, diff = [];

try {
  data = fs.readFileSync(args.src_font, 'utf-8');
} catch (e) {
  console.error('Can\'t read font file ' + args.src_font);
  process.exit(1);
}

try {
  config = yaml.load(fs.readFileSync(args.config, 'utf-8'));
} catch (e) {
  console.error('Can\'t read config file ' + args.config);
  process.exit(1);
}

var glyphs = font_dump(data);

glyphs.forEach(function(glyph) {

  var exists,
      glyph_out = {};

  // if got config from existing font, then write only missed files
  if (config) {
    exists = _.find(config.glyphs, function(element) {
      //console.log('---' + element.from + '---' + glyph.unicode)
      return (element.from || element.code) === glyph.unicode;
    });

    if (exists && !args.force) {
      console.log((glyph.unicode.toString(16)) + ' exists, skipping');
      return;
    }
  }

  glyph.svg = svg_template({
    path :glyph.path,
    transform : glyph.transform,
    width : glyph.width,
    height : glyph.height
  });

  if (exists) {
    // glyph exists in config, but we forced dump

    fs.writeFileSync(path.join(args.glyphs_dir, (exists.file || exists.css) + '.svg'), glyph.svg);
    console.log((glyph.unicode.toString(16)) + ' - Found, but override forced');
    return;
  }

  // Completely new glyph

  glyph_out = {
    css: glyph.name,
    code: '0x' + glyph.unicode.toString(16),
    uid: crypto.randomBytes(16).toString('hex'),
    search: []
  };

  console.log((glyph.unicode.toString(16)) + ' - NEW glyph, writing...');

  fs.writeFile(path.join(args.glyphs_dir, glyph.name + '.svg'), glyph.svg);

  diff.push(glyph_out);

});

// Create config template for new glyphs, if option set
if (args.diff_config) {

  if (!diff.length) {
    console.log("No new glyphs, skip writing diff");
    return;
  }

  fs.writeFileSync(args.diff_config, yaml.dump({glyphs: diff}));
}
<|endoftext|>