<commit_before>/**
 * @license Copyright 2019 Google Inc. 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.
 */
'use strict';

const crypto = require('crypto');
const childProcess = require('child_process');

/**
 * @param {Array<[string, ReadonlyArray<string>]>} commands
 * @return {import('child_process').SpawnSyncReturns<string>}
 */
function runCommandsUntilFirstSuccess(commands) {
  /** @type {import('child_process').SpawnSyncReturns<string>|undefined} */
  let result;

  for (const [command, args] of commands) {
    result = childProcess.spawnSync(command, args, {encoding: 'utf8'});
    if (result.status === 0) break;
  }

  if (!result) throw new Error('Must specify at least one command');
  return result;
}

const envVars = process.env;

/**
 * @return {string}
 */
function getCurrentHash() {
  if (envVars.TRAVIS_PULL_REQUEST_SHA) return envVars.TRAVIS_PULL_REQUEST_SHA;
  if (envVars.TRAVIS_COMMIT) return envVars.TRAVIS_COMMIT;

  const result = childProcess.spawnSync('git', ['rev-list', '--no-merges', '-n1', 'HEAD'], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to determine current hash with `git rev-parse HEAD`');
  }

  return result.stdout.trim();
}

/**
 * @param {string} hash
 * @return {string}
 */
function getCommitTime(hash) {
  const result = childProcess.spawnSync('git', ['log', '-n1', '--pretty=%cI', hash], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to retrieve committer timestamp from commit');
  }

  return result.stdout.trim();
}

/**
 * @return {string}
 */
function getCurrentBranchRaw_() {
  // Use Travis CI vars if available.
  if (envVars.TRAVIS_PULL_REQUEST_BRANCH) return envVars.TRAVIS_PULL_REQUEST_BRANCH;
  if (envVars.TRAVIS_BRANCH) return envVars.TRAVIS_BRANCH;
  // Use GitHub Actions vars if available. See https://github.com/GoogleChrome/lighthouse-ci/issues/43#issuecomment-551174778
  if (envVars.GITHUB_HEAD_REF) return envVars.GITHUB_HEAD_REF;
  if (envVars.GITHUB_REF) return envVars.GITHUB_REF;

  const result = childProcess.spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
    encoding: 'utf8',
  });

  const branch = result.stdout.trim();
  if (result.status !== 0 || branch === 'HEAD') {
    throw new Error('Unable to determine current branch with `git rev-parse --abbrev-ref HEAD`');
  }

  return branch;
}

/**
 * @return {string}
 */
function getCurrentBranch() {
  const branch = getCurrentBranchRaw_();
  if (branch === 'HEAD') throw new Error('Unable to determine current branch');
  return branch.replace('refs/heads/', '').slice(0, 40);
}

/**
 * @return {string}
 */
function getExternalBuildUrl() {
  return envVars.TRAVIS_BUILD_WEB_URL || '';
}

/**
 * @param {string} hash
 * @return {string}
 */
function getCommitMessage(hash = 'HEAD') {
  const result = childProcess.spawnSync('git', ['log', '--format=%s', '-n', '1', hash], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to determine commit message with `git log --format=%s -n 1`');
  }

  return result.stdout.trim().slice(0, 80);
}

/**
 * @param {string} hash
 * @return {string}
 */
function getAuthor(hash = 'HEAD') {
  const result = childProcess.spawnSync('git', ['log', '--format=%aN <%aE>', '-n', '1', hash], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to determine commit author with `git log --format=%aN <%aE> -n 1`');
  }

  return result.stdout.trim().slice(0, 256);
}

/**
 * @param {string} hash
 * @return {string}
 */
function getAvatarUrl(hash = 'HEAD') {
  const result = childProcess.spawnSync('git', ['log', '--format=%aE', '-n', '1', hash], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to determine commit email with `git log --format=%aE -n 1`');
  }

  // Use default gravatar image, see https://en.gravatar.com/site/implement/images/.
  const md5 = crypto.createHash('md5');
  md5.update(result.stdout.trim().toLowerCase());
  return `https://www.gravatar.com/avatar/${md5.digest('hex')}.jpg?d=identicon`;
}

/**
 * @param {string} [hash]
 * @return {string}
 */
function getAncestorHashForMaster(hash = 'HEAD') {
  const result = childProcess.spawnSync('git', ['rev-parse', `${hash}^`], {encoding: 'utf8'});
  // Ancestor hash is optional, so do not throw if it can't be computed.
  // See https://github.com/GoogleChrome/lighthouse-ci/issues/36
  if (result.status !== 0) return '';

  return result.stdout.trim();
}

/**
 * @param {string} [hash]
 * @return {string}
 */
function getAncestorHashForBranch(hash = 'HEAD') {
  const result = runCommandsUntilFirstSuccess([
    ['git', ['merge-base', hash, 'origin/master']],
    ['git', ['merge-base', hash, 'master']],
  ]);

  // Ancestor hash is optional, so do not throw if it can't be computed.
  // See https://github.com/GoogleChrome/lighthouse-ci/issues/36
  if (result.status !== 0) return '';

  return result.stdout.trim();
}

/**
 * @param {string} [hash]
 * @return {string}
 */
function getAncestorHash(hash = 'HEAD') {
  return getCurrentBranch() === 'master'
    ? getAncestorHashForMaster(hash)
    : getAncestorHashForBranch(hash);
}

function getRepoSlug() {
  if (envVars.TRAVIS_PULL_REQUEST_SLUG) return envVars.TRAVIS_PULL_REQUEST_SLUG;
  if (envVars.TRAVIS_REPO_SLUG) return envVars.TRAVIS_REPO_SLUG;
}

module.exports = {
  getCurrentHash,
  getCommitTime,
  getCurrentBranch,
  getExternalBuildUrl,
  getCommitMessage,
  getAuthor,
  getAvatarUrl,
  getAncestorHash,
  getAncestorHashForMaster,
  getAncestorHashForBranch,
  getRepoSlug,
};
<commit_msg>feat(utils): support github actions status checks<commit_after>/**
 * @license Copyright 2019 Google Inc. 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.
 */
'use strict';

const crypto = require('crypto');
const childProcess = require('child_process');

/**
 * @param {Array<[string, ReadonlyArray<string>]>} commands
 * @return {import('child_process').SpawnSyncReturns<string>}
 */
function runCommandsUntilFirstSuccess(commands) {
  /** @type {import('child_process').SpawnSyncReturns<string>|undefined} */
  let result;

  for (const [command, args] of commands) {
    result = childProcess.spawnSync(command, args, {encoding: 'utf8'});
    if (result.status === 0) break;
  }

  if (!result) throw new Error('Must specify at least one command');
  return result;
}

const envVars = process.env;

/**
 * @return {string}
 */
function getCurrentHash() {
  if (envVars.TRAVIS_PULL_REQUEST_SHA) return envVars.TRAVIS_PULL_REQUEST_SHA;
  if (envVars.TRAVIS_COMMIT) return envVars.TRAVIS_COMMIT;

  const result = childProcess.spawnSync('git', ['rev-list', '--no-merges', '-n1', 'HEAD'], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to determine current hash with `git rev-parse HEAD`');
  }

  return result.stdout.trim();
}

/**
 * @param {string} hash
 * @return {string}
 */
function getCommitTime(hash) {
  const result = childProcess.spawnSync('git', ['log', '-n1', '--pretty=%cI', hash], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to retrieve committer timestamp from commit');
  }

  return result.stdout.trim();
}

/**
 * @return {string}
 */
function getCurrentBranchRaw_() {
  // Use Travis CI vars if available.
  if (envVars.TRAVIS_PULL_REQUEST_BRANCH) return envVars.TRAVIS_PULL_REQUEST_BRANCH;
  if (envVars.TRAVIS_BRANCH) return envVars.TRAVIS_BRANCH;
  // Use GitHub Actions vars if available. See https://github.com/GoogleChrome/lighthouse-ci/issues/43#issuecomment-551174778
  if (envVars.GITHUB_HEAD_REF) return envVars.GITHUB_HEAD_REF;
  if (envVars.GITHUB_REF) return envVars.GITHUB_REF;

  const result = childProcess.spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
    encoding: 'utf8',
  });

  const branch = result.stdout.trim();
  if (result.status !== 0 || branch === 'HEAD') {
    throw new Error('Unable to determine current branch with `git rev-parse --abbrev-ref HEAD`');
  }

  return branch;
}

/**
 * @return {string}
 */
function getCurrentBranch() {
  const branch = getCurrentBranchRaw_();
  if (branch === 'HEAD') throw new Error('Unable to determine current branch');
  return branch.replace('refs/heads/', '').slice(0, 40);
}

/**
 * @return {string}
 */
function getExternalBuildUrl() {
  return envVars.TRAVIS_BUILD_WEB_URL || '';
}

/**
 * @param {string} hash
 * @return {string}
 */
function getCommitMessage(hash = 'HEAD') {
  const result = childProcess.spawnSync('git', ['log', '--format=%s', '-n', '1', hash], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to determine commit message with `git log --format=%s -n 1`');
  }

  return result.stdout.trim().slice(0, 80);
}

/**
 * @param {string} hash
 * @return {string}
 */
function getAuthor(hash = 'HEAD') {
  const result = childProcess.spawnSync('git', ['log', '--format=%aN <%aE>', '-n', '1', hash], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to determine commit author with `git log --format=%aN <%aE> -n 1`');
  }

  return result.stdout.trim().slice(0, 256);
}

/**
 * @param {string} hash
 * @return {string}
 */
function getAvatarUrl(hash = 'HEAD') {
  const result = childProcess.spawnSync('git', ['log', '--format=%aE', '-n', '1', hash], {
    encoding: 'utf8',
  });
  if (result.status !== 0) {
    throw new Error('Unable to determine commit email with `git log --format=%aE -n 1`');
  }

  // Use default gravatar image, see https://en.gravatar.com/site/implement/images/.
  const md5 = crypto.createHash('md5');
  md5.update(result.stdout.trim().toLowerCase());
  return `https://www.gravatar.com/avatar/${md5.digest('hex')}.jpg?d=identicon`;
}

/**
 * @param {string} [hash]
 * @return {string}
 */
function getAncestorHashForMaster(hash = 'HEAD') {
  const result = childProcess.spawnSync('git', ['rev-parse', `${hash}^`], {encoding: 'utf8'});
  // Ancestor hash is optional, so do not throw if it can't be computed.
  // See https://github.com/GoogleChrome/lighthouse-ci/issues/36
  if (result.status !== 0) return '';

  return result.stdout.trim();
}

/**
 * @param {string} [hash]
 * @return {string}
 */
function getAncestorHashForBranch(hash = 'HEAD') {
  const result = runCommandsUntilFirstSuccess([
    ['git', ['merge-base', hash, 'origin/master']],
    ['git', ['merge-base', hash, 'master']],
  ]);

  // Ancestor hash is optional, so do not throw if it can't be computed.
  // See https://github.com/GoogleChrome/lighthouse-ci/issues/36
  if (result.status !== 0) return '';

  return result.stdout.trim();
}

