<commit_before>define('app/controllers/machine_add', ['ember'],
    /**
     *  Machine Add Controller
     *
     *  @returns Class
     */
    function() {
        return Ember.Object.extend({

            /**
             *  Properties
             */

            callback: null,
            formReady: null,

            newMachineKey: null,
            newMachineName: null,
            newMachineSize: null,
            newMachineImage: null,
            newMachineScript: null,
            newMachineLocation: null,
            newMachineProvider: null,
            newMachineMonitoring: true,
            newMachineDockerCommand: null,
            newMachineDockerEnvironment: null,
            newMachineDockerPorts: null,
            newMachineAzurePorts: null,

            /**
             *
             *  Methods
             *
             */

            open: function(callback) {
                // In case page is scrolled, opening the
                // panel introduces an unpleasant view.
                // Scrolling to top fixes that
                $('#machine-create .docker').hide();
                $('#machine-create .azure').hide();
                $('#create-machine-location').addClass('ui-state-disabled');
                $('#create-machine-image').addClass('ui-state-disabled');
                $('#create-machine-size').addClass('ui-state-disabled');
                $('#create-machine-key').addClass('ui-state-disabled');
                $('#machine-create .ui-collapsible').collapsible('option', 'collapsedIcon', 'carat-d')
                    .collapsible('collapse');

                this._clear();
                this._updateFormReady();
                this.set('callback', callback);
            },

            close: function() {
                this._clear();
                $('#create-machine').collapsible('collapse');
            },

            add: function() {

                var providerName = this.newMachineProvider.title;
                var machineSize = this.newMachineSize.name;
                var machineImage = this.newMachineImage.name;
                var machineName = this.newMachineName;

                // Validate machine name
                // TODO: This thing is ugly. Move regex and strings into a dict

                if (providerName == 'NephoScale') {
                    var re = /^[0-9a-z_-]*$/;
                    if ( machineName.length > 64 || !re.test(machineName)) {

                        Mist.notificationController.timeNotify(
                            'Server name in NephoScale may have lower-case letters, numbers, hyphen (\'-\') and underscore (\'_\') characters, cannot exceed 64 ' +
                            'characters, and can end with a letter or a number.', 7000);
                        return;
                    }
                    if (machineSize.indexOf('CS025') > -1) {
                        if ((machineImage != 'Linux Ubuntu Server 10.04 LTS 64-bit') &&
                            (machineImage !='Linux CentOS 6.2 64-bit')) {

                                Mist.notificationController.timeNotify(
                                    'On CS025 size you can only create one of the two images: ' +
                                    'Linux Ubuntu Server 10.04 LTS 64-bit or Linux CentOS 6.2 64-bit', 7000);
                                return;
                        }
                    }
                }
                if (providerName == 'Azure') {
                    var re = /^[0-9a-zA-Z-]*$/;
                    if (!re.test(machineName)) {
                        Mist.notificationController.timeNotify('The name can contain only letters, numbers, and hyphens. The name must start with a letter and must end with a letter or a number.', 7000);
                        return;
                    }
                }
                if (providerName == 'Google Compute Engine') {
                    var re = /^[0-9a-z-]*$/;
                    if (!re.test(machineName)) {
                        Mist.notificationController.timeNotify('Name must be lowercase letters, numbers, and hyphens', 7000);
                        return;
                    }
                }
                if (providerName == 'SoftLayer') {
                    var re = /^[0-9a-zA-Z.-]*$/;
                    if (machineName.length > 253 || !re.test(machineName)) {
                        Mist.notificationController.timeNotify(
                            'Server name in Softlayer must be an alphanumeric string,' +
                            ' that may contain period (\'.\') and dash (\'-\') special characters.', 7000);
                        return;
                    }
                }

                var that = this;
                this.newMachineProvider.machines.newMachine(
                        this.newMachineName,
                        this.newMachineImage,
                        this.newMachineSize,
                        this.newMachineLocation,
                        this.newMachineKey,
                        this.newMachineScript,
                        this.newMachineMonitoring,
                        this.newMachineDockerEnvironment.trim(),
                        this.newMachineDockerCommand,
                        this.newMachineScriptParams,
                        this.newMachineDockerPorts,
                        this.newMachineAzurePorts,
                        function(success, machine) {
                            that._giveCallback(success, machine);
                        }
                );

                this.close();

                // Redirect to machine list view if user is in image list view
                if ($('#image-list-page').length) {
                    Mist.__container__.lookup('router:main').transitionTo('machines');
                }
            },

            /**
             *
             *  Pseudo-Private Methods
             *
             */

             _clear: function() {
                this.set('callback', null)
                    .set('newMachineName', '')
                    .set('newMachineScript', '')
                    .set('newMachineKey', {'id' : 'Select Key'})
                    .set('newMachineSize', {'name' : 'Select Size'})
                    .set('newMachineImage', {'name' : 'Select Image'})
                    .set('newMachineLocation', {'name' : 'Select Location'})
                    .set('newMachineProvider', {'title' : 'Select Provider'})
                    .set('newMachineDockerEnvironment', '')
                    .set('newMachineDockerCommand', '')
                    .set('newMachineScriptParams', '')
                    .set('newMachineDockerPorts', '')
                    .set('newMachineAzurePorts', '');
                this.view.clear();
             },

            _updateFormReady: function() {
                var formReady = false;
                if (this.newMachineName &&
                    this.newMachineSize.id &&
                    this.newMachineImage.id &&
                    this.newMachineProvider.id) {
                    formReady = true;
                }

                // SSH key and location are optional for docker
                if (this.newMachineProvider.provider != 'docker') {
                    if (Mist.keysController.keyExists(this.newMachineKey.id) &&
                        this.newMachineLocation.id) {
                        formReady = true;
                    } else {
                        formReady = false;
                    }
                }

                if (this.newMachineImage.id &&
                    this.newMachineImage.get('isMist')) {
                        if (!Mist.keysController.keyExists(this.newMachineKey.id)) formReady=false;
                }

                this.set('formReady', formReady);
            },

            _giveCallback: function(success, machine) {
                if (this.callback) this.callback(success, machine);
            },

            /**
             *
             *  Observers
             *
             */

            formObserver: function() {
                Ember.run.once(this, '_updateFormReady');
            }.observes('newMachineKey',
                       'newMachineName',
                       'newMachineSize',
                       'newMachineImage',
                       'newMachineScript',
                       'newMachineLocation',
                       'newMachineProvider')
        });
    }
);
<commit_msg>fix create machine formReady validation<commit_after>define('app/controllers/machine_add', ['ember'],
    /**
     *  Machine Add Controller
     *
     *  @returns Class
     */
    function() {
        return Ember.Object.extend({

            //
            //  Properties
            //

            callback: null,
            formReady: null,

            newMachineKey: null,
            newMachineName: null,
            newMachineSize: null,
            newMachineImage: null,
            newMachineScript: null,
            newMachineLocation: null,
            newMachineProvider: null,
            newMachineMonitoring: true,
            newMachineDockerCommand: null,
            newMachineDockerEnvironment: null,
            newMachineDockerPorts: null,
            newMachineAzurePorts: null,

            //
            //  Methods
            //

            open: function(callback) {
                // In case page is scrolled, opening the
                // panel introduces an unpleasant view.
                // Scrolling to top fixes that
                $('#machine-create .docker').hide();
                $('#machine-create .azure').hide();
                $('#create-machine-location').addClass('ui-state-disabled');
                $('#create-machine-image').addClass('ui-state-disabled');
                $('#create-machine-size').addClass('ui-state-disabled');
                $('#create-machine-key').addClass('ui-state-disabled');
                $('#machine-create .ui-collapsible').collapsible('option', 'collapsedIcon', 'carat-d')
                    .collapsible('collapse');

                this._clear();
                this._updateFormReady();
                this.set('callback', callback);
            },

            close: function() {
                this._clear();
                $('#create-machine').collapsible('collapse');
            },

            add: function() {
                var providerName = this.newMachineProvider.title;
                var machineSize = this.newMachineSize.name;
                var machineImage = this.newMachineImage.name;
                var machineName = this.newMachineName;

                // Validate machine name
                // TODO: This thing is ugly. Move regex and strings into a dict

                if (providerName == 'NephoScale') {
                    var re = /^[0-9a-z_-]*$/;
                    if ( machineName.length > 64 || !re.test(machineName)) {

                        Mist.notificationController.timeNotify(
                            'Server name in NephoScale may have lower-case letters, numbers, hyphen (\'-\') and underscore (\'_\') characters, cannot exceed 64 ' +
                            'characters, and can end with a letter or a number.', 7000);
                        return;
                    }
                    if (machineSize.indexOf('CS025') > -1) {
                        if ((machineImage != 'Linux Ubuntu Server 10.04 LTS 64-bit') &&
                            (machineImage !='Linux CentOS 6.2 64-bit')) {

                                Mist.notificationController.timeNotify(
                                    'On CS025 size you can only create one of the two images: ' +
                                    'Linux Ubuntu Server 10.04 LTS 64-bit or Linux CentOS 6.2 64-bit', 7000);
                                return;
                        }
                    }
                }
                if (providerName == 'Azure') {
                    var re = /^[0-9a-zA-Z-]*$/;
                    if (!re.test(machineName)) {
                        Mist.notificationController.timeNotify('The name can contain only letters, numbers, and hyphens. The name must start with a letter and must end with a letter or a number.', 7000);
                        return;
                    }
                }
                if (providerName == 'Google Compute Engine') {
                    var re = /^[0-9a-z-]*$/;
                    if (!re.test(machineName)) {
                        Mist.notificationController.timeNotify('Name must be lowercase letters, numbers, and hyphens', 7000);
                        return;
                    }
                }
                if (providerName == 'SoftLayer') {
                    var re = /^[0-9a-zA-Z.-]*$/;
                    if (machineName.length > 253 || !re.test(machineName)) {
                        Mist.notificationController.timeNotify(
                            'Server name in Softlayer must be an alphanumeric string,' +
                            ' that may contain period (\'.\') and dash (\'-\') special characters.', 7000);
                        return;
                    }
                }

                var that = this;
                this.newMachineProvider.machines.newMachine(
                        this.newMachineName,
                        this.newMachineImage,
                        this.newMachineSize,
                        this.newMachineLocation,
                        this.newMachineKey,
                        this.newMachineScript,
                        this.newMachineMonitoring,
                        this.newMachineDockerEnvironment.trim(),
                        this.newMachineDockerCommand,
                        this.newMachineScriptParams,
                        this.newMachineDockerPorts,
                        this.newMachineAzurePorts,
                        function(success, machine) {
                            that._giveCallback(success, machine);
                        }
                );

                this.close();

                // Redirect to machine list view if user is in image list view
                if ($('#image-list-page').length) {
                    Mist.__container__.lookup('router:main').transitionTo('machines');
                }
            },

            //
            //  Pseudo-Private Methods
            //

             _clear: function() {
                this.set('callback', null)
                    .set('newMachineName', '')
                    .set('newMachineScript', '')
                    .set('newMachineKey', {'id' : 'Select Key'})
                    .set('newMachineSize', {'name' : 'Select Size'})
                    .set('newMachineImage', {'name' : 'Select Image'})
                    .set('newMachineLocation', {'name' : 'Select Location'})
                    .set('newMachineProvider', {'title' : 'Select Provider'})
                    .set('newMachineDockerEnvironment', '')
                    .set('newMachineDockerCommand', '')
                    .set('newMachineScriptParams', '')
                    .set('newMachineDockerPorts', '')
                    .set('newMachineAzurePorts', '');
                this.view.clear();
             },

            _updateFormReady: function() {
                var formReady = false;
                if (this.newMachineName &&
                    this.newMachineSize.id &&
                    this.newMachineImage.id &&
                    this.newMachineProvider.id) {
                    formReady = true;
                }

                // SSH key and location are optional for docker
                if (this.newMachineProvider.provider != 'docker') {
                    if (!(Mist.keysController.keyExists(this.newMachineKey.id) &&
                        this.newMachineLocation.id)) {
                        formReady = false;
                    }
                }

                if (this.newMachineImage.id &&
                    this.newMachineImage.get('isMist')) {
                        if (!Mist.keysController.keyExists(this.newMachineKey.id))
                            formReady=false;
                }

                this.set('formReady', formReady);
            },

            _giveCallback: function(success, machine) {
                if (this.callback) this.callback(success, machine);
            },

            //
            //  Observers
            //

            formObserver: function() {
                Ember.run.once(this, '_updateFormReady');
            }.observes('newMachineKey',
                       'newMachineName',
                       'newMachineSize',
                       'newMachineImage',
                       'newMachineScript',
                       'newMachineLocation',
                       'newMachineProvider')
        });
    }
);
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @format
 * @flow
 */