/**
 * @param {string} [hash]
 * @return {string}
 */
function getAncestorHash(hash = 'HEAD') {
  return getCurrentBranch() === 'master'
    ? getAncestorHashForMaster(hash)
    : getAncestorHashForBranch(hash);
}

function getRepoSlug() {
  // Support Travis CI
  if (envVars.TRAVIS_PULL_REQUEST_SLUG) return envVars.TRAVIS_PULL_REQUEST_SLUG;
  if (envVars.TRAVIS_REPO_SLUG) return envVars.TRAVIS_REPO_SLUG;
  // Support GitHub Actions
  if (envVars.GITHUB_REPOSITORY) return envVars.GITHUB_REPOSITORY;
}

module.exports = {
  getCurrentHash,
  getCommitTime,
  getCurrentBranch,
  getExternalBuildUrl,
  getCommitMessage,
  getAuthor,
  getAvatarUrl,
  getAncestorHash,
  getAncestorHashForMaster,
  getAncestorHashForBranch,
  getRepoSlug,
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Relay from 'react-relay/classic';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import Button from '@material-ui/core/Button';
import Menu from '@material-ui/core/Menu';
import IconArrowDropDown from 'material-ui/svg-icons/navigation/arrow-drop-down';
import { can } from '../Can';
import MultiSelector from '../layout/MultiSelector';
import ProjectRoute from '../../relay/ProjectRoute';
import { black54 } from '../../styles/js/shared';
import UpdateProjectMutation from '../../relay/mutations/UpdateProjectMutation';
import Message from '../Message';
import { safelyParseJSON } from '../../helpers';
import UserAvatars from '../UserAvatars';

class ProjectAssignmentComponent extends Component {
  state = {
    message: null,
  };

  handleClick = (event) => {
    this.setState({ anchorEl: event.currentTarget });
  };

  handleClose = () => {
    this.setState({ anchorEl: null });
  };

  handleSelect = (selected) => {
    const onFailure = (transaction) => {
      const error = transaction.getError();
      let message = error.source;
      const json = safelyParseJSON(error.source);
      if (json && json.error) {
        message = json.error;
      }
      this.setState({ message });
    };

    const onSuccess = () => {
      this.setState({ message: null });
      const message = (
        <FormattedMessage
          id="projectAssignment.processing"
          defaultMessage="Done! The assignments are now propagating to items and tasks. You will receive an e-mail when it's ready."
        />
      );
      this.context.setMessage(message);
    };

    Relay.Store.commitUpdate(
      new UpdateProjectMutation({
        id: this.props.project.id,
        assigned_to_ids: selected.join(','),
      }),
      { onSuccess, onFailure },
    );

    this.handleClose();
  };

  render() {
    if (!can(this.props.project.permissions, 'update Project')) {
      return null;
    }

    const { anchorEl } = this.state;
    const buttonStyle = {
      border: 0,
      color: black54,
    };

    const options = [];
    this.props.project.team.users.edges.forEach((user) => {
      options.push({ label: user.node.name, value: user.node.dbid.toString() });
    });
    const selected = [];
    const assignments = this.props.project.assigned_users.edges;
    assignments.forEach((user) => {
      selected.push(user.node.dbid.toString());
    });

    return (

        <Message message={this.state.message} />

          <Button
            className="project__assignment-button"
            onClick={this.handleClick}
            style={buttonStyle}
            disableTouchRipple
          >
            { selected.length > 0 ?
              <FormattedMessage
                id="projectAssignment.assignedTo"
                defaultMessage="Assigned to"
              /> :
              <FormattedMessage
                id="projectAssignment.unassigned"
                defaultMessage="Unassigned"
              /> }
            <IconArrowDropDown color={black54} />
          </Button>
          <Menu
            className="project__assignment-menu"
            anchorEl={anchorEl}
            open={Boolean(anchorEl)}
            onClose={this.handleClose}
          >
            <MultiSelector
              allowSelectAll
              allowUnselectAll
              allowSearch
              options={options}
              selected={selected}
              onDismiss={this.handleClose}
              onSubmit={this.handleSelect}
            />
          </Menu>
          <UserAvatars users={assignments} showMore />

    );
  }
}

ProjectAssignmentComponent.contextTypes = {
  setMessage: PropTypes.func,
};

const ProjectAssignmentContainer = Relay.createContainer(ProjectAssignmentComponent, {
  initialVariables: {
    contextId: null,
  },
  fragments: {
    project: () => Relay.QL`
      fragment on Project {
        id
        dbid
        permissions
        assigned_users(first: 10000) {
          edges {
            node {
              id
              dbid
              name
              source {
                id
                dbid
                image
              }
            }
          }
        }
        team {
          id
          dbid
          users(first: 10000) {
            edges {
              node {
                id
                dbid
                name
                source {
                  id
                  dbid
                  image
                }
              }
            }
          }
        }
      }
    `,
  },
});

const ProjectAssignment = (props) => {
  const { project } = props;
  const route = new ProjectRoute({ contextId: project.dbid.toString() });

  return (
    <Relay.RootContainer
      Component={ProjectAssignmentContainer}
      route={route}
    />
  );
};

export default ProjectAssignment;
<commit_msg>Ticket #7168: Do not list banned or pending users on project assignment<commit_after>import React, { Component } from 'react';
import Relay from 'react-relay/classic';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import Button from '@material-ui/core/Button';
import Menu from '@material-ui/core/Menu';
import IconArrowDropDown from 'material-ui/svg-icons/navigation/arrow-drop-down';
import { can } from '../Can';
import MultiSelector from '../layout/MultiSelector';
import ProjectRoute from '../../relay/ProjectRoute';
import { black54 } from '../../styles/js/shared';
import UpdateProjectMutation from '../../relay/mutations/UpdateProjectMutation';
import Message from '../Message';
import { safelyParseJSON } from '../../helpers';
import UserAvatars from '../UserAvatars';

class ProjectAssignmentComponent extends Component {
  state = {
    message: null,
  };

  handleClick = (event) => {
    this.setState({ anchorEl: event.currentTarget });
  };

  handleClose = () => {
    this.setState({ anchorEl: null });
  };

  handleSelect = (selected) => {
    const onFailure = (transaction) => {
      const error = transaction.getError();
      let message = error.source;
      const json = safelyParseJSON(error.source);
      if (json && json.error) {
        message = json.error;
      }
      this.setState({ message });
    };

    const onSuccess = () => {
      this.setState({ message: null });
      const message = (
        <FormattedMessage
          id="projectAssignment.processing"
          defaultMessage="Done! The assignments are now propagating to items and tasks. You will receive an e-mail when it's ready."
        />
      );
      this.context.setMessage(message);
    };

    Relay.Store.commitUpdate(
      new UpdateProjectMutation({
        id: this.props.project.id,
        assigned_to_ids: selected.join(','),
      }),
      { onSuccess, onFailure },
    );

    this.handleClose();
  };

  render() {
    if (!can(this.props.project.permissions, 'update Project')) {
      return null;
    }

    const { anchorEl } = this.state;
    const buttonStyle = {
      border: 0,
      color: black54,
    };

    const options = [];
    this.props.project.team.team_users.edges.forEach((teamUser) => {
      if (teamUser.node.status === 'member') {
        const { user } = teamUser.node;
        options.push({ label: user.name, value: user.dbid.toString() });
      }
    });
    const selected = [];
    const assignments = this.props.project.assigned_users.edges;
    assignments.forEach((user) => {
      selected.push(user.node.dbid.toString());
    });

    return (

        <Message message={this.state.message} />

          <Button
            className="project__assignment-button"
            onClick={this.handleClick}
            style={buttonStyle}
            disableTouchRipple
          >
            { selected.length > 0 ?
              <FormattedMessage
                id="projectAssignment.assignedTo"
                defaultMessage="Assigned to"
              /> :
              <FormattedMessage
                id="projectAssignment.unassigned"
                defaultMessage="Unassigned"
              /> }
            <IconArrowDropDown color={black54} />
          </Button>
          <Menu
            className="project__assignment-menu"
            anchorEl={anchorEl}
            open={Boolean(anchorEl)}
            onClose={this.handleClose}
          >
            <MultiSelector
              allowSelectAll
              allowUnselectAll
              allowSearch
              options={options}
              selected={selected}
              onDismiss={this.handleClose}
              onSubmit={this.handleSelect}
            />
          </Menu>
          <UserAvatars users={assignments} showMore />

    );
  }
}

ProjectAssignmentComponent.contextTypes = {
  setMessage: PropTypes.func,
};

const ProjectAssignmentContainer = Relay.createContainer(ProjectAssignmentComponent, {
  initialVariables: {
    contextId: null,
  },
  fragments: {
    project: () => Relay.QL`
      fragment on Project {
        id
        dbid
        permissions
        assigned_users(first: 10000) {
          edges {
            node {
              id
              dbid
              name
              source {
                id
                dbid
                image
              }
            }
          }
        }
        team {
          id
          dbid
          team_users(first: 10000) {
            edges {
              node {
                id
                status
                user {
                  id
                  dbid
                  name
                  source {
                    id
                    dbid
                    image
                  }
                }
              }
            }
          }
        }
      }
    `,
  },
});

const ProjectAssignment = (props) => {
  const { project } = props;
  const route = new ProjectRoute({ contextId: project.dbid.toString() });

  return (
    <Relay.RootContainer
      Component={ProjectAssignmentContainer}
      route={route}
    />
  );
};

export default ProjectAssignment;
<|endoftext|>


<commit_before>require('../handler-base')();

// Things that proxyquire will use.

var eventStore = {
    queryStatePartitionById: sinon.stub()
  },
  instanceFormatAsHal = sinon.stub(),
  validator = {
    isUUID: sinon.stub()
  };

// Configure up the controller use proxyquire
var handler = proxyquire('../../api/instances/instanceGet', {
  './instanceFormatAsHal': instanceFormatAsHal,
  '../helpers/eventStoreClient': eventStore,
  'validator': validator
});

function createRequest() {
  var request = httpMocks.createRequest({
      method: 'GET',
      url: '/api/instances/fakeId',
      params: {
        instanceId: 'fakeId'
      }
    });
  request.href = sinon.spy();
  return request;
}

function createResponse() {
  var response = httpMocks.createResponse();
  // Set up default mocks
  response.hal = sinon.spy();
  return response;
}

describe('instanceGet', function() {

  describe('when getting an instance with a valid instanceId it', function() {
    var instance = {},
        formattedInstance = {},
        request,
        response;

    before(function() {
      validator.isUUID.returns(true);
      eventStore.queryStatePartitionById.resolves(instance);
      instanceFormatAsHal.returns(formattedInstance);
      request = createRequest();
      response = createResponse();

      handler(request, response);
    });

    it('should call validator.isUUID once', function() {
      validator.isUUID.should.have.been.calledOnce;
    });

    it('should call validator.isUUID with correct args', function() {
      validator.isUUID.should.have.been.calledWith(request.params.instanceId);
    });

    it('should call eventStore.queryStatePartitionById once', function() {
      validator.isUUID.should.have.been.calledOnce;
    });

    it('should call eventStore.queryStatePartitionById with correct args', function() {
      var args = {
        name: 'instance',
        id: request.params.instanceId
      };
      eventStore.queryStatePartitionById.should.have.been.calledWith(args);
    });

    it('should call instanceFormatAsHal once', function() {
      instanceFormatAsHal.should.have.been.calledOnce;
    });

    it('should call instanceFormatAsHal with correct args', function() {
      instanceFormatAsHal.should.have.been.calledWith(request.href, instance);
    });

    it('should call res.hal with correct args', function() {
      response.hal.should.have.been.calledWith(formattedInstance);
    });

    it('should call res.hal once', function() {
      response.hal.should.have.been.calledOnce;
    });

  });

  describe('when getting an instance with an invalid instanceId it', function() {
    var request,
        response;

    before(function() {
      validator.isUUID.returns(false);
      request = createRequest();
      response = createResponse();
    });

    it('should call validator.isUUID once', function() {
      validator.isUUID.should.have.been.calledOnce;
    });

    it('should call validator.isUUID with correct args', function() {
      validator.isUUID.should.have.been.calledWith(request.params.instanceId);
    });

    // TODO: validate request.params with schema and make it throw exceptions when invalid
    /*
    it('should throw an exception when instanceId is invalid', function() {

    });
    */
  });

});<commit_msg>S-51508 Cleaned up nesting of sub-describes<commit_after>require('../handler-base')();

// Things that proxyquire will use.

var eventStore = {
    queryStatePartitionById: sinon.stub()
  },
  instanceFormatAsHal = sinon.stub(),
  validator = {
    isUUID: sinon.stub()
  };

// Configure up the controller use proxyquire
var handler = proxyquire('../../api/instances/instanceGet', {
  './instanceFormatAsHal': instanceFormatAsHal,
  '../helpers/eventStoreClient': eventStore,
  'validator': validator
});

function createRequest() {
  var request = httpMocks.createRequest({
      method: 'GET',
      url: '/api/instances/fakeId',
      params: {
        instanceId: 'fakeId'
      }
    });
  request.href = sinon.spy();
  return request;
}

function createResponse() {
  var response = httpMocks.createResponse();
  // Set up default mocks
  response.hal = sinon.spy();
  return response;
}

describe('instanceGet', function() {

  describe('when getting an instance with a valid instanceId it', function() {
    var instance = {},
        formattedInstance = {},
        request,
        response;

    before(function() {
      validator.isUUID.returns(true);
      eventStore.queryStatePartitionById.resolves(instance);
      instanceFormatAsHal.returns(formattedInstance);
      request = createRequest();
      response = createResponse();

      handler(request, response);
    });

    it('should call validator.isUUID once', function() {
      validator.isUUID.should.have.been.calledOnce;
    });

    it('should call validator.isUUID with correct args', function() {
      validator.isUUID.should.have.been.calledWith(request.params.instanceId);
    });

    it('should call eventStore.queryStatePartitionById once', function() {
      validator.isUUID.should.have.been.calledOnce;
    });

    it('should call eventStore.queryStatePartitionById with correct args', function() {
      var args = {
        name: 'instance',
        id: request.params.instanceId
      };
      eventStore.queryStatePartitionById.should.have.been.calledWith(args);
    });

    it('should call instanceFormatAsHal once', function() {
      instanceFormatAsHal.should.have.been.calledOnce;
    });

    it('should call instanceFormatAsHal with correct args', function() {
      instanceFormatAsHal.should.have.been.calledWith(request.href, instance);
    });

    it('should call res.hal with correct args', function() {
      response.hal.should.have.been.calledWith(formattedInstance);
    });

    it('should call res.hal once', function() {
      response.hal.should.have.been.calledOnce;
    });

  });

  describe('when getting an instance with an invalid instanceId it', function() {
    var request,
        response;

    before(function() {
      validator.isUUID.returns(false);
      request = createRequest();
      response = createResponse();
    });

    it('should call validator.isUUID once', function() {
      validator.isUUID.should.have.been.calledOnce;
    });

    it('should call validator.isUUID with correct args', function() {
      validator.isUUID.should.have.been.calledWith(request.params.instanceId);
    });

    // TODO: validate request.params with schema and make it throw exceptions when invalid
    /*
    it('should throw an exception when instanceId is invalid', function() {

    });
    */
  });

});
<|endoftext|>


<commit_before>var GetTextContentsComponent = require('GetTextContentsComponent');
var TextSelectorComponent = require('TextSelectorComponent');
var Case = require('Case');
const DOM = require('DOM');
var LanguageComponent = require('LanguageComponent');
var TextNodeFilterComponent = require('TextNodeFilterComponent');
var guessLanguage = require('guessLanguage/lib/guessLanguage');
var LanguageChangesAreIdentified = {
  run: function (test) {
    var scope = test.get('scope');
    var currentLanguage = LanguageComponent.getDocumentLanguage(scope, true);
    var text, matches, $element, failed;

    var noCharactersMatch = function ($element, language, matches, regularExpression) {
      var $children = DOM.scry('[lang=' + language + ']', $element);
      var childMatches;
      if ($children.length === 0) {
        return true;
      }
      matches = matches.length;
      $children.each(function () {
        childMatches = GetTextContentsComponent($(this)).match(regularExpression);
        if (childMatches) {
          matches -= childMatches.length;
        }
      });
      return matches > 0;
    };

    var findCurrentLanguage = function ($element) {
      if ($element.attr('lang')) {
        return $element.attr('lang').trim().toLowerCase().split('-')[0];
      }
      if ($element.parents('[lang]').length) {
        return $element.parents('[lang]:first').attr('lang').trim().toLowerCase().split('-')[0];
      }
      return LanguageComponent.getDocumentLanguage(scope, true);
    };

    DOM.scry(TextSelectorComponent, scope)
      .filter(function (element) {
        return TextNodeFilterComponent(element);
      })
      .forEach(function (element) {
        var self = element;
        $element = $(element);
        currentLanguage = findCurrentLanguage($element);
        text = GetTextContentsComponent($element);
        failed = false;

        var singletons = LanguageComponent.scriptSingletons;
        for (var code in singletons) {
          if (singletons.hasOwnProperty(code)) {
            var regularExpression = singletons[code];
            if (code === currentLanguage) {
              return;
            }
            matches = text.match(regularExpression);
            if (matches && matches.length && noCharactersMatch($element, code, matches, regularExpression)) {
              test.add(Case({
                element: self,
                info: {
                  language: code
                },
                status: 'failed'
              }));
              failed = true;
            }
          }
        }
        var scripts = LanguageComponent.scripts;
        for (var script in scripts) {
          if (scripts.hasOwnProperty(script)) {
            var script = scripts[script];
          }
          if (script.languageindexOf(currentLanguage) !== -1) {
            return;
          }
          matches = text.match(script.regularExpression);
          if (matches && matches.length && noCharactersMatch($element, script, matches, regularExpression)) {
            test.add(Case({
              element: self,
              info: {
                language: script
              },
              status: 'failed'
            }));
            failed = true;
          }
        }
        if (typeof guessLanguage !== 'undefined' && !DOM.scry('[lang]', $element).length && $element.text().trim().length > 400) {
          guessLanguage.info($element.text(), function (info) {
            if (info[0] !== currentLanguage) {
              test.add(Case({
                element: self,
                info: {
                  language: info[0]
                },
                status: 'failed'
              }));
              failed = true;
            }
          });
        }
        // Passes.
        if (!failed) {
          test.add(Case({
            element: self,
            status: 'passed'
          }));
        }
      });
  },

  meta: {
    testability: 0.5,
    title: {
      en: 'Use language attributes to indicate changes in language',
      nl: 'Gebruik het taal-attribuut om aan te geven dat de taal verandert'
    },
    description: {
      en: 'When the language of the document changes, make sure to wrap those changes in an element with the lang attribute.',
      nl: 'Als de taal van het document verandert, zet deze veranderingen dan in een element met het lang-attribuut.'
    },
    guidelines: {
      wcag: {
        '3.1.2': {
          techniques: [
            'H58'
          ]
        }
      }
    },
    tags: [
      'language',
      'content'
    ]
  }
};
module.exports = LanguageChangesAreIdentified;
<commit_msg>Fixing lint issues<commit_after>var GetTextContentsComponent = require('GetTextContentsComponent');
var TextSelectorComponent = require('TextSelectorComponent');
var Case = require('Case');
const DOM = require('DOM');
var LanguageComponent = require('LanguageComponent');
var TextNodeFilterComponent = require('TextNodeFilterComponent');
var guessLanguage = require('guessLanguage/lib/guessLanguage');
var LanguageChangesAreIdentified = {
  run: function (test) {
    var scope = test.get('scope');
    var currentLanguage = LanguageComponent.getDocumentLanguage(scope, true);
    var text, matches, $element, failed;

    var noCharactersMatch = function ($element, language, matches, regularExpression) {
      var $children = DOM.scry('[lang=' + language + ']', $element);
      var childMatches;
      if ($children.length === 0) {
        return true;
      }
      matches = matches.length;
      $children.each(function () {
        childMatches = GetTextContentsComponent($(this)).match(regularExpression);
        if (childMatches) {
          matches -= childMatches.length;
        }
      });
      return matches > 0;
    };

    var findCurrentLanguage = function ($element) {
      if ($element.attr('lang')) {
        return $element.attr('lang').trim().toLowerCase().split('-')[0];
      }
      if ($element.parents('[lang]').length) {
        return $element.parents('[lang]:first').attr('lang').trim().toLowerCase().split('-')[0];
      }
      return LanguageComponent.getDocumentLanguage(scope, true);
    };

    DOM.scry(TextSelectorComponent, scope)
      .filter(function (element) {
        return TextNodeFilterComponent(element);
      })
      .forEach(function (element) {
        var self = element;
        $element = $(element);
        currentLanguage = findCurrentLanguage($element);
        text = GetTextContentsComponent($element);
        failed = false;

        var singletons = LanguageComponent.scriptSingletons;
        for (var code in singletons) {
          if (singletons.hasOwnProperty(code)) {
            var regularExpression = singletons[code];
            if (code === currentLanguage) {
              return;
            }
            matches = text.match(regularExpression);
            if (matches && matches.length && noCharactersMatch($element, code, matches, regularExpression)) {
              test.add(Case({
                element: self,
                info: {
                  language: code
                },
                status: 'failed'
              }));
              failed = true;
            }
          }
        }
        var scripts = LanguageComponent.scripts;
        for (var name in scripts) {
          if (scripts.hasOwnProperty(name)) {
            var script = scripts[name];
          }
          if (script.languageindexOf(currentLanguage) !== -1) {
            return;
          }
          matches = text.match(script.regularExpression);
          if (matches && matches.length && noCharactersMatch($element, name, matches, regularExpression)) {
            test.add(Case({
              element: self,
              info: {
                language: name
              },
              status: 'failed'
            }));
            failed = true;
          }
        }
        if (typeof guessLanguage !== 'undefined' && !DOM.scry('[lang]', $element).length && $element.text().trim().length > 400) {
          guessLanguage.info($element.text(), function (info) {
            if (info[0] !== currentLanguage) {
              test.add(Case({
                element: self,
                info: {
                  language: info[0]
                },
                status: 'failed'
              }));
              failed = true;
            }
          });
        }
        // Passes.
        if (!failed) {
          test.add(Case({
            element: self,
            status: 'passed'
          }));
        }
      });
  },

  meta: {
    testability: 0.5,
    title: {
      en: 'Use language attributes to indicate changes in language',
      nl: 'Gebruik het taal-attribuut om aan te geven dat de taal verandert'
    },
    description: {
      en: 'When the language of the document changes, make sure to wrap those changes in an element with the lang attribute.',
      nl: 'Als de taal van het document verandert, zet deze veranderingen dan in een element met het lang-attribuut.'
    },
    guidelines: {
      wcag: {
        '3.1.2': {
          techniques: [
            'H58'
          ]
        }
      }
    },
    tags: [
      'language',
      'content'
    ]
  }
};
module.exports = LanguageChangesAreIdentified;
<|endoftext|>


<commit_before>/*
 *  (c) AstroPrint Product Team. 3DaGoGo, Inc. (product@astroprint.com)
 *
 *  Distributed under the GNU Affero General Public License http://www.gnu.org/licenses/agpl.html
 */

/* global */

/* exported ClearBedModal */

var ClearBedModal = Backbone.View.extend({
	el: '#clear-bed-modal',
	clearBedView: null,
	events: {
		'click button.alert': 'onConfirm',
		'click button.secondary': 'close'
	},
	onConfirm: function()
	{
		this.$el.foundation('reveal', 'close');
		if (!this.clearBedView) {
			this.clearBedView = new ClearBedView(this);
		}
    this.clearBedView.clearBed();
    this.$el.foundation('reveal', 'close');
	},
	open: function() {
		this.$el.foundation('reveal', 'open');
	},
	close: function() {
		this.$el.foundation('reveal', 'close');
	}
});

var ClearBedView = Backbone.View.extend({
	el: '#clear-bed-view',
	clearBed: function() {
    $.ajax({
    url: API_BASEURL + "clearbed",
    method: "POST"}).
    fail(function() {
      noty({text: "There was an error cleaning bed", timeout: 5000});
    })
	}
});
<commit_msg>fixed event to new class<commit_after>/*
 *  (c) AstroPrint Product Team. 3DaGoGo, Inc. (product@astroprint.com)
 *
 *  Distributed under the GNU Affero General Public License http://www.gnu.org/licenses/agpl.html
 */

/* global */

/* exported ClearBedModal */

var ClearBedModal = Backbone.View.extend({
	el: '#clear-bed-modal',
	clearBedView: null,
	events: {
		'click button.warning': 'onConfirm',
		'click button.secondary': 'close'
	},
	onConfirm: function()
	{
		this.$el.foundation('reveal', 'close');
		if (!this.clearBedView) {
			this.clearBedView = new ClearBedView(this);
		}
    this.clearBedView.clearBed();
    this.$el.foundation('reveal', 'close');
	},
	open: function() {
		this.$el.foundation('reveal', 'open');
	},
	close: function() {
		this.$el.foundation('reveal', 'close');
	}
});

var ClearBedView = Backbone.View.extend({
	el: '#clear-bed-view',
	clearBed: function() {
    $.ajax({
    url: API_BASEURL + "clearbed",
    method: "POST"}).
    fail(function() {
      noty({text: "There was an error cleaning bed", timeout: 5000});
    })
	}
});
<|endoftext|>


<commit_before>JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({
  setValue: function(value,initial) {

    // Sanitize value before setting it
    var sanitized = this.typecast(value || '');

    if(this.enum_values.indexOf(sanitized) < 0) sanitized = this.enum_values[0];

    if(this.value === sanitized) return;

    if(initial) this.is_dirty = false;
    else if(this.jsoneditor.options.show_errors === "change") this.is_dirty = true;

    this.input.value = this.enum_options[this.enum_values.indexOf(sanitized)];

    this.value = sanitized;
    this.onChange();
    this.change();
  },
  register: function() {
    this._super();
    if(!this.input) return;
    this.input.setAttribute('name',this.formname);
  },
  unregister: function() {
    this._super();
    if(!this.input) return;
    this.input.removeAttribute('name');
  },
  getNumColumns: function() {
    if(!this.enum_options) return 3;
    var longest_text = this.getTitle().length;
    for(var i=0; i<this.enum_options.length; i++) {
      longest_text = Math.max(longest_text,this.enum_options[i].length+4);
    }
    return Math.min(12,Math.max(longest_text/7,2));
  },
  typecast: function(value) {
    if (this.schema.type === "boolean") return !!value;
    else if (this.schema.type === "number") return 1*value || 0;
    else if (this.schema.type === "integer") return Math.floor(value*1 || 0);
    else return ""+value;
  },
  getValue: function() {
    if (!this.dependenciesFulfilled) {
      return undefined;
    }
    return this.typecast(this.value);
  },
  preBuild: function() {
    var self = this;
    this.input_type = 'select';
    this.enum_options = [];
    this.enum_values = [];
    this.enum_display = [];
    var i;

    // Enum options enumerated
    if(this.schema["enum"]) {
      var display = this.schema.options && this.schema.options.enum_titles || [];

      $each(this.schema["enum"],function(i,option) {
        self.enum_options[i] = ""+option;
        self.enum_display[i] = ""+(display[i] || option);
        self.enum_values[i] = self.typecast(option);
      });

      if(!this.isRequired()){
        self.enum_display.unshift(' ');
        self.enum_options.unshift('undefined');
        self.enum_values.unshift(undefined);
      }

    }
    // Boolean
    else if(this.schema.type === "boolean") {
      self.enum_display = this.schema.options && this.schema.options.enum_titles || ['true','false'];
      self.enum_options = ['1',''];
      self.enum_values = [true,false];

      if(!this.isRequired()){
        self.enum_display.unshift(' ');
        self.enum_options.unshift('undefined');
        self.enum_values.unshift(undefined);
      }

    }
    // Dynamic Enum
    else if(this.schema.enumSource) {
      this.enumSource = [];
      this.enum_display = [];
      this.enum_options = [];
      this.enum_values = [];

      // Shortcut declaration for using a single array
      if(!(Array.isArray(this.schema.enumSource))) {
        if(this.schema.enumValue) {
          this.enumSource = [
            {
              source: this.schema.enumSource,
              value: this.schema.enumValue
            }
          ];
        }
        else {
          this.enumSource = [
            {
              source: this.schema.enumSource
            }
          ];
        }
      }
      else {
        for(i=0; i<this.schema.enumSource.length; i++) {
          // Shorthand for watched variable
          if(typeof this.schema.enumSource[i] === "string") {
            this.enumSource[i] = {
              source: this.schema.enumSource[i]
            };
          }
          // Make a copy of the schema
          else if(!(Array.isArray(this.schema.enumSource[i]))) {
            this.enumSource[i] = $extend({},this.schema.enumSource[i]);
          }
          else {
            this.enumSource[i] = this.schema.enumSource[i];
          }
        }
      }

      // Now, enumSource is an array of sources
      // Walk through this array and fix up the values
      for(i=0; i<this.enumSource.length; i++) {
        if(this.enumSource[i].value) {
          this.enumSource[i].value = this.jsoneditor.compileTemplate(this.enumSource[i].value, this.template_engine);
        }
        if(this.enumSource[i].title) {
          this.enumSource[i].title = this.jsoneditor.compileTemplate(this.enumSource[i].title, this.template_engine);
        }
        if(this.enumSource[i].filter) {
          this.enumSource[i].filter = this.jsoneditor.compileTemplate(this.enumSource[i].filter, this.template_engine);
        }
      }
    }
    // Other, not supported
    else {
      throw "'select' editor requires the enum property to be set.";
    }

  },
  build: function() {
    var self = this;
    if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired());
    if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
    if(this.options.infoText) this.infoButton = this.theme.getInfoButton(this.options.infoText);
    if(this.options.compact) this.container.classList.add('compact');

    this.input = this.theme.getSelectInput(this.enum_options, false);
    this.theme.setSelectOptions(this.input,this.enum_options,this.enum_display);

    if(this.schema.readOnly || this.schema.readonly) {
      this.always_disabled = true;
      this.input.disabled = true;
    }

    // Set custom attributes on input element. Parameter is array of protected keys. Empty array if none.
    this.setInputAttributes([]);

    this.input.addEventListener('change',function(e) {
      e.preventDefault();
      e.stopPropagation();
      self.onInputChange();
    });

    this.control = this.theme.getFormControl(this.label, this.input, this.description, this.infoButton);
    this.container.appendChild(this.control);

    this.value = this.enum_values[0];

    // Any special formatting that needs to happen after the input is added to the dom
    window.requestAnimationFrame(function() {
      if(self.input.parentNode) self.afterInputReady();
    });
  },
  afterInputReady: function() {
    var self = this;
    self.theme.afterInputReady(self.input);
  },
  onInputChange: function() {
    var val = this.typecast(this.input.value);

    var new_val;
    // Invalid option, use first option instead
    if(this.enum_values.indexOf(val) === -1) {
      new_val = this.enum_values[0];
    }
    else {
      new_val = this.enum_values[this.enum_values.indexOf(val)];
    }

    // If valid hasn't changed
    if(new_val === this.value) return;

    this.is_dirty = true;

    // Store new value and propogate change event
    this.value = new_val;
    this.onChange(true);
  },
  onWatchedFieldChange: function() {
    var self = this, vars, j, update = false;
    var select_options = [], select_titles = [];

    // If this editor uses a dynamic select box
    if(this.enumSource) {
      vars = this.getWatchedFieldValues();

      for(var i=0; i<this.enumSource.length; i++) {
        // Constant values
        if(Array.isArray(this.enumSource[i])) {
          select_options = select_options.concat(this.enumSource[i]);
          select_titles = select_titles.concat(this.enumSource[i]);
        }
        else {
          var items = [];
          // Static list of items
          if(Array.isArray(this.enumSource[i].source)) {
            items = this.enumSource[i].source;
          // A watched field
          } else {
            items = vars[this.enumSource[i].source];
          }

          if(items) {
            // Only use a predefined part of the array
            if(this.enumSource[i].slice) {
              items = Array.prototype.slice.apply(items,this.enumSource[i].slice);
            }
            // Filter the items
            if(this.enumSource[i].filter) {
              var new_items = [];
              for(j=0; j<items.length; j++) {
                if(this.enumSource[i].filter({i:j,item:items[j],watched:vars})) new_items.push(items[j]);
              }
              items = new_items;
            }

            var item_titles = [];
            var item_values = [];
            for(j=0; j<items.length; j++) {
              var item = items[j];

              // Rendered value
              if(this.enumSource[i].value) {
                item_values[j] = this.typecast(this.enumSource[i].value({
                  i: j,
                  item: item
                }));
              }
              // Use value directly
              else {
                item_values[j] = items[j];
              }

              // Rendered title
              if(this.enumSource[i].title) {
                item_titles[j] = this.enumSource[i].title({
                  i: j,
                  item: item
                });
              }
              // Use value as the title also
              else {
                item_titles[j] = item_values[j];
              }
            }

            // TODO: sort

            select_options = select_options.concat(item_values);
            select_titles = select_titles.concat(item_titles);
          }
        }
      }

      var prev_value = this.value;

      this.theme.setSelectOptions(this.input, select_options, select_titles);
      this.enum_options = select_options;
      this.enum_display = select_titles;
      this.enum_values = select_options;

      // If the previous value is still in the new select options, stick with it
      if(select_options.indexOf(prev_value) !== -1) {
        this.input.value = prev_value;
        this.value = prev_value;
      }
      // Otherwise, set the value to the first select option
      else {
        this.input.value = select_options[0];
        this.value = this.typecast(select_options[0] || "");
        if(this.parent) this.parent.onChildEditorChange(this);
        else this.jsoneditor.onChange();
        this.jsoneditor.notifyWatchers(this.path);
      }

      update = true;
    }

    this._super();
  },
  enable: function() {
    if(!this.always_disabled) {
      this.input.disabled = false;
    }
    this._super();
  },
  disable: function(always_disabled) {
    if(always_disabled) this.always_disabled = true;
    this.input.disabled = true;
    this._super(always_disabled);
  },
  destroy: function() {
    if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
    if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
    if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);

    this._super();
  },
  showValidationErrors: function (errors) {
    var self = this;

    this.previous_error_setting = this.jsoneditor.options.show_errors;

    var messages = [];
    $each(errors, function (i, error) {
      if (error.path === self.path) {
        messages.push(error.message);
      }
    });

    if (messages.length) {
      this.theme.addInputError(this.input, messages.join('. ') + '.');
    }
    else {
      this.theme.removeInputError(this.input);
    }
  }
});<commit_msg>Fix of #470 - Constant enumSource value specified as array of strings causes a template compilation error<commit_after>JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({
  setValue: function(value,initial) {

    // Sanitize value before setting it
    var sanitized = this.typecast(value || '');

    if(this.enum_values.indexOf(sanitized) < 0) sanitized = this.enum_values[0];

    if(this.value === sanitized) return;

    if(initial) this.is_dirty = false;
    else if(this.jsoneditor.options.show_errors === "change") this.is_dirty = true;

    this.input.value = this.enum_options[this.enum_values.indexOf(sanitized)];

    this.value = sanitized;
    this.onChange();
    this.change();
  },
  register: function() {
    this._super();
    if(!this.input) return;
    this.input.setAttribute('name',this.formname);
  },
  unregister: function() {
    this._super();
    if(!this.input) return;
    this.input.removeAttribute('name');
  },
  getNumColumns: function() {
    if(!this.enum_options) return 3;
    var longest_text = this.getTitle().length;
    for(var i=0; i<this.enum_options.length; i++) {
      longest_text = Math.max(longest_text,this.enum_options[i].length+4);
    }
    return Math.min(12,Math.max(longest_text/7,2));
  },
  typecast: function(value) {
    if (this.schema.type === "boolean") return !!value;
    else if (this.schema.type === "number") return 1*value || 0;
    else if (this.schema.type === "integer") return Math.floor(value*1 || 0);
    else return ""+value;
  },
  getValue: function() {
    if (!this.dependenciesFulfilled) {
      return undefined;
    }
    return this.typecast(this.value);
  },
  preBuild: function() {
    var self = this;
    this.input_type = 'select';
    this.enum_options = [];
    this.enum_values = [];
    this.enum_display = [];
    var i;

    // Enum options enumerated
    if(this.schema["enum"]) {
      var display = this.schema.options && this.schema.options.enum_titles || [];

      $each(this.schema["enum"],function(i,option) {
        self.enum_options[i] = ""+option;
        self.enum_display[i] = ""+(display[i] || option);
        self.enum_values[i] = self.typecast(option);
      });

      if(!this.isRequired()){
        self.enum_display.unshift(' ');
        self.enum_options.unshift('undefined');
        self.enum_values.unshift(undefined);
      }

    }
    // Boolean
    else if(this.schema.type === "boolean") {
      self.enum_display = this.schema.options && this.schema.options.enum_titles || ['true','false'];
      self.enum_options = ['1',''];
      self.enum_values = [true,false];

      if(!this.isRequired()){
        self.enum_display.unshift(' ');
        self.enum_options.unshift('undefined');
        self.enum_values.unshift(undefined);
      }

    }
    // Dynamic Enum
    else if(this.schema.enumSource) {
      this.enumSource = [];
      this.enum_display = [];
      this.enum_options = [];
      this.enum_values = [];

      // Shortcut declaration for using a single array
      if(!(Array.isArray(this.schema.enumSource))) {
        if(this.schema.enumValue) {
          this.enumSource = [
            {
              source: this.schema.enumSource,
              value: this.schema.enumValue
            }
          ];
        }
        else {
          this.enumSource = [
            {
              source: this.schema.enumSource
            }
          ];
        }
      }
      else {
        for(i=0; i<this.schema.enumSource.length; i++) {
          // Shorthand for watched variable
          if(typeof this.schema.enumSource[i] === "string") {
            this.enumSource[i] = {
              source: this.schema.enumSource[i]
            };
          }
          // Make a copy of the schema
          else if(!(Array.isArray(this.schema.enumSource[i]))) {
            this.enumSource[i] = $extend({},this.schema.enumSource[i]);
          }
          else {
            this.enumSource[i] = this.schema.enumSource[i];
          }
        }
      }

      // Now, enumSource is an array of sources
      // Walk through this array and fix up the values
      for(i=0; i<this.enumSource.length; i++) {
        if(this.enumSource[i].value) {
          this.enumSource[i].value = this.jsoneditor.compileTemplate(this.enumSource[i].value, this.template_engine);
        }
        if(this.enumSource[i].title) {
          this.enumSource[i].title = this.jsoneditor.compileTemplate(this.enumSource[i].title, this.template_engine);
        }
        if(this.enumSource[i].filter && !(Array.isArray(this.enumSource[i]))) {
          this.enumSource[i].filter = this.jsoneditor.compileTemplate(this.enumSource[i].filter, this.template_engine);
        }
      }
    }
    // Other, not supported
    else {
      throw "'select' editor requires the enum property to be set.";
    }

  },
  build: function() {
    var self = this;
    if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired());
    if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
    if(this.options.infoText) this.infoButton = this.theme.getInfoButton(this.options.infoText);
    if(this.options.compact) this.container.classList.add('compact');

    this.input = this.theme.getSelectInput(this.enum_options, false);
    this.theme.setSelectOptions(this.input,this.enum_options,this.enum_display);

    if(this.schema.readOnly || this.schema.readonly) {
      this.always_disabled = true;
      this.input.disabled = true;
    }

    // Set custom attributes on input element. Parameter is array of protected keys. Empty array if none.
    this.setInputAttributes([]);

    this.input.addEventListener('change',function(e) {
      e.preventDefault();
      e.stopPropagation();
      self.onInputChange();
    });

    this.control = this.theme.getFormControl(this.label, this.input, this.description, this.infoButton);
    this.container.appendChild(this.control);

    this.value = this.enum_values[0];

    // Any special formatting that needs to happen after the input is added to the dom
    window.requestAnimationFrame(function() {
      if(self.input.parentNode) self.afterInputReady();
    });
  },
  afterInputReady: function() {
    var self = this;
    self.theme.afterInputReady(self.input);
  },
  onInputChange: function() {
    var val = this.typecast(this.input.value);

    var new_val;
    // Invalid option, use first option instead
    if(this.enum_values.indexOf(val) === -1) {
      new_val = this.enum_values[0];
    }
    else {
      new_val = this.enum_values[this.enum_values.indexOf(val)];
    }

    // If valid hasn't changed
    if(new_val === this.value) return;

    this.is_dirty = true;

    // Store new value and propogate change event
    this.value = new_val;
    this.onChange(true);
  },
  onWatchedFieldChange: function() {
    var self = this, vars, j, update = false;
    var select_options = [], select_titles = [];

    // If this editor uses a dynamic select box
    if(this.enumSource) {
      vars = this.getWatchedFieldValues();

      for(var i=0; i<this.enumSource.length; i++) {
        // Constant values
        if(Array.isArray(this.enumSource[i])) {
          select_options = select_options.concat(this.enumSource[i]);
          select_titles = select_titles.concat(this.enumSource[i]);
        }
        else {
          var items = [];
          // Static list of items
          if(Array.isArray(this.enumSource[i].source)) {
            items = this.enumSource[i].source;
          // A watched field
          } else {
            items = vars[this.enumSource[i].source];
          }

          if(items) {
            // Only use a predefined part of the array
            if(this.enumSource[i].slice) {
              items = Array.prototype.slice.apply(items,this.enumSource[i].slice);
            }
            // Filter the items
            if(this.enumSource[i].filter) {
              var new_items = [];
              for(j=0; j<items.length; j++) {
                if(this.enumSource[i].filter({i:j,item:items[j],watched:vars})) new_items.push(items[j]);
              }
              items = new_items;
            }

            var item_titles = [];
            var item_values = [];
            for(j=0; j<items.length; j++) {
              var item = items[j];

              // Rendered value
              if(this.enumSource[i].value) {
                item_values[j] = this.typecast(this.enumSource[i].value({
                  i: j,
                  item: item
                }));
              }
              // Use value directly
              else {
                item_values[j] = items[j];
              }

              // Rendered title
              if(this.enumSource[i].title) {
                item_titles[j] = this.enumSource[i].title({
                  i: j,
                  item: item
                });
              }
              // Use value as the title also
              else {
                item_titles[j] = item_values[j];
              }
            }

            // TODO: sort

            select_options = select_options.concat(item_values);
            select_titles = select_titles.concat(item_titles);
          }
        }
      }

      var prev_value = this.value;

      this.theme.setSelectOptions(this.input, select_options, select_titles);
      this.enum_options = select_options;
      this.enum_display = select_titles;
      this.enum_values = select_options;

      // If the previous value is still in the new select options, stick with it
      if(select_options.indexOf(prev_value) !== -1) {
        this.input.value = prev_value;
        this.value = prev_value;
      }
      // Otherwise, set the value to the first select option
      else {
        this.input.value = select_options[0];
        this.value = this.typecast(select_options[0] || "");
        if(this.parent) this.parent.onChildEditorChange(this);
        else this.jsoneditor.onChange();
        this.jsoneditor.notifyWatchers(this.path);
      }

      update = true;
    }

    this._super();
  },
  enable: function() {
    if(!this.always_disabled) {
      this.input.disabled = false;
    }
    this._super();
  },
  disable: function(always_disabled) {
    if(always_disabled) this.always_disabled = true;
    this.input.disabled = true;
    this._super(always_disabled);
  },
  destroy: function() {
    if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
    if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
    if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);

    this._super();
  },
  showValidationErrors: function (errors) {
    var self = this;

    this.previous_error_setting = this.jsoneditor.options.show_errors;

    var messages = [];
    $each(errors, function (i, error) {
      if (error.path === self.path) {
        messages.push(error.message);
      }
    });

    if (messages.length) {
      this.theme.addInputError(this.input, messages.join('. ') + '.');
    }
    else {
      this.theme.removeInputError(this.input);
    }
  }
});<|endoftext|>


<commit_before>JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
  register: function() {
    this._super();
    if(!this.input) return;
    this.input.setAttribute('name',this.formname);
  },
  unregister: function() {
    this._super();
    if(!this.input) return;
    this.input.removeAttribute('name');
  },
  setValue: function(value,initial,from_template) {
    var self = this;

    if(this.template && !from_template) {
      return;
    }

    if(value === null || typeof value === 'undefined') value = "";
    else if(typeof value === "object") value = JSON.stringify(value);
    else if(typeof value !== "string") value = ""+value;

    if(value === this.serialized) return;

    // Sanitize value before setting it
    var sanitized = this.sanitize(value);

    if(this.input.value === sanitized) {
      return;
    }

    this.input.value = sanitized;

    // If using SCEditor, update the WYSIWYG
    if(this.sceditor_instance) {
      this.sceditor_instance.val(sanitized);
    }
    else if(this.ace_editor) {
      this.ace_editor.setValue(sanitized);
    }

    var changed = from_template || this.getValue() !== value;

    this.refreshValue();

    if(initial) this.is_dirty = false;
    else if(this.jsoneditor.options.show_errors === "change") this.is_dirty = true;

    if(this.adjust_height) this.adjust_height(this.input);

    // Bubble this setValue to parents if the value changed
    this.onChange(changed);
  },
  getNumColumns: function() {
    var min = Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);
    var num;

    if(this.input_type === 'textarea') num = 6;
    else if(['text','email'].indexOf(this.input_type) >= 0) num = 4;
    else num = 2;

    return Math.min(12,Math.max(min,num));
  },
  build: function() {
    var self = this, i;
    if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
    if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);

    this.format = this.schema.format;
    if(!this.format && this.schema.media && this.schema.media.type) {
      this.format = this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,'');
    }
    if(!this.format && this.options.default_format) {
      this.format = this.options.default_format;
    }
    if(this.options.format) {
      this.format = this.options.format;
    }

    // Specific format
    if(this.format) {
      // Text Area
      if(this.format === 'textarea') {
        this.input_type = 'textarea';
        this.input = this.theme.getTextareaInput();
      }
      // Range Input
      else if(this.format === 'range') {
        this.input_type = 'range';
        var min = this.schema.minimum || 0;
        var max = this.schema.maximum || Math.max(100,min+1);
        var step = 1;
        if(this.schema.multipleOf) {
          if(min%this.schema.multipleOf) min = Math.ceil(min/this.schema.multipleOf)*this.schema.multipleOf;
          if(max%this.schema.multipleOf) max = Math.floor(max/this.schema.multipleOf)*this.schema.multipleOf;
          step = this.schema.multipleOf;
        }

        this.input = this.theme.getRangeInput(min,max,step);
      }
      // Source Code
      else if([
          'actionscript',
          'batchfile',
          'bbcode',
          'c',
          'c++',
          'cpp',
          'coffee',
          'csharp',
          'css',
          'dart',
          'django',
          'ejs',
          'erlang',
          'golang',
          'groovy',
          'handlebars',
          'haskell',
          'haxe',
          'html',
          'ini',
          'jade',
          'java',
          'javascript',
          'json',
          'less',
          'lisp',
          'lua',
          'makefile',
          'markdown',
          'matlab',
          'mysql',
          'objectivec',
          'pascal',
          'perl',
          'pgsql',
          'php',
          'python',
          'r',
          'ruby',
          'sass',
          'scala',
          'scss',
          'smarty',
          'sql',
          'stylus',
          'svg',
          'twig',
          'vbscript',
          'xml',
          'yaml'
        ].indexOf(this.format) >= 0
      ) {
        this.input_type = this.format;
        this.source_code = true;

        this.input = this.theme.getTextareaInput();
      }
      // HTML5 Input type
      else {
        this.input_type = this.format;
        this.input = this.theme.getFormInputField(this.input_type);
      }
    }
    // Normal text input
    else {
      this.input_type = 'text';
      this.input = this.theme.getFormInputField(this.input_type);
    }

    // minLength, maxLength, and pattern
    if(typeof this.schema.maxLength !== "undefined") this.input.setAttribute('maxlength',this.schema.maxLength);
    if(typeof this.schema.pattern !== "undefined") this.input.setAttribute('pattern',this.schema.pattern);
    else if(typeof this.schema.minLength !== "undefined") this.input.setAttribute('pattern','.{'+this.schema.minLength+',}');

    if(this.options.compact) {
      this.container.className += ' compact';
    }
    else {
      if(this.options.input_width) this.input.style.width = this.options.input_width;
    }

    if(this.schema.readOnly || this.schema.readonly || this.schema.template) {
      this.always_disabled = true;
      this.input.disabled = true;
    }

    this.input
      .addEventListener('change',function(e) {
        e.preventDefault();
        e.stopPropagation();

        // Don't allow changing if this field is a template
        if(self.schema.template) {
          this.value = self.value;
          return;
        }

        var val = this.value;

        // sanitize value
        var sanitized = self.sanitize(val);
        if(val !== sanitized) {
          this.value = sanitized;
        }

        self.is_dirty = true;

        self.refreshValue();
        self.onChange(true);
      });

    if(this.options.input_height) this.input.style.height = this.options.input_height;
    if(this.options.expand_height) {
      this.adjust_height = function(el) {
        if(!el) return;
        var i, ch=el.offsetHeight;
        // Input too short
        if(el.offsetHeight < el.scrollHeight) {
          i=0;
          while(el.offsetHeight < el.scrollHeight+3) {
            if(i>100) break;
            i++;
            ch++;
            el.style.height = ch+'px';
          }
        }
        else {
          i=0;
          while(el.offsetHeight >= el.scrollHeight+3) {
            if(i>100) break;
            i++;
            ch--;
            el.style.height = ch+'px';
          }
          el.style.height = (ch+1)+'px';
        }
      };

      this.input.addEventListener('keyup',function(e) {
        self.adjust_height(this);
      });
      this.input.addEventListener('change',function(e) {
        self.adjust_height(this);
      });
      this.adjust_height();
    }

    if(this.format) this.input.setAttribute('data-schemaformat',this.format);

    this.control = this.theme.getFormControl(this.label, this.input, this.description);
    this.container.appendChild(this.control);

    // Any special formatting that needs to happen after the input is added to the dom
    window.requestAnimationFrame(function() {
      // Skip in case the input is only a temporary editor,
      // otherwise, in the case of an ace_editor creation,
      // it will generate an error trying to append it to the missing parentNode
      if(self.input.parentNode) self.afterInputReady();
      if(self.adjust_height) self.adjust_height(self.input);
    });

    // Compile and store the template
    if(this.schema.template) {
      this.template = this.jsoneditor.compileTemplate(this.schema.template, this.template_engine);
      this.refreshValue();
    }
    else {
      this.refreshValue();
    }
  },
  enable: function() {
    if(!this.always_disabled) {
      this.input.disabled = false;
      // TODO: WYSIWYG and Markdown editors
    }
    this._super();
  },
  disable: function() {
    this.input.disabled = true;
    // TODO: WYSIWYG and Markdown editors
    this._super();
  },
  afterInputReady: function() {
    var self = this, options;

    // Code editor
    if(this.source_code) {
      // WYSIWYG html and bbcode editor
      if(this.options.wysiwyg &&
        ['html','bbcode'].indexOf(this.input_type) >= 0 &&
        window.jQuery && window.jQuery.fn && window.jQuery.fn.sceditor
      ) {
        options = $extend({},{
          plugins: self.input_type==='html'? 'xhtml' : 'bbcode',
          emoticonsEnabled: false,
          width: '100%',
          height: 300
        },JSONEditor.plugins.sceditor,self.options.sceditor_options||{});

        window.jQuery(self.input).sceditor(options);

        self.sceditor_instance = window.jQuery(self.input).sceditor('instance');

        self.sceditor_instance.blur(function() {
          // Get editor's value
          var val = window.jQuery("<div>"+self.sceditor_instance.val()+"</div>");
          // Remove sceditor spans/divs
          window.jQuery('#sceditor-start-marker,#sceditor-end-marker,.sceditor-nlf',val).remove();
          // Set the value and update
          self.input.value = val.html();
          self.value = self.input.value;
          self.is_dirty = true;
          self.onChange(true);
        });
      }
      // SimpleMDE for markdown (if it's loaded)
      else if (this.input_type === 'markdown' && window.SimpleMDE) {
        options = $extend({},JSONEditor.plugins.SimpleMDE,{
          element: this.input
        });

        this.SimpleMDE = new window.SimpleMDE((options));

        this.SimpleMDE.codemirror.on("change",function() {
          self.value = self.SimpleMDE.value();
          self.is_dirty = true;
          self.onChange(true);
        });
      }
      // ACE editor for everything else
      else if(window.ace) {
        var mode = this.input_type;
        // aliases for c/cpp
        if(mode === 'cpp' || mode === 'c++' || mode === 'c') {
          mode = 'c_cpp';
        }

        this.ace_container = document.createElement('div');
        this.ace_container.style.width = '100%';
        this.ace_container.style.position = 'relative';
        this.ace_container.style.height = '400px';
        this.input.parentNode.insertBefore(this.ace_container,this.input);
        this.input.style.display = 'none';
        this.ace_editor = window.ace.edit(this.ace_container);

        this.ace_editor.setValue(this.getValue());

        // The theme
        if(JSONEditor.plugins.ace.theme) this.ace_editor.setTheme('ace/theme/'+JSONEditor.plugins.ace.theme);
        // The mode
        mode = window.ace.require("ace/mode/"+mode);
        if(mode) this.ace_editor.getSession().setMode(new mode.Mode());

        // Listen for changes
        this.ace_editor.on('change',function() {
          var val = self.ace_editor.getValue();
          self.input.value = val;
          self.refreshValue();
          self.is_dirty = true;
          self.onChange(true);
        });
      }
    }

    self.theme.afterInputReady(self.input);
  },
  refreshValue: function() {
    this.value = this.input.value;
    if(typeof this.value !== "string") this.value = '';
    this.serialized = this.value;
  },
  destroy: function() {
    // If using SCEditor, destroy the editor instance
    if(this.sceditor_instance) {
      this.sceditor_instance.destroy();
    }
    else if(this.SimpleMDE) {
      this.SimpleMDE.destroy();
    }
    else if(this.ace_editor) {
      this.ace_editor.destroy();
    }

    this.template = null;
    if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
    if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
    if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);

    this._super();
  },
  /**
   * This is overridden in derivative editors
   */
  sanitize: function(value) {
    return value;
  },
  /**
   * Re-calculates the value if needed
   */
  onWatchedFieldChange: function() {
    var self = this, vars, j;

    // If this editor needs to be rendered by a macro template
    if(this.template) {
      vars = this.getWatchedFieldValues();
      this.setValue(this.template(vars),false,true);
    }

    this._super();
  },
  showValidationErrors: function(errors) {
    var self = this;

    if(this.jsoneditor.options.show_errors === "always") {}
    else if(!this.is_dirty && this.previous_error_setting===this.jsoneditor.options.show_errors) return;

    this.previous_error_setting = this.jsoneditor.options.show_errors;

    var messages = [];
    $each(errors,function(i,error) {
      if(error.path === self.path) {
        messages.push(error.message);
      }
    });

    if(messages.length) {
      this.theme.addInputError(this.input, messages.join('. ')+'.');
    }
    else {
      this.theme.removeInputError(this.input);
    }
  }
});
<commit_msg>Fix disappearing text when moving block, credit @xmiao<commit_after>JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
  register: function() {
    this._super();
    if(!this.input) return;
    this.input.setAttribute('name',this.formname);
  },
  unregister: function() {
    this._super();
    if(!this.input) return;
    this.input.removeAttribute('name');
  },
  setValue: function(value,initial,from_template) {
    var self = this;

    if(this.template && !from_template) {
      return;
    }

    if(value === null || typeof value === 'undefined') value = "";
    else if(typeof value === "object") value = JSON.stringify(value);
    else if(typeof value !== "string") value = ""+value;

    if(value === this.serialized) return;

    // Sanitize value before setting it
    var sanitized = this.sanitize(value);

    if(this.input.value === sanitized) {
      return;
    }

    this.input.value = sanitized;

    // If using SCEditor, update the WYSIWYG
    if(this.sceditor_instance) {
      this.sceditor_instance.val(sanitized);
    }
    else if(this.SimpleMDE) {
      this.SimpleMDE.value(sanitized);
    }
    else if(this.ace_editor) {
      this.ace_editor.setValue(sanitized);
    }

    var changed = from_template || this.getValue() !== value;

    this.refreshValue();

    if(initial) this.is_dirty = false;
    else if(this.jsoneditor.options.show_errors === "change") this.is_dirty = true;

    if(this.adjust_height) this.adjust_height(this.input);

    // Bubble this setValue to parents if the value changed
    this.onChange(changed);
  },
  getNumColumns: function() {
    var min = Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);
    var num;

    if(this.input_type === 'textarea') num = 6;
    else if(['text','email'].indexOf(this.input_type) >= 0) num = 4;
    else num = 2;

    return Math.min(12,Math.max(min,num));
  },
  build: function() {
    var self = this, i;
    if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
    if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);

    this.format = this.schema.format;
    if(!this.format && this.schema.media && this.schema.media.type) {
      this.format = this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,'');
    }
    if(!this.format && this.options.default_format) {
      this.format = this.options.default_format;
    }
    if(this.options.format) {
      this.format = this.options.format;
    }

    // Specific format
    if(this.format) {
      // Text Area
      if(this.format === 'textarea') {
        this.input_type = 'textarea';
        this.input = this.theme.getTextareaInput();
      }
      // Range Input
      else if(this.format === 'range') {
        this.input_type = 'range';
        var min = this.schema.minimum || 0;
        var max = this.schema.maximum || Math.max(100,min+1);
        var step = 1;
        if(this.schema.multipleOf) {
          if(min%this.schema.multipleOf) min = Math.ceil(min/this.schema.multipleOf)*this.schema.multipleOf;
          if(max%this.schema.multipleOf) max = Math.floor(max/this.schema.multipleOf)*this.schema.multipleOf;
          step = this.schema.multipleOf;
        }

        this.input = this.theme.getRangeInput(min,max,step);
      }
      // Source Code
      else if([
          'actionscript',
          'batchfile',
          'bbcode',
          'c',
          'c++',
          'cpp',
          'coffee',
          'csharp',
          'css',
          'dart',
          'django',
          'ejs',
          'erlang',
          'golang',
          'groovy',
          'handlebars',
          'haskell',
          'haxe',
          'html',
          'ini',
          'jade',
          'java',
          'javascript',
          'json',
          'less',
          'lisp',
          'lua',
          'makefile',
          'markdown',
          'matlab',
          'mysql',
          'objectivec',
          'pascal',
          'perl',
          'pgsql',
          'php',
          'python',
          'r',
          'ruby',
          'sass',
          'scala',
          'scss',
          'smarty',
          'sql',
          'stylus',
          'svg',
          'twig',
          'vbscript',
          'xml',
          'yaml'
        ].indexOf(this.format) >= 0
      ) {
        this.input_type = this.format;
        this.source_code = true;

        this.input = this.theme.getTextareaInput();
      }
      // HTML5 Input type
      else {
        this.input_type = this.format;
        this.input = this.theme.getFormInputField(this.input_type);
      }
    }
    // Normal text input
    else {
      this.input_type = 'text';
      this.input = this.theme.getFormInputField(this.input_type);
    }

    // minLength, maxLength, and pattern
    if(typeof this.schema.maxLength !== "undefined") this.input.setAttribute('maxlength',this.schema.maxLength);
    if(typeof this.schema.pattern !== "undefined") this.input.setAttribute('pattern',this.schema.pattern);
    else if(typeof this.schema.minLength !== "undefined") this.input.setAttribute('pattern','.{'+this.schema.minLength+',}');

    if(this.options.compact) {
      this.container.className += ' compact';
    }
    else {
      if(this.options.input_width) this.input.style.width = this.options.input_width;
    }

    if(this.schema.readOnly || this.schema.readonly || this.schema.template) {
      this.always_disabled = true;
      this.input.disabled = true;
    }

    this.input
      .addEventListener('change',function(e) {
        e.preventDefault();
        e.stopPropagation();

        // Don't allow changing if this field is a template
        if(self.schema.template) {
          this.value = self.value;
          return;
        }

        var val = this.value;

        // sanitize value
        var sanitized = self.sanitize(val);
        if(val !== sanitized) {
          this.value = sanitized;
        }

        self.is_dirty = true;

        self.refreshValue();
        self.onChange(true);
      });

    if(this.options.input_height) this.input.style.height = this.options.input_height;
    if(this.options.expand_height) {
      this.adjust_height = function(el) {
        if(!el) return;
        var i, ch=el.offsetHeight;
        // Input too short
        if(el.offsetHeight < el.scrollHeight) {
          i=0;
          while(el.offsetHeight < el.scrollHeight+3) {
            if(i>100) break;
            i++;
            ch++;
            el.style.height = ch+'px';
          }
        }
        else {
          i=0;
          while(el.offsetHeight >= el.scrollHeight+3) {
            if(i>100) break;
            i++;
            ch--;
            el.style.height = ch+'px';
          }
          el.style.height = (ch+1)+'px';
        }
      };

      this.input.addEventListener('keyup',function(e) {
        self.adjust_height(this);
      });
      this.input.addEventListener('change',function(e) {
        self.adjust_height(this);
      });
      this.adjust_height();
    }

    if(this.format) this.input.setAttribute('data-schemaformat',this.format);

    this.control = this.theme.getFormControl(this.label, this.input, this.description);
    this.container.appendChild(this.control);

    // Any special formatting that needs to happen after the input is added to the dom
    window.requestAnimationFrame(function() {
      // Skip in case the input is only a temporary editor,
      // otherwise, in the case of an ace_editor creation,
      // it will generate an error trying to append it to the missing parentNode
      if(self.input.parentNode) self.afterInputReady();
      if(self.adjust_height) self.adjust_height(self.input);
    });

    // Compile and store the template
    if(this.schema.template) {
      this.template = this.jsoneditor.compileTemplate(this.schema.template, this.template_engine);
      this.refreshValue();
    }
    else {
      this.refreshValue();
    }
  },
  enable: function() {
    if(!this.always_disabled) {
      this.input.disabled = false;
      // TODO: WYSIWYG and Markdown editors
    }
    this._super();
  },
  disable: function() {
    this.input.disabled = true;
    // TODO: WYSIWYG and Markdown editors
    this._super();
  },
  afterInputReady: function() {
    var self = this, options;

    // Code editor
    if(this.source_code) {
      // WYSIWYG html and bbcode editor
      if(this.options.wysiwyg &&
        ['html','bbcode'].indexOf(this.input_type) >= 0 &&
        window.jQuery && window.jQuery.fn && window.jQuery.fn.sceditor
      ) {
        options = $extend({},{
          plugins: self.input_type==='html'? 'xhtml' : 'bbcode',
          emoticonsEnabled: false,
          width: '100%',
          height: 300
        },JSONEditor.plugins.sceditor,self.options.sceditor_options||{});

        window.jQuery(self.input).sceditor(options);

        self.sceditor_instance = window.jQuery(self.input).sceditor('instance');

        self.sceditor_instance.blur(function() {
          // Get editor's value
          var val = window.jQuery("<div>"+self.sceditor_instance.val()+"</div>");
          // Remove sceditor spans/divs
          window.jQuery('#sceditor-start-marker,#sceditor-end-marker,.sceditor-nlf',val).remove();
          // Set the value and update
          self.input.value = val.html();
          self.value = self.input.value;
          self.is_dirty = true;
          self.onChange(true);
        });
      }
      // SimpleMDE for markdown (if it's loaded)
      else if (this.input_type === 'markdown' && window.SimpleMDE) {
        options = $extend({},JSONEditor.plugins.SimpleMDE,{
          element: this.input
        });

        this.SimpleMDE = new window.SimpleMDE((options));

        this.SimpleMDE.codemirror.on("change",function() {
          self.value = self.SimpleMDE.value();
          self.is_dirty = true;
          self.onChange(true);
        });
      }
      // ACE editor for everything else
      else if(window.ace) {
        var mode = this.input_type;
        // aliases for c/cpp
        if(mode === 'cpp' || mode === 'c++' || mode === 'c') {
          mode = 'c_cpp';
        }

        this.ace_container = document.createElement('div');
        this.ace_container.style.width = '100%';
        this.ace_container.style.position = 'relative';
        this.ace_container.style.height = '400px';
        this.input.parentNode.insertBefore(this.ace_container,this.input);
        this.input.style.display = 'none';
        this.ace_editor = window.ace.edit(this.ace_container);

        this.ace_editor.setValue(this.getValue());

        // The theme
        if(JSONEditor.plugins.ace.theme) this.ace_editor.setTheme('ace/theme/'+JSONEditor.plugins.ace.theme);
        // The mode
        mode = window.ace.require("ace/mode/"+mode);
        if(mode) this.ace_editor.getSession().setMode(new mode.Mode());

        // Listen for changes
        this.ace_editor.on('change',function() {
          var val = self.ace_editor.getValue();
          self.input.value = val;
          self.refreshValue();
          self.is_dirty = true;
          self.onChange(true);
        });
      }
    }

    self.theme.afterInputReady(self.input);
  },
  refreshValue: function() {
    this.value = this.input.value;
    if(typeof this.value !== "string") this.value = '';
    this.serialized = this.value;
  },
  destroy: function() {
    // If using SCEditor, destroy the editor instance
    if(this.sceditor_instance) {
      this.sceditor_instance.destroy();
    }
    else if(this.SimpleMDE) {
      this.SimpleMDE.destroy();
    }
    else if(this.ace_editor) {
      this.ace_editor.destroy();
    }

    this.template = null;
    if(this.input && this.input.parentNode) this.input.parentNode.removeChild(this.input);
    if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
    if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);

    this._super();
  },
  /**
   * This is overridden in derivative editors
   */
  sanitize: function(value) {
    return value;
  },
  /**
   * Re-calculates the value if needed
   */
  onWatchedFieldChange: function() {
    var self = this, vars, j;

    // If this editor needs to be rendered by a macro template
    if(this.template) {
      vars = this.getWatchedFieldValues();
      this.setValue(this.template(vars),false,true);
    }

    this._super();
  },
  showValidationErrors: function(errors) {
    var self = this;

    if(this.jsoneditor.options.show_errors === "always") {}
    else if(!this.is_dirty && this.previous_error_setting===this.jsoneditor.options.show_errors) return;

    this.previous_error_setting = this.jsoneditor.options.show_errors;

    var messages = [];
    $each(errors,function(i,error) {
      if(error.path === self.path) {
        messages.push(error.message);
      }
    });

    if(messages.length) {
      this.theme.addInputError(this.input, messages.join('. ')+'.');
    }
    else {
      this.theme.removeInputError(this.input);
    }
  }
});
<|endoftext|>