'use strict';

import type {BlockMap} from 'BlockMap';
import type CharacterMetadata from 'CharacterMetadata';
import type ContentState from 'ContentState';
import type SelectionState from 'SelectionState';

const ContentBlockNode = require('ContentBlockNode');

const getNextDelimiterBlockKey = require('getNextDelimiterBlockKey');
const Immutable = require('immutable');

const {List, Map} = Immutable;

const transformBlock = (
  key: ?string,
  blockMap: BlockMap,
  func: (block: ContentBlockNode) => ContentBlockNode,
): void => {
  if (!key) {
    return;
  }

  const block = blockMap.get(key);

  if (!block) {
    return;
  }

  blockMap.set(key, func(block));
};

/**
 * Ancestors needs to be preserved when there are non selected
 * children to make sure we do not leave any orphans behind
 */
const getAncestorsKeys = (
  blockKey: ?string,
  blockMap: BlockMap,
): Array<string> => {
  const parents = [];

  if (!blockKey) {
    return parents;
  }

  let blockNode = blockMap.get(blockKey);
  while (blockNode && blockNode.getParentKey()) {
    const parentKey = blockNode.getParentKey();
    if (parentKey) {
      parents.push(parentKey);
    }
    blockNode = parentKey ? blockMap.get(parentKey) : null;
  }

  return parents;
};

/**
 * Get all next delimiter keys until we hit a root delimiter and return
 * an array of key references
 */
const getNextDelimitersBlockKeys = (
  block: ContentBlockNode,
  blockMap: BlockMap,
): Array<string> => {
  const nextDelimiters = [];

  if (!block) {
    return nextDelimiters;
  }

  let nextDelimiter = getNextDelimiterBlockKey(block, blockMap);
  while (nextDelimiter && blockMap.get(nextDelimiter)) {
    const block = blockMap.get(nextDelimiter);
    nextDelimiters.push(nextDelimiter);

    // we do not need to keep checking all root node siblings, just the first occurance
    nextDelimiter = block.getParentKey()
      ? getNextDelimiterBlockKey(block, blockMap)
      : null;
  }

  return nextDelimiters;
};

const getNextValidSibling = (
  block: ?ContentBlockNode,
  blockMap: BlockMap,
  originalBlockMap: BlockMap,
): ?string => {
  if (!block) {
    return null;
  }

  // note that we need to make sure we refer to the original block since this
  // function is called within a withMutations
  let nextValidSiblingKey = originalBlockMap
    .get(block.getKey())
    .getNextSiblingKey();

  while (nextValidSiblingKey && !blockMap.get(nextValidSiblingKey)) {
    nextValidSiblingKey =
      originalBlockMap.get(nextValidSiblingKey).getNextSiblingKey() || null;
  }

  return nextValidSiblingKey;
};

const getPrevValidSibling = (
  block: ?ContentBlockNode,
  blockMap: BlockMap,
  originalBlockMap: BlockMap,
): ?string => {
  if (!block) {
    return null;
  }

  // note that we need to make sure we refer to the original block since this
  // function is called within a withMutations
  let prevValidSiblingKey = originalBlockMap
    .get(block.getKey())
    .getPrevSiblingKey();

  while (prevValidSiblingKey && !blockMap.get(prevValidSiblingKey)) {
    prevValidSiblingKey =
      originalBlockMap.get(prevValidSiblingKey).getPrevSiblingKey() || null;
  }

  return prevValidSiblingKey;
};

const updateBlockMapLinks = (
  blockMap: BlockMap,
  startBlock: ContentBlockNode,
  endBlock: ContentBlockNode,
  originalBlockMap: BlockMap,
): BlockMap => {
  return blockMap.withMutations(blocks => {
    // update start block if its retained
    transformBlock(startBlock.getKey(), blocks, block =>
      block.merge({
        nextSibling: getNextValidSibling(startBlock, blocks, originalBlockMap),
        prevSibling: getPrevValidSibling(startBlock, blocks, originalBlockMap),
      }),
    );

    // update endblock if its retained
    transformBlock(endBlock.getKey(), blocks, block =>
      block.merge({
        nextSibling: getNextValidSibling(endBlock, blocks, originalBlockMap),
        prevSibling: getPrevValidSibling(endBlock, blocks, originalBlockMap),
      }),
    );

    // update start block parent ancestors
    getAncestorsKeys(startBlock.getKey(), originalBlockMap).forEach(parentKey =>
      transformBlock(parentKey, blocks, block =>
        block.merge({
          children: block.getChildKeys().filter(key => blocks.get(key)),
          nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
          prevSibling: getPrevValidSibling(block, blocks, originalBlockMap),
        }),
      ),
    );

    // update start block next - can only happen if startBlock == endBlock
    transformBlock(startBlock.getNextSiblingKey(), blocks, block =>
      block.merge({
        prevSibling: startBlock.getPrevSiblingKey(),
      }),
    );

    // update start block prev
    transformBlock(startBlock.getPrevSiblingKey(), blocks, block =>
      block.merge({
        nextSibling: getNextValidSibling(startBlock, blocks, originalBlockMap),
      }),
    );

    // update end block next
    transformBlock(endBlock.getNextSiblingKey(), blocks, block =>
      block.merge({
        prevSibling: getPrevValidSibling(endBlock, blocks, originalBlockMap),
      }),
    );

    // update end block prev
    transformBlock(endBlock.getPrevSiblingKey(), blocks, block =>
      block.merge({
        nextSibling: endBlock.getNextSiblingKey(),
      }),
    );

    // update end block parent ancestors
    getAncestorsKeys(endBlock.getKey(), originalBlockMap).forEach(parentKey => {
      transformBlock(parentKey, blocks, block =>
        block.merge({
          children: block.getChildKeys().filter(key => blocks.get(key)),
          nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
          prevSibling: getPrevValidSibling(block, blocks, originalBlockMap),
        }),
      );
    });

    // update next delimiters all the way to a root delimiter
    getNextDelimitersBlockKeys(endBlock, originalBlockMap).forEach(
      delimiterKey =>
        transformBlock(delimiterKey, blocks, block =>
          block.merge({
            nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
            prevSibling: getPrevValidSibling(block, blocks, originalBlockMap),
          }),
        ),
    );

    // if parent (startBlock) was deleted
    if (
      blockMap.get(startBlock.getKey()) == null &&
      blockMap.get(endBlock.getKey()) != null &&
      endBlock.getParentKey() === startBlock.getKey() &&
      endBlock.getPrevSiblingKey() == null
    ) {
      const prevSiblingKey = startBlock.getPrevSiblingKey();
      // endBlock becomes next sibling of parent's prevSibling
      transformBlock(endBlock.getKey(), blocks, block =>
        block.merge({
          prevSibling: prevSiblingKey,
        }),
      );
      transformBlock(prevSiblingKey, blocks, block =>
        block.merge({
          nextSibling: endBlock.getKey(),
        }),
      );

      // Update parent for previous parent's children, and children for that parent
      const prevSibling = prevSiblingKey ? blockMap.get(prevSiblingKey) : null;
      const newParentKey = prevSibling ? prevSibling.getParentKey() : null;
      startBlock.getChildKeys().forEach(childKey => {
        transformBlock(childKey, blocks, block =>
          block.merge({
            parent: newParentKey, // set to null if there is no parent
          }),
        );
      });
      if (newParentKey != null) {
        const newParent = blockMap.get(newParentKey);
        transformBlock(newParentKey, blocks, block =>
          block.merge({
            children: newParent
              .getChildKeys()
              .concat(startBlock.getChildKeys()),
          }),
        );
      }

      // last child of deleted parent should point to next sibling
      transformBlock(
        startBlock.getChildKeys().find(key => {
          const block = (blockMap.get(key): ContentBlockNode);
          return block.getNextSiblingKey() === null;
        }),
        blocks,
        block =>
          block.merge({
            nextSibling: startBlock.getNextSiblingKey(),
          }),
      );
    }
  });
};