<commit_before>import h from '../hyperscript.js';
import SiimpleComponent from '../index.js';
import SiimpleClose from './close.js';

//Alert component
export default class SiimpleAlert extends SiimpleComponent
{
  //Render the alert component
  render()
  {
    //Save this
    var self = this;

    //Initialize the list with the element classes
    var class_list = [ 'siimple-alert' ];

    //Check the color
    if(typeof this.props.color === 'string')
    {
      //Add the alert color
      class_list.push('siimple-alert--'  + this.props.color.toLowerCase().trim());
    }

    //Initialize the child elements list
    var children = [ h('span', {}, this.props.children) ];

    //Check if the close button is enabled
    if(this.props.showCloseButton === true)
    {
      //Add the close button element
      children.push(h(SiimpleClose, { onClick: self.props.onClose }));
    }

    //Create the alert element
    return h('div', { className: class_list, style: this.props.style }, children);
  }
}

//Alert default props
SiimpleAlert.defaultProps = { color: 'blue', showCloseButton: false, onClose: null, style: null };
<commit_msg>src/elements/alert.js: renamed close click property. Renamed show close button property<commit_after>import h from '../hyperscript.js';
import SiimpleComponent from '../index.js';
import SiimpleClose from './close.js';

//Alert component
export default class SiimpleAlert extends SiimpleComponent
{
  //Render the alert component
  render()
  {
    //Save this
    var self = this;

    //Initialize the list with the element classes
    var class_list = [ 'siimple-alert' ];

    //Check the color
    if(typeof this.props.color === 'string')
    {
      //Add the alert color
      class_list.push('siimple-alert--'  + this.props.color.toLowerCase().trim());
    }

    //Initialize the child elements list
    var children = [ h('span', {}, this.props.children) ];

    //Check if the close button is enabled
    if(this.props.showClose === true)
    {
      //Add the close button element
      children.push(h(SiimpleClose, { onClick: self.props.onCloseClick }));
    }

    //Create the alert element
    return h('div', { className: class_list, style: this.props.style }, children);
  }
}