const removeRangeFromContentState = (
  contentState: ContentState,
  selectionState: SelectionState,
): ContentState => {
  if (selectionState.isCollapsed()) {
    return contentState;
  }

  const blockMap = contentState.getBlockMap();
  const startKey = selectionState.getStartKey();
  const startOffset = selectionState.getStartOffset();
  const endKey = selectionState.getEndKey();
  const endOffset = selectionState.getEndOffset();

  const startBlock = blockMap.get(startKey);
  const endBlock = blockMap.get(endKey);

  // we assume that ContentBlockNode and ContentBlocks are not mixed together
  const isExperimentalTreeBlock = startBlock instanceof ContentBlockNode;

  // used to retain blocks that should not be deleted to avoid orphan children
  let parentAncestors = [];

  if (isExperimentalTreeBlock) {
    const endBlockchildrenKeys = endBlock.getChildKeys();
    const endBlockAncestors = getAncestorsKeys(endKey, blockMap);

    // endBlock has unselected siblings so we can not remove its ancestors parents
    if (endBlock.getNextSiblingKey()) {
      parentAncestors = parentAncestors.concat(endBlockAncestors);
    }

    // endBlock has children so can not remove this block or any of its ancestors
    if (!endBlockchildrenKeys.isEmpty()) {
      parentAncestors = parentAncestors.concat(
        endBlockAncestors.concat([endKey]),
      );
    }

    // we need to retain all ancestors of the next delimiter block
    parentAncestors = parentAncestors.concat(
      getAncestorsKeys(getNextDelimiterBlockKey(endBlock, blockMap), blockMap),
    );
  }

  let characterList;

  if (startBlock === endBlock) {
    characterList = removeFromList(
      startBlock.getCharacterList(),
      startOffset,
      endOffset,
    );
  } else {
    characterList = startBlock
      .getCharacterList()
      .slice(0, startOffset)
      .concat(endBlock.getCharacterList().slice(endOffset));
  }

  const modifiedStart = startBlock.merge({
    text:
      startBlock.getText().slice(0, startOffset) +
      endBlock.getText().slice(endOffset),
    characterList,
  });

  // If cursor (collapsed) is at the start of the first child, delete parent
  // instead of child
  const shouldDeleteParent =
    isExperimentalTreeBlock &&
    startOffset === 0 &&
    endOffset === 0 &&
    endBlock.getParentKey() === startKey &&
    endBlock.getPrevSiblingKey() == null;
  const newBlocks = shouldDeleteParent
    ? Map([[startKey, null]])
    : blockMap
        .toSeq()
        .skipUntil((_, k) => k === startKey)
        .takeUntil((_, k) => k === endKey)
        .filter((_, k) => parentAncestors.indexOf(k) === -1)
        .concat(Map([[endKey, null]]))
        .map((_, k) => {
          return k === startKey ? modifiedStart : null;
        });
  let updatedBlockMap = blockMap.merge(newBlocks).filter(block => !!block);

  // Only update tree block pointers if the range is across blocks
  if (isExperimentalTreeBlock && startBlock !== endBlock) {
    updatedBlockMap = updateBlockMapLinks(
      updatedBlockMap,
      startBlock,
      endBlock,
      blockMap,
    );
  }

  return contentState.merge({
    blockMap: updatedBlockMap,
    selectionBefore: selectionState,
    selectionAfter: selectionState.merge({
      anchorKey: startKey,
      anchorOffset: startOffset,
      focusKey: startKey,
      focusOffset: startOffset,
      isBackward: false,
    }),
  });
};

/**
 * Maintain persistence for target list when removing characters on the
 * head and tail of the character list.
 */
const removeFromList = (
  targetList: List<CharacterMetadata>,
  startOffset: number,
  endOffset: number,
): List<CharacterMetadata> => {
  if (startOffset === 0) {
    while (startOffset < endOffset) {
      targetList = targetList.shift();
      startOffset++;
    }
  } else if (endOffset === targetList.count()) {
    while (endOffset > startOffset) {
      targetList = targetList.pop();
      endOffset--;
    }
  } else {
    const head = targetList.slice(0, startOffset);
    const tail = targetList.slice(endOffset);
    targetList = head.concat(tail).toList();
  }
  return targetList;
};

module.exports = removeRangeFromContentState;
<commit_msg>2/n splitting PR #1828: updating removeRangeFromContentState (#1828)<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @format
 * @flow
 */

'use strict';

import type {BlockMap} from 'BlockMap';
import type CharacterMetadata from 'CharacterMetadata';
import type ContentState from 'ContentState';
import type SelectionState from 'SelectionState';

const ContentBlockNode = require('ContentBlockNode');

const getNextDelimiterBlockKey = require('getNextDelimiterBlockKey');
const Immutable = require('immutable');

const {List, Map} = Immutable;

const transformBlock = (
  key: ?string,
  blockMap: BlockMap,
  func: (block: ContentBlockNode) => ContentBlockNode,
): void => {
  if (!key) {
    return;
  }

  const block = blockMap.get(key);

  if (!block) {
    return;
  }

  blockMap.set(key, func(block));
};

/**
 * Ancestors needs to be preserved when there are non selected
 * children to make sure we do not leave any orphans behind
 */
const getAncestorsKeys = (
  blockKey: ?string,
  blockMap: BlockMap,
): Array<string> => {
  const parents = [];

  if (!blockKey) {
    return parents;
  }

  let blockNode = blockMap.get(blockKey);
  while (blockNode && blockNode.getParentKey()) {
    const parentKey = blockNode.getParentKey();
    if (parentKey) {
      parents.push(parentKey);
    }
    blockNode = parentKey ? blockMap.get(parentKey) : null;
  }

  return parents;
};

/**
 * Get all next delimiter keys until we hit a root delimiter and return
 * an array of key references
 */
const getNextDelimitersBlockKeys = (
  block: ContentBlockNode,
  blockMap: BlockMap,
): Array<string> => {
  const nextDelimiters = [];

  if (!block) {
    return nextDelimiters;
  }

  let nextDelimiter = getNextDelimiterBlockKey(block, blockMap);
  while (nextDelimiter && blockMap.get(nextDelimiter)) {
    const block = blockMap.get(nextDelimiter);
    nextDelimiters.push(nextDelimiter);

    // we do not need to keep checking all root node siblings, just the first occurance
    nextDelimiter = block.getParentKey()
      ? getNextDelimiterBlockKey(block, blockMap)
      : null;
  }

  return nextDelimiters;
};

const getNextValidSibling = (
  block: ?ContentBlockNode,
  blockMap: BlockMap,
  originalBlockMap: BlockMap,
): ?string => {
  if (!block) {
    return null;
  }

  // note that we need to make sure we refer to the original block since this
  // function is called within a withMutations
  let nextValidSiblingKey = originalBlockMap
    .get(block.getKey())
    .getNextSiblingKey();

  while (nextValidSiblingKey && !blockMap.get(nextValidSiblingKey)) {
    nextValidSiblingKey =
      originalBlockMap.get(nextValidSiblingKey).getNextSiblingKey() || null;
  }

  return nextValidSiblingKey;
};

const getPrevValidSibling = (
  block: ?ContentBlockNode,
  blockMap: BlockMap,
  originalBlockMap: BlockMap,
): ?string => {
  if (!block) {
    return null;
  }

  // note that we need to make sure we refer to the original block since this
  // function is called within a withMutations
  let prevValidSiblingKey = originalBlockMap
    .get(block.getKey())
    .getPrevSiblingKey();

  while (prevValidSiblingKey && !blockMap.get(prevValidSiblingKey)) {
    prevValidSiblingKey =
      originalBlockMap.get(prevValidSiblingKey).getPrevSiblingKey() || null;
  }

  return prevValidSiblingKey;
};

const updateBlockMapLinks = (
  blockMap: BlockMap,
  startBlock: ContentBlockNode,
  endBlock: ContentBlockNode,
  originalBlockMap: BlockMap,
): BlockMap => {
  return blockMap.withMutations(blocks => {
    // update start block if its retained
    transformBlock(startBlock.getKey(), blocks, block =>
      block.merge({
        nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
        prevSibling: getPrevValidSibling(block, blocks, originalBlockMap),
      }),
    );

    // update endblock if its retained
    transformBlock(endBlock.getKey(), blocks, block =>
      block.merge({
        nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
        prevSibling: getPrevValidSibling(block, blocks, originalBlockMap),
      }),
    );

    // update start block parent ancestors
    getAncestorsKeys(startBlock.getKey(), originalBlockMap).forEach(parentKey =>
      transformBlock(parentKey, blocks, block =>
        block.merge({
          children: block.getChildKeys().filter(key => blocks.get(key)),
          nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
          prevSibling: getPrevValidSibling(block, blocks, originalBlockMap),
        }),
      ),
    );

    // update start block next - can only happen if startBlock == endBlock
    transformBlock(startBlock.getNextSiblingKey(), blocks, block =>
      block.merge({
        prevSibling: startBlock.getPrevSiblingKey(),
      }),
    );

    // update start block prev
    transformBlock(startBlock.getPrevSiblingKey(), blocks, block =>
      block.merge({
        nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
      }),
    );

    // update end block next
    transformBlock(endBlock.getNextSiblingKey(), blocks, block =>
      block.merge({
        prevSibling: getPrevValidSibling(block, blocks, originalBlockMap),
      }),
    );

    // update end block prev
    transformBlock(endBlock.getPrevSiblingKey(), blocks, block =>
      block.merge({
        nextSibling: endBlock.getNextSiblingKey(),
      }),
    );

    // update end block parent ancestors
    getAncestorsKeys(endBlock.getKey(), originalBlockMap).forEach(parentKey => {
      transformBlock(parentKey, blocks, block =>
        block.merge({
          children: block.getChildKeys().filter(key => blocks.get(key)),
          nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
          prevSibling: getPrevValidSibling(block, blocks, originalBlockMap),
        }),
      );
    });

    // update next delimiters all the way to a root delimiter
    getNextDelimitersBlockKeys(endBlock, originalBlockMap).forEach(
      delimiterKey =>
        transformBlock(delimiterKey, blocks, block =>
          block.merge({
            nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
            prevSibling: getPrevValidSibling(block, blocks, originalBlockMap),
          }),
        ),
    );

    // if parent (startBlock) was deleted
    if (
      blockMap.get(startBlock.getKey()) == null &&
      blockMap.get(endBlock.getKey()) != null &&
      endBlock.getParentKey() === startBlock.getKey() &&
      endBlock.getPrevSiblingKey() == null
    ) {
      const prevSiblingKey = startBlock.getPrevSiblingKey();
      // endBlock becomes next sibling of parent's prevSibling
      transformBlock(endBlock.getKey(), blocks, block =>
        block.merge({
          prevSibling: prevSiblingKey,
        }),
      );
      transformBlock(prevSiblingKey, blocks, block =>
        block.merge({
          nextSibling: endBlock.getKey(),
        }),
      );

      // Update parent for previous parent's children, and children for that parent
      const prevSibling = prevSiblingKey ? blockMap.get(prevSiblingKey) : null;
      const newParentKey = prevSibling ? prevSibling.getParentKey() : null;
      startBlock.getChildKeys().forEach(childKey => {
        transformBlock(childKey, blocks, block =>
          block.merge({
            parent: newParentKey, // set to null if there is no parent
          }),
        );
      });
      if (newParentKey != null) {
        const newParent = blockMap.get(newParentKey);
        transformBlock(newParentKey, blocks, block =>
          block.merge({
            children: newParent
              .getChildKeys()
              .concat(startBlock.getChildKeys()),
          }),
        );
      }

      // last child of deleted parent should point to next sibling
      transformBlock(
        startBlock.getChildKeys().find(key => {
          const block = (blockMap.get(key): ContentBlockNode);
          return block.getNextSiblingKey() === null;
        }),
        blocks,
        block =>
          block.merge({
            nextSibling: startBlock.getNextSiblingKey(),
          }),
      );
    }
  });
};

const removeRangeFromContentState = (
  contentState: ContentState,
  selectionState: SelectionState,
): ContentState => {
  if (selectionState.isCollapsed()) {
    return contentState;
  }

  const blockMap = contentState.getBlockMap();
  const startKey = selectionState.getStartKey();
  const startOffset = selectionState.getStartOffset();
  const endKey = selectionState.getEndKey();
  const endOffset = selectionState.getEndOffset();

  const startBlock = blockMap.get(startKey);
  const endBlock = blockMap.get(endKey);

  // we assume that ContentBlockNode and ContentBlocks are not mixed together
  const isExperimentalTreeBlock = startBlock instanceof ContentBlockNode;

  // used to retain blocks that should not be deleted to avoid orphan children
  let parentAncestors = [];

  if (isExperimentalTreeBlock) {
    const endBlockchildrenKeys = endBlock.getChildKeys();
    const endBlockAncestors = getAncestorsKeys(endKey, blockMap);

    // endBlock has unselected siblings so we can not remove its ancestors parents
    if (endBlock.getNextSiblingKey()) {
      parentAncestors = parentAncestors.concat(endBlockAncestors);
    }

    // endBlock has children so can not remove this block or any of its ancestors
    if (!endBlockchildrenKeys.isEmpty()) {
      parentAncestors = parentAncestors.concat(
        endBlockAncestors.concat([endKey]),
      );
    }

    // we need to retain all ancestors of the next delimiter block
    parentAncestors = parentAncestors.concat(
      getAncestorsKeys(getNextDelimiterBlockKey(endBlock, blockMap), blockMap),
    );
  }

  let characterList;

  if (startBlock === endBlock) {
    characterList = removeFromList(
      startBlock.getCharacterList(),
      startOffset,
      endOffset,
    );
  } else {
    characterList = startBlock
      .getCharacterList()
      .slice(0, startOffset)
      .concat(endBlock.getCharacterList().slice(endOffset));
  }

  const modifiedStart = startBlock.merge({
    text:
      startBlock.getText().slice(0, startOffset) +
      endBlock.getText().slice(endOffset),
    characterList,
  });

  // If cursor (collapsed) is at the start of the first child, delete parent
  // instead of child
  const shouldDeleteParent =
    isExperimentalTreeBlock &&
    startOffset === 0 &&
    endOffset === 0 &&
    endBlock.getParentKey() === startKey &&
    endBlock.getPrevSiblingKey() == null;
  const newBlocks = shouldDeleteParent
    ? Map([[startKey, null]])
    : blockMap
        .toSeq()
        .skipUntil((_, k) => k === startKey)
        .takeUntil((_, k) => k === endKey)
        .filter((_, k) => parentAncestors.indexOf(k) === -1)
        .concat(Map([[endKey, null]]))
        .map((_, k) => {
          return k === startKey ? modifiedStart : null;
        });
  let updatedBlockMap = blockMap.merge(newBlocks).filter(block => !!block);

  // Only update tree block pointers if the range is across blocks
  if (isExperimentalTreeBlock && startBlock !== endBlock) {
    updatedBlockMap = updateBlockMapLinks(
      updatedBlockMap,
      startBlock,
      endBlock,
      blockMap,
    );
  }

  return contentState.merge({
    blockMap: updatedBlockMap,
    selectionBefore: selectionState,
    selectionAfter: selectionState.merge({
      anchorKey: startKey,
      anchorOffset: startOffset,
      focusKey: startKey,
      focusOffset: startOffset,
      isBackward: false,
    }),
  });
};

/**
 * Maintain persistence for target list when removing characters on the
 * head and tail of the character list.
 */
const removeFromList = (
  targetList: List<CharacterMetadata>,
  startOffset: number,
  endOffset: number,
): List<CharacterMetadata> => {
  if (startOffset === 0) {
    while (startOffset < endOffset) {
      targetList = targetList.shift();
      startOffset++;
    }
  } else if (endOffset === targetList.count()) {
    while (endOffset > startOffset) {
      targetList = targetList.pop();
      endOffset--;
    }
  } else {
    const head = targetList.slice(0, startOffset);
    const tail = targetList.slice(endOffset);
    targetList = head.concat(tail).toList();
  }
  return targetList;
};

module.exports = removeRangeFromContentState;
<|endoftext|>


<commit_before>/*
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You 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.
*/

/******************************************
 *  NavNode
 ******************************************/

function NavNode(id, parent) {
    this.id = id;
    this.parent = parent;
    this.items = {};
    this.isfolder = false;
    this.uuid = '';
    this.href = '';
    this.label = '';
    this.area = '';
    this.path = '';
    this.isprotected = true;
    this.existsChosenLanguage = true;
    this.langSuffix = '';
};

NavNode.prototype = new LenyaNode;

NavNode.prototype.createNewNode = function(node)
{
    var newItem = new NavNode(node.getAttribute('id'), this);
    newItem.init();

    newItem.isfolder = isNodeFolder(node);
    newItem.area = this.area;
    newItem.path = node.getAttribute('basic-url');
    newItem.uuid = node.getAttribute('uuid');
    newItem.isprotected = isNodeProtected(node);
    newItem.href = node.getAttribute('href');
    newItem.label = getLabel(node);
    newItem.existsChosenLanguage = existsChosenLanguage(node);
    newItem.langSuffix = node.getAttribute('language-suffix');

    return newItem;
}

NavNode.prototype.getLoadSubTreeURL = function() {
    area = this.area;
    path = this.path;
    return encodeURI(CONTEXT_PREFIX + '/' + PUBLICATION_ID + PIPELINE_PATH + '?area='+area+'&path='+path+'&language='+CHOSEN_LANGUAGE+'&areas='+ALL_AREAS+'&lenya.module=sitetree');
}

NavNode.prototype.getStyle = function() {
    if (this.tree.root == this) {
        return 'lenya-info-root';
    } else if (this.isprotected) {
        return 'lenya-info-protected';
    } else if (this.path == CUT_DOCUMENT_ID) {
        return 'lenya-info-cut';
    } else if (!this.existsChosenLanguage) {
        return 'lenya-info-nolanguage';
    } else {
        return 'treenode_label';
    }
}

function getLabel(node) {
    var cs = node.childNodes;
    var l = cs.length;
    // lenya generates the xml and is responsible to insert the label
    // of the correct language
    for (var i = 0; i < l; i++) {
       if (getTagName(cs[i]) =='nav:label') {
          if (cs[i].hasChildNodes()) {
              return cs[i].firstChild.nodeValue;
          }
          else {
              return "[no title]";
          }
       }
    }
    return '';
}

function isNodeProtected(node) {
    var prot = node.getAttribute('protected');
    if (prot == 'true') return true;
    return false;
}

function isNodeFolder(node) {
    var isfolder = node.getAttribute('folder');
    if (isfolder == 'true') return true;
    return false;
}

// check if the node has a label of the chosen language
function existsChosenLanguage(node) {
    var children = node.childNodes;
    for (var i = 0; i < children.length; i++) {
       if (getTagName(children[i]) =='nav:label' && children[i].getAttribute('xml:lang')==CHOSEN_LANGUAGE) {
          return true;
       }
    }
    return false;
}

/******************************************
 *  NavTree
 ******************************************/

function NavTree(doc, treeElement) {
    this.doc = doc;
    this.treeElement = treeElement;
    this.selected = null;
};

NavTree.prototype = new LenyaTree;

NavTree.prototype.init = function(id) {
    this.root = new NavNode(id);
    this.root.tree = this;
    this.root.depth = 0;
    this.root.reopen = false;
    this.root.isprotected = false;
    this.root.isfolder = true;
    this._currentId = 0;
};

NavTree.prototype.loadInitialTree = function(area, path) {
    var url = encodeURI(CONTEXT_PREFIX + '/' + PUBLICATION_ID + PIPELINE_PATH + '?area='+area+'&path='+path+'&language='+CHOSEN_LANGUAGE+'&initial=true&areas='+ALL_AREAS+'&lenya.module=sitetree');

    callback = function(fragment, param) {
        var tree = param[0];
        var area = param[1];
        var path = param[2];
        tree.initialTreeLoaded(fragment);
        var selectedItem = tree.getItemByPath(PUBLICATION_ID+'/'+area+path)
        if (selectedItem != false) {
            tree.select(selectedItem);
        }
    }

    var param = new Array(this, area, path);
    loadAsyncXML(url, callback, param);
};

NavTree.prototype.initialTreeLoaded = function(xml)
{
  var root = xml.documentElement;
  var children = root.childNodes;
  var items=[];
  var item;
  for (var i = 0; i < children.length; i++) {
     if (getTagName(children[i]) == "nav:site") {
        item = this.addLoadedSite(children[i]);
        items.push(item);
        item.addNodesRec(children[i]);
     }
  }
  this.root.addItems(items);
  this.root.open();
}

NavTree.prototype.addLoadedSite = function(site)
{
  var langSuffix = '';
  if (CHOSEN_LANGUAGE!=DEFAULT_LANGUAGE) langSuffix = '_'+CHOSEN_LANGUAGE;

  var siteArea = site.getAttribute('area');
  var newSite = new NavNode(siteArea, this.root);
  newSite.init();

  newSite.isfolder = isNodeFolder(site);
  newSite.area = siteArea;
  newSite.path = '/';
  newSite.isprotected = isNodeProtected(site);
  newSite.href = langSuffix;
  newSite.langSuffix = langSuffix;
  newSite.label = site.getAttribute('label');

  return newSite;
}