//Alert default props
SiimpleAlert.defaultProps = { color: 'blue', showClose: false, onCloseClick: null, style: null };
<|endoftext|>


<commit_before>/**
 * OpenAPI content detector.
 *
 * @author Chris Spiliotopoulos
 */

import jsyaml from 'js-yaml';

export default (function() {

  // only allow the following
  // content types:
  const VALID_MIME_TYPES = [
    'text/html',
    'text/plain',
    'application/json',
    'text/yaml',
    'application/vnd.github+json',
    'application/vnd.github.v3+json',
    'application/vnd.github.v3.raw+json',
    'application/vnd.github.v3.text+json',
    'application/vnd.github.v3.html+json',
    'application/vnd.github.v3.full+json'
  ];

  const VALID_FILE_NAMES = [
    'api.json',
    'apis.json',
    'apis.yaml',
    'apis.txt'
  ];

  /**
   * Scans for possible OpenAPI spec.
   * @return {[type]} [description]
   */
  const _scan = function(document) {

    // get the document's content type
    const contentType = document.contentType;

    // get the outer text of the page body
    let content = document.body.textContent;
    const url = document.URL;

    return new Promise((resolve, reject) => {

      try {

        if (VALID_MIME_TYPES.indexOf(contentType) === -1) {
          throw new Error('Invalid content type');
        }

        const message = {
          channel: 'content',
          topic: 'catalog.apisjons.detected',
          data: {}
        };

        if (contentType === 'text/html') {

          const data = _scanHtmlFolLinkRelation(document);
          message.data = data;

          // return the message
          resolve(message);
        } else {

          // check if the resource name matches
          // any of the valid file names
          const res = VALID_FILE_NAMES.map((filename) => {
            if (url.toLowerCase().endsWith(filename)) {
              return true;
            }

            return false;
          });

          if (res.indexOf(true) === -1) {
            throw new Error('Not a valid APIS.json resource');
          }

          // is this a valid YAML or JSON document?
          content = jsyaml.load(content);

          // check for the 'swaggerVersion' attribute
          if ((typeof content.specificationVersion === 'undefined') ||
            (typeof content.name == 'undefined') ||
            (typeof content.url == 'undefined')) {
            throw new Error('Not a valid APIS.json resource');
          }

          // set the message data
          message.data = {
            url,
            name: content.name,
            image: content.image
          };

          // return the message
          resolve(message);

        }
      } catch (e) {
        reject(e);
      }
    });
  };

  /**
   * Scans the HTML content for
   * possible link relation that
   * points to an APIs.json catalog.
   * @param  {[type]} content [description]
   * @return {[type]}         [description]
   */
  const _scanHtmlFolLinkRelation = function(document) {

    const match = document.querySelector('link[rel=api]');

    if (!match) {
      throw new Error('no API link found');
    }

    // get the link attributes
    const url = match.getAttribute('href');
    const title = match.getAttribute('title');

    const data = {
      type: 'link',
      url,
      name: title
    };

    return data;
  };

  return {

    /*
     * Public
     */
    scan: _scan

  };

}());
<commit_msg>APIs.JSON content scanner should check for lower-case version of the properties as well<commit_after>/**
 * OpenAPI content detector.
 *
 * @author Chris Spiliotopoulos
 */