NavTree.prototype.handleItemClick = function(item, event) {
    if (!item.isprotected) { // && item.root!=item) {
        var itemhref = item.href.replace(/^\//, "");
        href = encodeURI(CONTEXT_PREFIX+'/'+PUBLICATION_ID+"/"+item.area+"/"+itemhref+"?lenya.usecase=tab.overview");
        window.location = href;
    }
};

/*
  overriding this function because of a bug in lenya:
  the area cannot be clickable because lenya expects
  a document for the area.
*/
NavTree.prototype.createItemHtml = function(item) {
    var div = this.doc.createElement('div');
    div.className = 'treenode';

    // place a reference to the item on the div
    div.treeitem = item;

    var table = this.doc.createElement('table');
    div.appendChild(table);
    var tbody = this.doc.createElement('tbody');
    table.appendChild(tbody);
    var tr = this.doc.createElement('tr');
    tbody.appendChild(tr);

    // add the lines:
    this.addLines(item, tr);

    // add the opensign
    var td1 = this.doc.createElement('td');
    tr.appendChild(td1);
    item.opensign = this.getCloseSign(item);
    item.opensign.className = 'treenode_sign';

    if (this.getSignType(item) == 'T') {
        // if the label of this item is too long the line will wrap
        // by setting the background image we get a continuos line
        td1.style.backgroundImage = 'url('+IMAGE_PATH+'vertical-line.gif)';
    }

    td1.appendChild(item.opensign);

    // add the label
    var td2 = this.doc.createElement('td');
    tr.appendChild(td2);
    td2.className = 'lenya-info-label-td';
    var line = this.createItemLine(item);
    td2.appendChild(line);

    // make root node not clickable
    if (item.depth > 0) {
      addEventHandler(line, 'click', this.handleItemClick, this, item);
    }
    addEventHandler(item.opensign, 'click', this.handleItemSignClick,
                    this, item);

    if (this.selected == item.getPath()) {
        this.unselect();
        this.select(item);
        div.className = 'selected_node';
    };

    return div;
};

<commit_msg>Avoid / as base path for sitetree fragment requests<commit_after>/*
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You 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.
*/

/******************************************
 *  NavNode
 ******************************************/

function NavNode(id, parent) {
    this.id = id;
    this.parent = parent;
    this.items = {};
    this.isfolder = false;
    this.uuid = '';
    this.href = '';
    this.label = '';
    this.area = '';
    this.path = '';
    this.isprotected = true;
    this.existsChosenLanguage = true;
    this.langSuffix = '';
};

NavNode.prototype = new LenyaNode;

NavNode.prototype.createNewNode = function(node)
{
    var newItem = new NavNode(node.getAttribute('id'), this);
    newItem.init();

    newItem.isfolder = isNodeFolder(node);
    newItem.area = this.area;
    newItem.path = node.getAttribute('basic-url');
    newItem.uuid = node.getAttribute('uuid');
    newItem.isprotected = isNodeProtected(node);
    newItem.href = node.getAttribute('href');
    newItem.label = getLabel(node);
    newItem.existsChosenLanguage = existsChosenLanguage(node);
    newItem.langSuffix = node.getAttribute('language-suffix');

    return newItem;
}

NavNode.prototype.getBasePath = function() {
    var path = this.path != '/' ? this.path : '' ;
    return path;
}

NavNode.prototype.getLoadSubTreeURL = function() {
    area = this.area;
    var path = this.getBasePath();
    return encodeURI(CONTEXT_PREFIX + '/' + PUBLICATION_ID + PIPELINE_PATH + '?area='+area+'&path='+path+'&language='+CHOSEN_LANGUAGE+'&areas='+ALL_AREAS+'&lenya.module=sitetree');
}

NavNode.prototype.getStyle = function() {
    if (this.tree.root == this) {
        return 'lenya-info-root';
    } else if (this.isprotected) {
        return 'lenya-info-protected';
    } else if (this.path == CUT_DOCUMENT_ID) {
        return 'lenya-info-cut';
    } else if (!this.existsChosenLanguage) {
        return 'lenya-info-nolanguage';
    } else {
        return 'treenode_label';
    }
}

function getLabel(node) {
    var cs = node.childNodes;
    var l = cs.length;
    // lenya generates the xml and is responsible to insert the label
    // of the correct language
    for (var i = 0; i < l; i++) {
       if (getTagName(cs[i]) =='nav:label') {
          if (cs[i].hasChildNodes()) {
              return cs[i].firstChild.nodeValue;
          }
          else {
              return "[no title]";
          }
       }
    }
    return '';
}

function isNodeProtected(node) {
    var prot = node.getAttribute('protected');
    if (prot == 'true') return true;
    return false;
}

function isNodeFolder(node) {
    var isfolder = node.getAttribute('folder');
    if (isfolder == 'true') return true;
    return false;
}

// check if the node has a label of the chosen language
function existsChosenLanguage(node) {
    var children = node.childNodes;
    for (var i = 0; i < children.length; i++) {
       if (getTagName(children[i]) =='nav:label' && children[i].getAttribute('xml:lang')==CHOSEN_LANGUAGE) {
          return true;
       }
    }
    return false;
}

/******************************************
 *  NavTree
 ******************************************/

function NavTree(doc, treeElement) {
    this.doc = doc;
    this.treeElement = treeElement;
    this.selected = null;
};

NavTree.prototype = new LenyaTree;

NavTree.prototype.init = function(id) {
    this.root = new NavNode(id);
    this.root.tree = this;
    this.root.depth = 0;
    this.root.reopen = false;
    this.root.isprotected = false;
    this.root.isfolder = true;
    this._currentId = 0;
};

NavTree.prototype.loadInitialTree = function(area, path) {
    var url = encodeURI(CONTEXT_PREFIX + '/' + PUBLICATION_ID + PIPELINE_PATH + '?area='+area+'&path='+path+'&language='+CHOSEN_LANGUAGE+'&initial=true&areas='+ALL_AREAS+'&lenya.module=sitetree');

    callback = function(fragment, param) {
        var tree = param[0];
        var area = param[1];
        var path = param[2];
        tree.initialTreeLoaded(fragment);
        var selectedItem = tree.getItemByPath(PUBLICATION_ID+'/'+area+path)
        if (selectedItem != false) {
            tree.select(selectedItem);
        }
    }

    var param = new Array(this, area, path);
    loadAsyncXML(url, callback, param);
};

NavTree.prototype.initialTreeLoaded = function(xml)
{
  var root = xml.documentElement;
  var children = root.childNodes;
  var items=[];
  var item;
  for (var i = 0; i < children.length; i++) {
     if (getTagName(children[i]) == "nav:site") {
        item = this.addLoadedSite(children[i]);
        items.push(item);
        item.addNodesRec(children[i]);
     }
  }
  this.root.addItems(items);
  this.root.open();
}

NavTree.prototype.addLoadedSite = function(site)
{
  var langSuffix = '';
  if (CHOSEN_LANGUAGE!=DEFAULT_LANGUAGE) langSuffix = '_'+CHOSEN_LANGUAGE;

  var siteArea = site.getAttribute('area');
  var newSite = new NavNode(siteArea, this.root);
  newSite.init();

  newSite.isfolder = isNodeFolder(site);
  newSite.area = siteArea;
  newSite.path = '/';
  newSite.isprotected = isNodeProtected(site);
  newSite.href = langSuffix;
  newSite.langSuffix = langSuffix;
  newSite.label = site.getAttribute('label');

  return newSite;
}

NavTree.prototype.handleItemClick = function(item, event) {
    if (!item.isprotected) { // && item.root!=item) {
        var itemhref = item.href.replace(/^\//, "");
        href = encodeURI(CONTEXT_PREFIX+'/'+PUBLICATION_ID+"/"+item.area+"/"+itemhref+"?lenya.usecase=tab.overview");
        window.location = href;
    }
};

/*
  overriding this function because of a bug in lenya:
  the area cannot be clickable because lenya expects
  a document for the area.
*/
NavTree.prototype.createItemHtml = function(item) {
    var div = this.doc.createElement('div');
    div.className = 'treenode';

    // place a reference to the item on the div
    div.treeitem = item;

    var table = this.doc.createElement('table');
    div.appendChild(table);
    var tbody = this.doc.createElement('tbody');
    table.appendChild(tbody);
    var tr = this.doc.createElement('tr');
    tbody.appendChild(tr);

    // add the lines:
    this.addLines(item, tr);

    // add the opensign
    var td1 = this.doc.createElement('td');
    tr.appendChild(td1);
    item.opensign = this.getCloseSign(item);
    item.opensign.className = 'treenode_sign';

    if (this.getSignType(item) == 'T') {
        // if the label of this item is too long the line will wrap
        // by setting the background image we get a continuos line
        td1.style.backgroundImage = 'url('+IMAGE_PATH+'vertical-line.gif)';
    }

    td1.appendChild(item.opensign);

    // add the label
    var td2 = this.doc.createElement('td');
    tr.appendChild(td2);
    td2.className = 'lenya-info-label-td';
    var line = this.createItemLine(item);
    td2.appendChild(line);

    // make root node not clickable
    if (item.depth > 0) {
      addEventHandler(line, 'click', this.handleItemClick, this, item);
    }
    addEventHandler(item.opensign, 'click', this.handleItemSignClick,
                    this, item);

    if (this.selected == item.getPath()) {
        this.unselect();
        this.select(item);
        div.className = 'selected_node';
    };

    return div;
};

<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import Box from '@material-ui/core/Box';
import HubspotForm from 'react-hubspot-form';
import CodeBlock from '@src/components/CodeBlock';
import LinuxIcon from '@src/assets/icons/linux.inline.svg';
import AppleIcon from '@src/assets/icons/apple.inline.svg';
import WindowsIcon from '@src/assets/icons/windows.inline.svg';

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return (
    <div
      role="tabpanel"
      hidden={value !== index}
      id={`simple-tabpanel-${index}`}
      aria-labelledby={`simple-tab-${index}`}
      {...other}
    >
      {value === index && (
        <Box p={3}>
          {children}
        </Box>
      )}

  );
}

TabPanel.propTypes = {
  children: PropTypes.node,
  index: PropTypes.any.isRequired,
  value: PropTypes.any.isRequired,
};

function a11yProps(index) {
  return {
    id: `simple-tab-${index}`,
    'aria-controls': `simple-tabpanel-${index}`,
  };
}

const useStyles = makeStyles((theme) => ({
  root: {
    flexGrow: 1,
    backgroundColor: 'transparent',
  },
}));

export default function SimpleTabs() {
  const classes = useStyles();
  const [value, setValue] = React.useState(0);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (

      <AppBar elevation={0} style={{ background: 'transparent', color: 'black', borderBottom: '1px solid #e8e8e8', }} position="static">
        <Tabs value={value} onChange={handleChange} aria-label="operating system tabs">
          <Tab icon={<AppleIcon />} label="macOS" {...a11yProps(0)} style={{ minWidth: "10%", textTransform: 'none' }} />
          <Tab icon={<LinuxIcon />} label="Linux" {...a11yProps(1)} style={{ minWidth: "10%", textTransform: 'none' }} />
          <Tab icon={<WindowsIcon />} label="Windows" {...a11yProps(2)} style={{ minWidth: "10%", textTransform: 'none' }} />
        </Tabs>
      </AppBar>
      <TabPanel value={value} index={0}>
        <CodeBlock>
        {
          '# Install via brew:' +
          '\n' +
          'brew install datawire/blackbird/telepresence' +
          '\n \n' +
          '# OR install manually:' +
          '\n' +
          '# 1. Download the latest binary (~60 MB):' +
          '\n' +
          'sudo curl -fL https://app.getambassador.io/download/tel2/darwin/amd64/latest/telepresence -o /usr/local/bin/telepresence' +
          '\n \n' +
          '# 2. Make the binary executable:' +
          '\n' +
          'sudo chmod a+x /usr/local/bin/telepresence'
        }
        </CodeBlock>
      </TabPanel>
      <TabPanel value={value} index={1}>
        <CodeBlock>
        {
          '# 1. Download the latest binary (~50 MB):' +
          '\n' +
          'sudo curl -fL https://app.getambassador.io/download/tel2/linux/amd64/latest/telepresence -o /usr/local/bin/telepresence' +
          '\n \n' +
          '# 2. Make the binary executable:' +
          '\n' +
          'sudo chmod a+x /usr/local/bin/telepresence'
        }
        </CodeBlock>
      </TabPanel>
      <TabPanel value={value} index={2}>

            Telepresence for Windows is coming soon! Sign up here to notified when it is available. Until then, Telepresence will work with WSL 2.

            <HubspotForm
              portalId='485087'
              formId='2f542f1b-3da8-4319-8057-96fed78e4c26'
              onSubmit={() => console.log('Submit!')}
              onReady={(form) => console.log('Form ready!')}
              loading={
Loading...
}
            />

      </TabPanel>

  );
}
<commit_msg>Add windows install to qs-tabs<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import Box from '@material-ui/core/Box';
import HubspotForm from 'react-hubspot-form';
import CodeBlock from '@src/components/CodeBlock';
import LinuxIcon from '@src/assets/icons/linux.inline.svg';
import AppleIcon from '@src/assets/icons/apple.inline.svg';
import WindowsIcon from '@src/assets/icons/windows.inline.svg';

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return (
    <div
      role="tabpanel"
      hidden={value !== index}
      id={`simple-tabpanel-${index}`}
      aria-labelledby={`simple-tab-${index}`}
      {...other}
    >
      {value === index && (
        <Box p={3}>
          {children}
        </Box>
      )}

  );
}

TabPanel.propTypes = {
  children: PropTypes.node,
  index: PropTypes.any.isRequired,
  value: PropTypes.any.isRequired,
};

function a11yProps(index) {
  return {
    id: `simple-tab-${index}`,
    'aria-controls': `simple-tabpanel-${index}`,
  };
}

const useStyles = makeStyles((theme) => ({
  root: {
    flexGrow: 1,
    backgroundColor: 'transparent',
  },
}));

export default function SimpleTabs() {
  const classes = useStyles();
  const [value, setValue] = React.useState(0);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (

      <AppBar elevation={0} style={{ background: 'transparent', color: 'black', borderBottom: '1px solid #e8e8e8', }} position="static">
        <Tabs value={value} onChange={handleChange} aria-label="operating system tabs">
          <Tab icon={<AppleIcon />} label="macOS" {...a11yProps(0)} style={{ minWidth: "10%", textTransform: 'none' }} />
          <Tab icon={<LinuxIcon />} label="Linux" {...a11yProps(1)} style={{ minWidth: "10%", textTransform: 'none' }} />
          <Tab icon={<WindowsIcon />} label="Windows" {...a11yProps(2)} style={{ minWidth: "10%", textTransform: 'none' }} />
        </Tabs>
      </AppBar>
      <TabPanel value={value} index={0}>
        <CodeBlock>
        {
          '# Install via brew:' +
          '\n' +
          'brew install datawire/blackbird/telepresence' +
          '\n \n' +
          '# OR install manually:' +
          '\n' +
          '# 1. Download the latest binary (~60 MB):' +
          '\n' +
          'sudo curl -fL https://app.getambassador.io/download/tel2/darwin/amd64/latest/telepresence -o /usr/local/bin/telepresence' +
          '\n \n' +
          '# 2. Make the binary executable:' +
          '\n' +
          'sudo chmod a+x /usr/local/bin/telepresence'
        }
        </CodeBlock>
      </TabPanel>
      <TabPanel value={value} index={1}>
        <CodeBlock>
        {
          '# 1. Download the latest binary (~50 MB):' +
          '\n' +
          'sudo curl -fL https://app.getambassador.io/download/tel2/linux/amd64/latest/telepresence -o /usr/local/bin/telepresence' +
          '\n \n' +
          '# 2. Make the binary executable:' +
          '\n' +
          'sudo chmod a+x /usr/local/bin/telepresence'
        }
        </CodeBlock>
      </TabPanel>
      <TabPanel value={value} index={2}>
        <CodeBlock>
        {
          '# Make sure you run the following from Powershell as Administrator' +
          '# 1. Download the latest windows zip containing telepresence.exe and its dependencies (~50 MB):' +
          '\n' +
          'curl -fL https://app.getambassador.io/download/tel2/windows/amd64/latest/telepresence.zip -o telepresence.zip' +
          '\n \n' +
          '# 2. Unzip the zip file to a suitable directory + cleanup zip' +
          '\n' +
          'Expand-Archive -Path telepresence.zip' +
          '\n' +
          'Remove-Item \'telepresence.zip\'' +
          '\n' +
          'cd telepresence' +
          '\n \n' +
          '# 3. Run the install-telepresence.ps1 to install telepresence\'s dependencies. It will install telepresence to' +
          '\n' +
          '# C:\\telepresence by default, but you can specify a custom path $path with -Path $path' +
          '\n' +
          '.\install-telepresence.ps1' +
          '\n \n' +
          '# 4. Remove the unzipped directory' +
          '\n' +
          'cd ..' +
          'Remove-Item telepresence'
        }
        </CodeBlock>
      </TabPanel>

  );
}
<|endoftext|>


<commit_before>/**
 * @overview tests for ccm component for rendering a quiz
 * @author André Kless <andre.kless@web.de> 2017
 * @license The MIT License (MIT)
 */

ccm.files[ 'ccm.quiz.tests.js' ] = {
  setup: function ( suite, callback ) {
    suite.container = document.createElement( 'div' );
    document.body.appendChild( suite.container );
    suite.ccm.component( './../../ccm-components/quiz/ccm.quiz.js', { element: suite.container }, function ( component ) {
      suite.component = component;
      callback();
    } );
  },
  fundamental: {
    setup: function ( suite, callback ) {
      suite.component.instance( function ( instance ) {
        suite.instance = instance;
        callback();
      } );
    },
    tests: {
      componentName: function ( suite ) {
        suite.assertSame( 'quiz', suite.instance.component.name );
      },
      frameworkVersion: function ( suite ) {
        suite.assertEquals( '8.0.0', suite.instance.ccm.version() );
      },
      publicProperties: function ( suite ) {
        suite.assertEquals( [ 'start', 'ccm', 'element', 'id', 'index', 'component' ], Object.keys( suite.instance ) );
      },
      startCallback: function ( suite ) {
        suite.instance.start( suite.passed );
      },
      componentTag: function ( suite ) {
        suite.container.innerHTML = '<ccm-quiz></ccm-quiz>';
        suite.passed();
      }
    }
  },
  templates: {
    setup: function ( suite, callback ) {
      suite.component.start( { questions: [ {}, {} ] }, function ( instance ) {
        suite.instance = instance;
        callback();
      } );
    },
    tests: {
      main: function ( suite ) {
        if ( !suite.instance.element.querySelector( '#questions' ) ) return suite.failed( 'missing "questions" container' );
        if ( !suite.instance.element.querySelector( '#prev' ) ) return suite.failed( 'missing "prev" container' );
        if ( !suite.instance.element.querySelector( '#next' ) ) return suite.failed( 'missing "next" container' );
        if ( !suite.instance.element.querySelector( '#finish' ) ) return suite.failed( 'missing "finish" container' );
        suite.passed();
      }
    }
  },
  dependencies: {
    tests: {
      loggedInUser: function ( suite ) {
        suite.component.start( {
          user: [ 'ccm.instance', './../../ccm-components/user/ccm.user.js' ],
          onfinish: function ( instance ) {
            suite.assertTrue( instance.user.isLoggedIn() );
          }
        }, function ( instance ) {
          if ( instance.user.isLoggedIn() ) suite.failed( 'user is already logged in' );
          instance.element.querySelector( 'button' ).click();
        } );
      },
      logStartEvent: function ( suite ) {
        suite.component.start( {
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', { onfinish: function ( instance, results ) {
            suite.assertSame( 'start', results.event );
          } } ]
        } );
      },
      logFinishEvent: function ( suite ) {
        suite.component.start( {
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            events: { finish: true },
            onfinish: function ( instance, results ) {
              suite.assertSame( 'finish', results.event );
            }
          } ]
        }, function ( instance ) {
          instance.element.querySelector( 'button' ).click();
        } );
      }
    }
  },
  uniform_data_structure: {
    tests: {
      singleQuestion: function ( suite ) {
        suite.component.start( {
          questions: {},
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertSame( 1, results.data.questions.length );
            }
          } ]
        } );
      },
      defaultsByConfig: function ( suite ) {
        suite.component.start( {
          text: true, description: true, answers: true, input: true, attributes: true, swap: true, encode: true, random: true, feedback: true, correct: true,
          questions: {},
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {

              var data = results.data;
              if ( data.text        ) return suite.failed( 'text property not deleted' );
              if ( data.description ) return suite.failed( 'description property not deleted' );
              if ( data.answers     ) return suite.failed( 'answers property not deleted' );
              if ( data.input       ) return suite.failed( 'input property not deleted' );
              if ( data.attributes  ) return suite.failed( 'attributes property not deleted' );
              if ( data.swap        ) return suite.failed( 'swap property not deleted' );
              if ( data.encode      ) return suite.failed( 'encode property not deleted' );
              if ( data.random      ) return suite.failed( 'random property not deleted' );
              if ( data.feedback    ) return suite.failed( 'feedback property not deleted' );
              if ( data.correct     ) return suite.failed( 'correct property not deleted' );

              var question = data.questions[ 0 ];
              if ( !question.text        ) return suite.failed( 'missing text property' );
              if ( !question.description ) return suite.failed( 'missing description property' );
              if ( !question.answers     ) return suite.failed( 'missing answers property' );
              if ( !question.input       ) return suite.failed( 'missing input property' );
              if ( !question.random      ) return suite.failed( 'missing random property' );
              if ( !question.feedback    ) return suite.failed( 'missing feedback property' );
              if ( !question.correct     ) return suite.failed( 'missing correct property' );

              var answer = question.answers[ 0 ];
              if ( !answer.attributes  ) return suite.failed( 'missing attributes property' );
              if ( !answer.swap        ) return suite.failed( 'missing swap property' );
              if ( !answer.encode      ) return suite.failed( 'missing encode property' );

              suite.passed();
            }
          } ]
        } );
      },
      defaultInput: function ( suite ) {
        suite.component.start( {
          questions: {},
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertSame( 'checkbox', results.data.questions[ 0 ].input );
            }
          } ]
        } );
      },
      singleAnswer: function ( suite ) {
        suite.component.start( {
          questions: { answers: {} },
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertSame( 1, results.data.questions[ 0 ].answers.length );
            }
          } ]
        } );
      },
      stringAnswers: function ( suite ) {
        suite.component.start( {
          questions: { answers: [ 'foo', {}, 'bar' ] },
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertEquals( [ { text: 'foo' }, {}, { text: 'bar' } ], results.data.questions[ 0 ].answers );
            }
          } ]
        } );
      },
      singleCorrect: function ( suite ) {
        suite.component.start( {
          questions: { answers: [ "", "", "" ], correct: 1 },
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertEquals( [ false, true, false ], results.data.questions[ 0 ].correct );
            }
          } ]
        } );
      }
    }
  },
  finally: function ( suite, callback ) {
    document.body.removeChild( suite.container );
    callback();
  }
};

ccm.components.testsuite.quiz = ccm.files[ 'ccm.quiz.tests.js' ];<commit_msg>add unit test for ccm.quiz.js<commit_after>/**
 * @overview tests for ccm component for rendering a quiz
 * @author André Kless <andre.kless@web.de> 2017
 * @license The MIT License (MIT)
 */