import jsyaml from 'js-yaml';
import _ from 'lodash';

export default (function() {

  // only allow the following
  // content types:
  const VALID_MIME_TYPES = [
    'text/html',
    'text/plain',
    'application/json',
    'text/yaml',
    'application/vnd.github+json',
    'application/vnd.github.v3+json',
    'application/vnd.github.v3.raw+json',
    'application/vnd.github.v3.text+json',
    'application/vnd.github.v3.html+json',
    'application/vnd.github.v3.full+json'
  ];

  const VALID_FILE_NAMES = [
    'api.json',
    'apis.json',
    'apis.yaml',
    'apis.txt'
  ];

  /**
   * Scans for possible OpenAPI spec.
   * @return {[type]} [description]
   */
  const _scan = function(document) {

    // get the document's content type
    const contentType = document.contentType;

    // get the outer text of the page body
    let content = document.body.textContent;
    const url = document.URL;

    return new Promise((resolve, reject) => {

      try {

        if (VALID_MIME_TYPES.indexOf(contentType) === -1) {
          throw new Error('Invalid content type');
        }

        const message = {
          channel: 'content',
          topic: 'catalog.apisjons.detected',
          data: {}
        };

        if (contentType === 'text/html') {

          const data = _scanHtmlFolLinkRelation(document);
          message.data = data;

          // return the message
          resolve(message);
        } else {

          // check if the resource name matches
          // any of the valid file names
          const res = VALID_FILE_NAMES.map((filename) => {
            if (url.toLowerCase().endsWith(filename)) {
              return true;
            }

            return false;
          });

          if (res.indexOf(true) === -1) {
            throw new Error('Not a valid APIS.json resource');
          }

          // is this a valid YAML or JSON document?
          content = jsyaml.load(content);

          // if array, get first element
          if (_.isArray(content)) {
            content = content[0];
          }

          // check for the 'swaggerVersion' attribute
          if (
            ((!_.has(content, 'specificationVersion')) && (!_.has(content, 'specificationversion'))) ||
            (typeof content.name == 'undefined') ||
            (typeof content.url == 'undefined')) {
            throw new Error('Not a valid APIS.json resource');
          }

          // set the message data
          message.data = {
            url,
            name: content.name,
            image: content.image
          };

          // return the message
          resolve(message);

        }
      } catch (e) {
        reject(e);
      }
    });
  };

  /**
   * Scans the HTML content for
   * possible link relation that
   * points to an APIs.json catalog.
   * @param  {[type]} content [description]
   * @return {[type]}         [description]
   */
  const _scanHtmlFolLinkRelation = function(document) {

    const match = document.querySelector('link[rel=api]');

    if (!match) {
      throw new Error('no API link found');
    }

    // get the link attributes
    const url = match.getAttribute('href');
    const title = match.getAttribute('title');

    const data = {
      type: 'link',
      url,
      name: title
    };

    return data;
  };

  return {

    /*
     * Public
     */
    scan: _scan

  };

}());
<|endoftext|>