ccm.files[ 'ccm.quiz.tests.js' ] = {
  setup: function ( suite, callback ) {
    suite.container = document.createElement( 'div' );
    document.body.appendChild( suite.container );
    suite.ccm.component( './../../ccm-components/quiz/ccm.quiz.js', { element: suite.container }, function ( component ) {
      suite.component = component;
      callback();
    } );
  },
  fundamental: {
    setup: function ( suite, callback ) {
      suite.component.instance( function ( instance ) {
        suite.instance = instance;
        callback();
      } );
    },
    tests: {
      componentName: function ( suite ) {
        suite.assertSame( 'quiz', suite.instance.component.name );
      },
      frameworkVersion: function ( suite ) {
        suite.assertEquals( '8.0.0', suite.instance.ccm.version() );
      },
      publicProperties: function ( suite ) {
        suite.assertEquals( [ 'start', 'ccm', 'element', 'id', 'index', 'component' ], Object.keys( suite.instance ) );
      },
      startCallback: function ( suite ) {
        suite.instance.start( suite.passed );
      },
      componentTag: function ( suite ) {
        suite.container.innerHTML = '<ccm-quiz></ccm-quiz>';
        suite.passed();
      }
    }
  },
  templates: {
    setup: function ( suite, callback ) {
      suite.component.start( { questions: [ {}, {} ] }, function ( instance ) {
        suite.instance = instance;
        callback();
      } );
    },
    tests: {
      main: function ( suite ) {
        if ( !suite.instance.element.querySelector( '#questions' ) ) return suite.failed( 'missing "questions" container' );
        if ( !suite.instance.element.querySelector( '#prev' ) ) return suite.failed( 'missing "prev" container' );
        if ( !suite.instance.element.querySelector( '#next' ) ) return suite.failed( 'missing "next" container' );
        if ( !suite.instance.element.querySelector( '#finish' ) ) return suite.failed( 'missing "finish" container' );
        suite.passed();
      }
    }
  },
  dependencies: {
    tests: {
      loggedInUser: function ( suite ) {
        suite.component.start( {
          user: [ 'ccm.instance', './../../ccm-components/user/ccm.user.js' ],
          onfinish: function ( instance ) {
            suite.assertTrue( instance.user.isLoggedIn() );
          }
        }, function ( instance ) {
          if ( instance.user.isLoggedIn() ) suite.failed( 'user is already logged in' );
          instance.element.querySelector( 'button' ).click();
        } );
      },
      logStartEvent: function ( suite ) {
        suite.component.start( {
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', { onfinish: function ( instance, results ) {
            suite.assertSame( 'start', results.event );
          } } ]
        } );
      },
      logFinishEvent: function ( suite ) {
        suite.component.start( {
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            events: { finish: true },
            onfinish: function ( instance, results ) {
              suite.assertSame( 'finish', results.event );
            }
          } ]
        }, function ( instance ) {
          instance.element.querySelector( 'button' ).click();
        } );
      }
    }
  },
  uniform_data_structure: {
    tests: {
      singleQuestion: function ( suite ) {
        suite.component.start( {
          questions: {},
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertSame( 1, results.data.questions.length );
            }
          } ]
        } );
      },
      defaultsByConfig: function ( suite ) {
        suite.component.start( {
          text: true, description: true, answers: true, input: true, attributes: true, swap: true, encode: true, random: true, feedback: true, correct: true,
          questions: {},
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {

              var data = results.data;
              if ( data.text        ) return suite.failed( 'text property not deleted' );
              if ( data.description ) return suite.failed( 'description property not deleted' );
              if ( data.answers     ) return suite.failed( 'answers property not deleted' );
              if ( data.input       ) return suite.failed( 'input property not deleted' );
              if ( data.attributes  ) return suite.failed( 'attributes property not deleted' );
              if ( data.swap        ) return suite.failed( 'swap property not deleted' );
              if ( data.encode      ) return suite.failed( 'encode property not deleted' );
              if ( data.random      ) return suite.failed( 'random property not deleted' );
              if ( data.feedback    ) return suite.failed( 'feedback property not deleted' );
              if ( data.correct     ) return suite.failed( 'correct property not deleted' );

              var question = data.questions[ 0 ];
              if ( !question.text        ) return suite.failed( 'missing text property' );
              if ( !question.description ) return suite.failed( 'missing description property' );
              if ( !question.answers     ) return suite.failed( 'missing answers property' );
              if ( !question.input       ) return suite.failed( 'missing input property' );
              if ( !question.random      ) return suite.failed( 'missing random property' );
              if ( !question.feedback    ) return suite.failed( 'missing feedback property' );
              if ( !question.correct     ) return suite.failed( 'missing correct property' );

              var answer = question.answers[ 0 ];
              if ( !answer.attributes  ) return suite.failed( 'missing attributes property' );
              if ( !answer.swap        ) return suite.failed( 'missing swap property' );
              if ( !answer.encode      ) return suite.failed( 'missing encode property' );

              suite.passed();
            }
          } ]
        } );
      },
      defaultInput: function ( suite ) {
        suite.component.start( {
          questions: {},
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertSame( 'checkbox', results.data.questions[ 0 ].input );
            }
          } ]
        } );
      },
      singleAnswer: function ( suite ) {
        suite.component.start( {
          questions: { answers: {} },
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertSame( 1, results.data.questions[ 0 ].answers.length );
            }
          } ]
        } );
      },
      stringAnswers: function ( suite ) {
        suite.component.start( {
          questions: { answers: [ 'foo', {}, 'bar' ] },
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertEquals( [ { text: 'foo' }, {}, { text: 'bar' } ], results.data.questions[ 0 ].answers );
            }
          } ]
        } );
      },
      singleCorrectNumber: function ( suite ) {
        suite.component.start( {
          questions: { answers: [ '', '', '' ], correct: 1 },
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertEquals( [ false, true, false ], results.data.questions[ 0 ].correct );
            }
          } ]
        } );
      },
      fillUpValue: function ( suite ) {
        suite.component.start( {
          questions: { input: 'number', answers: [ '', '', '' ], correct: 5711 },
          logger: [ 'ccm.instance', './../../ccm-components/log/ccm.log.js', {
            logging: { data: true },
            onfinish: function ( instance, results ) {
              suite.assertEquals( [ 5711, '', '' ], results.data.questions[ 0 ].correct );
            }
          } ]
        } );
      }
    }
  },
  finally: function ( suite, callback ) {
    document.body.removeChild( suite.container );
    callback();
  }
};

ccm.components.testsuite.quiz = ccm.files[ 'ccm.quiz.tests.js' ];<|endoftext|>


<commit_before>import BaseTool from './base/BaseTool.js';
import BaseAnnotationTool from './base/BaseAnnotationTool.js';
import BaseBrushTool from './base/BaseBrushTool.js';

import anyHandlesOutsideImage from './manipulators/anyHandlesOutsideImage.js';
import getHandleNearImagePoint from './manipulators/getHandleNearImagePoint.js';
import handleActivator from './manipulators/handleActivator.js';
import moveAllHandles from './manipulators/moveAllHandles.js';
import moveHandle from './manipulators/moveHandle.js';
import moveNewHandle from './manipulators/moveNewHandle.js';
import moveNewHandleTouch from './manipulators/moveNewHandleTouch.js';
import touchMoveAllHandles from './manipulators/touchMoveAllHandles.js';
import touchMoveHandle from './manipulators/touchMoveHandle.js';

import {
  moveHandleNearImagePoint,
  findHandleDataNearImagePoint,
  moveAnnotationNearClick,
  findAnnotationNearClick
} from './util/findAndMoveHelpers.js';

import mixins from './mixins/index.js';

import {
  getNewContext,
  draw,
  path,
  setShadow,
  drawLine,
  drawLines,
  drawJoinedLines,
  drawCircle,
  drawEllipse,
  drawRect,
  fillOutsideRect,
  fillBox,
  fillTextLines
} from './drawing/index.js';
import drawTextBox from './drawing/drawTextBox.js';
import drawArrow from './drawing/drawArrow.js';
import drawLink from './drawing/drawLink.js';
import drawLinkedTextBox from './drawing/drawLinkedTextBox.js';
import drawHandles from './drawing/drawHandles.js';

import getLuminance from './util/getLuminance.js';
import copyPoints from './util/copyPoints.js';
import calculateSUV from './util/calculateSUV.js';
import setContextToDisplayFontSize from './util/setContextToDisplayFontSize.js';
import scrollToIndex from './util/scrollToIndex.js';
import scroll from './util/scroll.js';
import roundToDecimal from './util/roundToDecimal.js';
import {
  projectPatientPointToImagePlane,
  imagePointToPatientPoint,
  planePlaneIntersection
} from './util/pointProjector.js';

import pointInsideBoundingBox from './util/pointInsideBoundingBox.js';
import makeUnselectable from './util/makeUnselectable.js';
import getRGBPixels from './util/getRGBPixels.js';
import {
  getDefaultSimultaneousRequests,
  getMaxSimultaneousRequests,
  getBrowserInfo,
  isMobileDevice
} from './util/getMaxSimultaneousRequests.js';
import angleBetweenPoints from './util/angleBetweenPoints.js';
import getKeyFromKeyCode from './util/getKeyFromKeyCode.js';
import numbersWithCommas from './util/numbersWithCommas.js';

import ellipseUtils from './util/ellipse/index.js';
import freehandUtils from './util/freehand/index.js';
import brushUtils from './util/brush/index.js';
import zoomUtils from './util/zoom/index.js';

export const lib = {
  'base/BaseTool': BaseTool,
  'base/BaseAnnotationTool': BaseAnnotationTool,
  'base/BaseBrushTool': BaseBrushTool,

  'manipulators/anyHandlesOutsideImage': anyHandlesOutsideImage,
  'manipulators/getHandleNearImagePoint': getHandleNearImagePoint,
  'manipulators/handleActivator': handleActivator,
  'manipulators/moveAllHandles': moveAllHandles,
  'manipulators/moveHandle': moveHandle,
  'manipulators/moveNewHandle': moveNewHandle,
  'manipulators/moveNewHandleTouch': moveNewHandleTouch,
  'manipulators/touchMoveAllHandles': touchMoveAllHandles,
  'manipulators/touchMoveHandle': touchMoveHandle,
  'manipulators/moveHandleNearImagePoint': moveHandleNearImagePoint,
  'manipulators/findHandleDataNearImagePoint': findHandleDataNearImagePoint,
  'manipulators/moveAnnotationNearClick': moveAnnotationNearClick,
  'manipulators/findAnnotationNearClick': findAnnotationNearClick,

  'mixins/activeOrDisabledBinaryTool': mixins.activeOrDisabledBinaryTool,
  'mixins/enabledOrDisabledBinaryTool': mixins.enabledOrDisabledBinaryTool,

  'drawing/getNewContext': getNewContext,
  'drawing/draw': draw,
  'drawing/path': path,
  'drawing/setShadow': setShadow,
  'drawing/drawLine': drawLine,
  'drawing/drawLines': drawLines,
  'drawing/drawJoinedLines': drawJoinedLines,
  'drawing/drawCircle': drawCircle,
  'drawing/drawEllipse': drawEllipse,
  'drawing/drawRect': drawRect,
  'drawing/fillOutsideRect': fillOutsideRect,
  'drawing/drawTextBox': drawTextBox,
  'drawing/drawArrow': drawArrow,
  'drawing/fillBox': fillBox,
  'drawing/fillTextLines': fillTextLines,
  'drawing/drawLink': drawLink,
  'drawing/drawLinkedTextBox': drawLinkedTextBox,
  'drawing/drawHandles': drawHandles,

  'util/getLuminance': getLuminance,
  'util/copyPoints': copyPoints,
  'util/calculateSUV': calculateSUV,
  'util/setContextToDisplayFontSize': setContextToDisplayFontSize,
  'util/scrollToIndex': scrollToIndex,
  'util/scroll': scroll,
  'util/roundToDecimal': roundToDecimal,
  'util/projectPatientPointToImagePlane': projectPatientPointToImagePlane,
  'util/imagePointToPatientPoint': imagePointToPatientPoint,
  'util/planePlaneIntersection': planePlaneIntersection,
  'util/pointInsideBoundingBox': pointInsideBoundingBox,
  'util/makeUnselectable': makeUnselectable,
  'util/getRGBPixels': getRGBPixels,
  'util/getDefaultSimultaneousRequests': getDefaultSimultaneousRequests,
  'util/getMaxSimultaneousRequests': getMaxSimultaneousRequests,
  'util/getBrowserInfo': getBrowserInfo,
  'util/isMobileDevice': isMobileDevice,
  'util/angleBetweenPoints': angleBetweenPoints,
  'util/getKeyFromKeyCode': getKeyFromKeyCode,
  'util/numbersWithCommas': numbersWithCommas,

  // Whole tool specific util packages
  'util/ellipseUtils': ellipseUtils,
  'util/freehandUtils': freehandUtils,
  'util/brushUtils': brushUtils,
  'util/zoomUtils': zoomUtils,
  'util/findAndMoveHelpers': findAndMoveHelpers
};
<commit_msg>Fix my mistake<commit_after>import BaseTool from './base/BaseTool.js';
import BaseAnnotationTool from './base/BaseAnnotationTool.js';
import BaseBrushTool from './base/BaseBrushTool.js';

import anyHandlesOutsideImage from './manipulators/anyHandlesOutsideImage.js';
import getHandleNearImagePoint from './manipulators/getHandleNearImagePoint.js';
import handleActivator from './manipulators/handleActivator.js';
import moveAllHandles from './manipulators/moveAllHandles.js';
import moveHandle from './manipulators/moveHandle.js';
import moveNewHandle from './manipulators/moveNewHandle.js';
import moveNewHandleTouch from './manipulators/moveNewHandleTouch.js';
import touchMoveAllHandles from './manipulators/touchMoveAllHandles.js';
import touchMoveHandle from './manipulators/touchMoveHandle.js';

import {
  moveHandleNearImagePoint,
  findHandleDataNearImagePoint,
  moveAnnotationNearClick,
  findAnnotationNearClick
} from './util/findAndMoveHelpers.js';

import mixins from './mixins/index.js';

import {
  getNewContext,
  draw,
  path,
  setShadow,
  drawLine,
  drawLines,
  drawJoinedLines,
  drawCircle,
  drawEllipse,
  drawRect,
  fillOutsideRect,
  fillBox,
  fillTextLines
} from './drawing/index.js';
import drawTextBox from './drawing/drawTextBox.js';
import drawArrow from './drawing/drawArrow.js';
import drawLink from './drawing/drawLink.js';
import drawLinkedTextBox from './drawing/drawLinkedTextBox.js';
import drawHandles from './drawing/drawHandles.js';

import getLuminance from './util/getLuminance.js';
import copyPoints from './util/copyPoints.js';
import calculateSUV from './util/calculateSUV.js';
import setContextToDisplayFontSize from './util/setContextToDisplayFontSize.js';
import scrollToIndex from './util/scrollToIndex.js';
import scroll from './util/scroll.js';
import roundToDecimal from './util/roundToDecimal.js';
import {
  projectPatientPointToImagePlane,
  imagePointToPatientPoint,
  planePlaneIntersection
} from './util/pointProjector.js';

import pointInsideBoundingBox from './util/pointInsideBoundingBox.js';
import makeUnselectable from './util/makeUnselectable.js';
import getRGBPixels from './util/getRGBPixels.js';
import {
  getDefaultSimultaneousRequests,
  getMaxSimultaneousRequests,
  getBrowserInfo,
  isMobileDevice
} from './util/getMaxSimultaneousRequests.js';
import angleBetweenPoints from './util/angleBetweenPoints.js';
import getKeyFromKeyCode from './util/getKeyFromKeyCode.js';
import numbersWithCommas from './util/numbersWithCommas.js';

import ellipseUtils from './util/ellipse/index.js';
import freehandUtils from './util/freehand/index.js';
import brushUtils from './util/brush/index.js';
import zoomUtils from './util/zoom/index.js';

export const lib = {
  'base/BaseTool': BaseTool,
  'base/BaseAnnotationTool': BaseAnnotationTool,
  'base/BaseBrushTool': BaseBrushTool,

  'manipulators/anyHandlesOutsideImage': anyHandlesOutsideImage,
  'manipulators/getHandleNearImagePoint': getHandleNearImagePoint,
  'manipulators/handleActivator': handleActivator,
  'manipulators/moveAllHandles': moveAllHandles,
  'manipulators/moveHandle': moveHandle,
  'manipulators/moveNewHandle': moveNewHandle,
  'manipulators/moveNewHandleTouch': moveNewHandleTouch,
  'manipulators/touchMoveAllHandles': touchMoveAllHandles,
  'manipulators/touchMoveHandle': touchMoveHandle,
  'manipulators/moveHandleNearImagePoint': moveHandleNearImagePoint,
  'manipulators/findHandleDataNearImagePoint': findHandleDataNearImagePoint,
  'manipulators/moveAnnotationNearClick': moveAnnotationNearClick,
  'manipulators/findAnnotationNearClick': findAnnotationNearClick,

  'mixins/activeOrDisabledBinaryTool': mixins.activeOrDisabledBinaryTool,
  'mixins/enabledOrDisabledBinaryTool': mixins.enabledOrDisabledBinaryTool,

  'drawing/getNewContext': getNewContext,
  'drawing/draw': draw,
  'drawing/path': path,
  'drawing/setShadow': setShadow,
  'drawing/drawLine': drawLine,
  'drawing/drawLines': drawLines,
  'drawing/drawJoinedLines': drawJoinedLines,
  'drawing/drawCircle': drawCircle,
  'drawing/drawEllipse': drawEllipse,
  'drawing/drawRect': drawRect,
  'drawing/fillOutsideRect': fillOutsideRect,
  'drawing/drawTextBox': drawTextBox,
  'drawing/drawArrow': drawArrow,
  'drawing/fillBox': fillBox,
  'drawing/fillTextLines': fillTextLines,
  'drawing/drawLink': drawLink,
  'drawing/drawLinkedTextBox': drawLinkedTextBox,
  'drawing/drawHandles': drawHandles,

  'util/getLuminance': getLuminance,
  'util/copyPoints': copyPoints,
  'util/calculateSUV': calculateSUV,
  'util/setContextToDisplayFontSize': setContextToDisplayFontSize,
  'util/scrollToIndex': scrollToIndex,
  'util/scroll': scroll,
  'util/roundToDecimal': roundToDecimal,
  'util/projectPatientPointToImagePlane': projectPatientPointToImagePlane,
  'util/imagePointToPatientPoint': imagePointToPatientPoint,
  'util/planePlaneIntersection': planePlaneIntersection,
  'util/pointInsideBoundingBox': pointInsideBoundingBox,
  'util/makeUnselectable': makeUnselectable,
  'util/getRGBPixels': getRGBPixels,
  'util/getDefaultSimultaneousRequests': getDefaultSimultaneousRequests,
  'util/getMaxSimultaneousRequests': getMaxSimultaneousRequests,
  'util/getBrowserInfo': getBrowserInfo,
  'util/isMobileDevice': isMobileDevice,
  'util/angleBetweenPoints': angleBetweenPoints,
  'util/getKeyFromKeyCode': getKeyFromKeyCode,
  'util/numbersWithCommas': numbersWithCommas,

  // Whole tool specific util packages
  'util/ellipseUtils': ellipseUtils,
  'util/freehandUtils': freehandUtils,
  'util/brushUtils': brushUtils,
  'util/zoomUtils': zoomUtils
};
<|endoftext|>


<commit_before>/**
 * Copyright 2015 Darshak Parikh
 *
 * 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.
 */

var isStream = require('is-stream');
var magic = require('stream-mmmagic');
var request = require('request');
var stream = require('stream');
var types = require('./types.json');
var _ = require('lodash');

/**
 * Get the type of message based on contents
 * @param  {stream|string}   content  content to check
 * @param  {Function} callback Callback function
 */
exports.getMessageType = function (content, callback) {
  'use strict';
  if (isStream.readable(content)) {
    // Hack to keep `magic` from ruining the stream
    var contentCopy = new stream.PassThrough();
    content.pipe(contentCopy);

    magic(contentCopy, function (err, mime) {
      if (err) {
        console.log(err);
      }
      if (_.contains(types.image, mime.type)) {
        callback('image');
      } else if (_.contains(types.audio, mime.type)) {
        callback('audio');
      } else if (_.contains(types.video, mime.type)) {
        callback('video');
      } else {
        callback('document');
      }
    });
  } else if (_.isString(content)) {
    callback('text');
  } else {
    console.error('Couldn\'t determine type of content. Please pass a string or a readable stream.');
  }
};

/**
 * Generic method to send HTTPS requests
 * @param  {Object}   params   form data
 * @param  {Function} callback Callback function
 */
exports.sendRequest = function (params, callback) {
  'use strict';
  request.post(params, function (err, res, body) {
    if (err) {
      console.error(err);
    }
    if (callback) {
      callback(JSON.parse(body));
    }
  });
};
<commit_msg>Hack didn't work, revert<commit_after>/**
 * Copyright 2015 Darshak Parikh
 *
 * 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.
 */

var isStream = require('is-stream');
var magic = require('stream-mmmagic');
var request = require('request');
var types = require('./types.json');
var _ = require('lodash');

/**
 * Get the type of message based on contents
 * @param  {stream|string}   content  content to check
 * @param  {Function} callback Callback function
 */
exports.getMessageType = function (content, callback) {
  'use strict';
  if (isStream.readable(content)) {
    magic(content, function (err, mime) {
      if (err) {
        console.log(err);
      }
      if (_.contains(types.image, mime.type)) {
        callback('image');
      } else if (_.contains(types.audio, mime.type)) {
        callback('audio');
      } else if (_.contains(types.video, mime.type)) {
        callback('video');
      } else {
        callback('document');
      }
    });
  } else if (_.isString(content)) {
    callback('text');
  } else {
    console.error('Couldn\'t determine type of content. Please pass a string or a readable stream.');
  }
};

/**
 * Generic method to send HTTPS requests
 * @param  {Object}   params   form data
 * @param  {Function} callback Callback function
 */
exports.sendRequest = function (params, callback) {
  'use strict';
  request.post(params, function (err, res, body) {
    if (err) {
      console.error(err);
    }
    if (callback) {
      callback(JSON.parse(body));
    }
  });
};
<|endoftext|>