<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * A SourceRow represents the row corresponding to a single SourceEntry
 * displayed by the EventsView.
 *
 * @constructor
 */
function SourceRow(parentView, sourceEntry) {
  this.parentView_ = parentView;

  this.sourceEntry_ = sourceEntry;
  this.isSelected_ = false;
  this.isMatchedByFilter_ = false;

  // Used to set CSS class for display.  Must only be modified by calling
  // corresponding set functions.
  this.isSelected_ = false;
  this.isMouseOver_ = false;

  // Mirror sourceEntry's values, so we only update the DOM when necessary.
  this.isError_ = sourceEntry.isError();
  this.isInactive_ = sourceEntry.isInactive();
  this.description_ = sourceEntry.getDescription();

  this.createRow_();
  this.onSourceUpdated();
}

SourceRow.prototype.createRow_ = function() {
  // Create a row.
  var tr = addNode(this.parentView_.tableBody_, 'tr');
  tr._id = this.sourceEntry_.getSourceId();
  tr.style.display = 'none';
  this.row_ = tr;

  var selectionCol = addNode(tr, 'td');
  var checkbox = addNode(selectionCol, 'input');
  checkbox.type = 'checkbox';

  var idCell = addNode(tr, 'td');
  idCell.style.textAlign = 'right';

  var typeCell = addNode(tr, 'td');
  var descriptionCell = addNode(tr, 'td');
  this.descriptionCell_ = descriptionCell;

  // Connect listeners.
  checkbox.onchange = this.onCheckboxToggled_.bind(this);

  var onclick = this.onClicked_.bind(this);
  idCell.onclick = onclick;
  typeCell.onclick = onclick;
  descriptionCell.onclick = onclick;

  tr.onmouseover = this.onMouseover_.bind(this);
  tr.onmouseout = this.onMouseout_.bind(this);

  // Set the cell values to match this source's data.
  if (this.sourceEntry_.getSourceId() >= 0) {
    addTextNode(idCell, this.sourceEntry_.getSourceId());
  } else {
    addTextNode(idCell, '-');
  }
  var sourceTypeString = this.sourceEntry_.getSourceTypeString();
  addTextNode(typeCell, sourceTypeString);
  this.updateDescription_();

  // Add a CSS classname specific to this source type (so CSS can specify
  // different stylings for different types).
  changeClassName(this.row_, 'source_' + sourceTypeString, true);

  this.updateClass_();
};

SourceRow.prototype.onSourceUpdated = function() {
  if (this.sourceEntry_.isInactive() != this.isInactive_ ||
      this.sourceEntry_.isError() != this.isError_) {
    this.updateClass_();
  }

  if (this.description_ != this.sourceEntry_.getDescription())
    this.updateDescription_();

  // Update filters.
  var matchesFilter = this.matchesFilter(this.parentView_.currentFilter_);
  this.setIsMatchedByFilter(matchesFilter);
};

/**
 * Changes |row_|'s class based on currently set flags.  Clears any previous
 * class set by this method.  This method is needed so that some styles
 * override others.
 */
SourceRow.prototype.updateClass_ = function() {
  this.isInactive_ = this.sourceEntry_.isInactive();
  this.isError_ = this.sourceEntry_.isError();

  // Each element of this list contains a property of |this| and the
  // corresponding class name to set if that property is true.  Entries earlier
  // in the list take precedence.
  var propertyNames = [
    ['isSelected_', 'selected'],
    ['isMouseOver_', 'mouseover'],
    ['isError_', 'error'],
    ['isInactive_', 'inactive'],
  ];

  // Loop through |propertyNames| in order, checking if each property
  // is true.  For the first such property found, if any, add the
  // corresponding class to the SourceEntry's row.  Remove classes
  // that correspond to any other property.
  var noStyleSet = true;
  for (var i = 0; i < propertyNames.length; ++i) {
    var setStyle = noStyleSet && this[propertyNames[i][0]];
    changeClassName(this.row_, propertyNames[i][1], setStyle);
    if (setStyle)
      noStyleSet = false;
  }
};

SourceRow.prototype.getSourceEntry = function() {
  return this.sourceEntry_;
};

SourceRow.prototype.setIsMatchedByFilter = function(isMatchedByFilter) {
  if (this.isMatchedByFilter() == isMatchedByFilter)
    return;  // No change.

  this.isMatchedByFilter_ = isMatchedByFilter;

  this.setFilterStyles(isMatchedByFilter);

  if (isMatchedByFilter) {
    this.parentView_.incrementPostfilterCount(1);
  } else {
    this.parentView_.incrementPostfilterCount(-1);
    // If we are filtering an entry away, make sure it is no longer
    // part of the selection.
    this.setSelected(false);
  }
};

SourceRow.prototype.isMatchedByFilter = function() {
  return this.isMatchedByFilter_;
};

SourceRow.prototype.setFilterStyles = function(isMatchedByFilter) {
  // Hide rows which have been filtered away.
  if (isMatchedByFilter) {
    this.row_.style.display = '';
  } else {
    this.row_.style.display = 'none';
  }
};

SourceRow.prototype.matchesFilter = function(filter) {
  if (filter.isActive && this.isInactive_)
    return false;
  if (filter.isInactive && !this.isInactive_)
    return false;
  if (filter.isError && !this.isError_)
    return false;
  if (filter.isNotError && this.isError_)
    return false;

  // Check source type, if needed.
  if (filter.type) {
    var sourceType = this.sourceEntry_.getSourceTypeString().toLowerCase();
    if (filter.type.indexOf(sourceType) == -1)
      return false;
  }

  // Check source ID, if needed.
  if (filter.id) {
    if (filter.id.indexOf(this.sourceEntry_.getSourceId() + '') == -1)
      return false;
  }

  if (filter.text == '')
    return true;

  var filterText = filter.text;
  var entryText = this.sourceEntry_.PrintEntriesAsText().toLowerCase();

  return entryText.indexOf(filterText) != -1;
};

SourceRow.prototype.isSelected = function() {
  return this.isSelected_;
};

SourceRow.prototype.setSelected = function(isSelected) {
  if (isSelected == this.isSelected())
    return;

  this.isSelected_ = isSelected;

  this.setSelectedStyles(isSelected);
  this.parentView_.modifySelectionArray(this, isSelected);
  this.parentView_.onSelectionChanged();
};

SourceRow.prototype.setSelectedStyles = function(isSelected) {
  this.isSelected_ = isSelected;
  this.getSelectionCheckbox().checked = isSelected;
  this.updateClass_();
};

SourceRow.prototype.setMouseoverStyle = function(isMouseOver) {
  this.isMouseOver_ = isMouseOver;
  this.updateClass_();
};

SourceRow.prototype.onClicked_ = function() {
  this.parentView_.clearSelection();
  this.setSelected(true);
};

SourceRow.prototype.onMouseover_ = function() {
  this.setMouseoverStyle(true);
};

SourceRow.prototype.onMouseout_ = function() {
  this.setMouseoverStyle(false);
};

SourceRow.prototype.updateDescription_ = function() {
  this.description_ = this.sourceEntry_.getDescription();
  this.descriptionCell_.innerHTML = '';
  addTextNode(this.descriptionCell_, this.description_);
};

SourceRow.prototype.onCheckboxToggled_ = function() {
  this.setSelected(this.getSelectionCheckbox().checked);
};

SourceRow.prototype.getSelectionCheckbox = function() {
  return this.row_.childNodes[0].firstChild;
};

/**
 * Returns source ID of the entry whose row is currently above this one's.
 * Returns null if no such node exists.
 */
SourceRow.prototype.getPreviousNodeSourceId = function() {
  var prevNode = this.row_.previousSibling;
  if (prevNode == null)
    return null;
  return prevNode._id;
};

/**
 * Returns source ID of the entry whose row is currently below this one's.
 * Returns null if no such node exists.
 */
SourceRow.prototype.getNextNodeSourceId = function() {
  var nextNode = this.row_.nextSibling;
  if (nextNode == null)
    return null;
  return nextNode._id;
};

/**
 * Moves current object's row before |entry|'s row.
 */
SourceRow.prototype.moveBefore = function(entry) {
  this.row_.parentNode.insertBefore(this.row_, entry.row_);
};

/**
 * Moves current object's row after |entry|'s row.
 */
SourceRow.prototype.moveAfter = function(entry) {
  this.row_.parentNode.insertBefore(this.row_, entry.row_.nextSibling);
};

SourceRow.prototype.remove = function() {
  this.setSelected(false);
  this.setIsMatchedByFilter(false);
  this.row_.parentNode.removeChild(this.row_);
};
<commit_msg>net-internals:  Fix regression that breaks things when a filter is active.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * A SourceRow represents the row corresponding to a single SourceEntry
 * displayed by the EventsView.
 *
 * @constructor
 */
function SourceRow(parentView, sourceEntry) {
  this.parentView_ = parentView;

  this.sourceEntry_ = sourceEntry;
  this.isSelected_ = false;
  this.isMatchedByFilter_ = false;

  // Used to set CSS class for display.  Must only be modified by calling
  // corresponding set functions.
  this.isSelected_ = false;
  this.isMouseOver_ = false;

  // Mirror sourceEntry's values, so we only update the DOM when necessary.
  this.isError_ = sourceEntry.isError();
  this.isInactive_ = sourceEntry.isInactive();
  this.description_ = sourceEntry.getDescription();

  this.createRow_();
  this.onSourceUpdated();
}

SourceRow.prototype.createRow_ = function() {
  // Create a row.
  var tr = addNode(this.parentView_.tableBody_, 'tr');
  tr._id = this.sourceEntry_.getSourceId();
  tr.style.display = 'none';
  this.row_ = tr;

  var selectionCol = addNode(tr, 'td');
  var checkbox = addNode(selectionCol, 'input');
  checkbox.type = 'checkbox';

  var idCell = addNode(tr, 'td');
  idCell.style.textAlign = 'right';

  var typeCell = addNode(tr, 'td');
  var descriptionCell = addNode(tr, 'td');
  this.descriptionCell_ = descriptionCell;

  // Connect listeners.
  checkbox.onchange = this.onCheckboxToggled_.bind(this);

  var onclick = this.onClicked_.bind(this);
  idCell.onclick = onclick;
  typeCell.onclick = onclick;
  descriptionCell.onclick = onclick;

  tr.onmouseover = this.onMouseover_.bind(this);
  tr.onmouseout = this.onMouseout_.bind(this);

  // Set the cell values to match this source's data.
  if (this.sourceEntry_.getSourceId() >= 0) {
    addTextNode(idCell, this.sourceEntry_.getSourceId());
  } else {
    addTextNode(idCell, '-');
  }
  var sourceTypeString = this.sourceEntry_.getSourceTypeString();
  addTextNode(typeCell, sourceTypeString);
  this.updateDescription_();

  // Add a CSS classname specific to this source type (so CSS can specify
  // different stylings for different types).
  changeClassName(this.row_, 'source_' + sourceTypeString, true);

  this.updateClass_();
};

SourceRow.prototype.onSourceUpdated = function() {
  if (this.sourceEntry_.isInactive() != this.isInactive_ ||
      this.sourceEntry_.isError() != this.isError_) {
    this.updateClass_();
  }

  if (this.description_ != this.sourceEntry_.getDescription())
    this.updateDescription_();

  // Update filters.
  var matchesFilter = this.matchesFilter(this.parentView_.currentFilter_);
  this.setIsMatchedByFilter(matchesFilter);
};

/**
 * Changes |row_|'s class based on currently set flags.  Clears any previous
 * class set by this method.  This method is needed so that some styles
 * override others.
 */
SourceRow.prototype.updateClass_ = function() {
  this.isInactive_ = this.sourceEntry_.isInactive();
  this.isError_ = this.sourceEntry_.isError();

  // Each element of this list contains a property of |this| and the
  // corresponding class name to set if that property is true.  Entries earlier
  // in the list take precedence.
  var propertyNames = [
    ['isSelected_', 'selected'],
    ['isMouseOver_', 'mouseover'],
    ['isError_', 'error'],
    ['isInactive_', 'inactive'],
  ];

  // Loop through |propertyNames| in order, checking if each property
  // is true.  For the first such property found, if any, add the
  // corresponding class to the SourceEntry's row.  Remove classes
  // that correspond to any other property.
  var noStyleSet = true;
  for (var i = 0; i < propertyNames.length; ++i) {
    var setStyle = noStyleSet && this[propertyNames[i][0]];
    changeClassName(this.row_, propertyNames[i][1], setStyle);
    if (setStyle)
      noStyleSet = false;
  }
};

SourceRow.prototype.getSourceEntry = function() {
  return this.sourceEntry_;
};

SourceRow.prototype.setIsMatchedByFilter = function(isMatchedByFilter) {
  if (this.isMatchedByFilter() == isMatchedByFilter)
    return;  // No change.

  this.isMatchedByFilter_ = isMatchedByFilter;

  this.setFilterStyles(isMatchedByFilter);

  if (isMatchedByFilter) {
    this.parentView_.incrementPostfilterCount(1);
  } else {
    this.parentView_.incrementPostfilterCount(-1);
    // If we are filtering an entry away, make sure it is no longer
    // part of the selection.
    this.setSelected(false);
  }
};

SourceRow.prototype.isMatchedByFilter = function() {
  return this.isMatchedByFilter_;
};

SourceRow.prototype.setFilterStyles = function(isMatchedByFilter) {
  // Hide rows which have been filtered away.
  if (isMatchedByFilter) {
    this.row_.style.display = '';
  } else {
    this.row_.style.display = 'none';
  }
};

SourceRow.prototype.matchesFilter = function(filter) {
  if (filter.isActive && this.isInactive_)
    return false;
  if (filter.isInactive && !this.isInactive_)
    return false;
  if (filter.isError && !this.isError_)
    return false;
  if (filter.isNotError && this.isError_)
    return false;

  // Check source type, if needed.
  if (filter.type) {
    var sourceType = this.sourceEntry_.getSourceTypeString().toLowerCase();
    if (filter.type.indexOf(sourceType) == -1)
      return false;
  }

  // Check source ID, if needed.
  if (filter.id) {
    if (filter.id.indexOf(this.sourceEntry_.getSourceId() + '') == -1)
      return false;
  }

  if (filter.text == '')
    return true;

  var filterText = filter.text;
  var entryText = this.sourceEntry_.printAsText().toLowerCase();

  return entryText.indexOf(filterText) != -1;
};

SourceRow.prototype.isSelected = function() {
  return this.isSelected_;
};

SourceRow.prototype.setSelected = function(isSelected) {
  if (isSelected == this.isSelected())
    return;

  this.isSelected_ = isSelected;

  this.setSelectedStyles(isSelected);
  this.parentView_.modifySelectionArray(this, isSelected);
  this.parentView_.onSelectionChanged();
};

SourceRow.prototype.setSelectedStyles = function(isSelected) {
  this.isSelected_ = isSelected;
  this.getSelectionCheckbox().checked = isSelected;
  this.updateClass_();
};

SourceRow.prototype.setMouseoverStyle = function(isMouseOver) {
  this.isMouseOver_ = isMouseOver;
  this.updateClass_();
};

SourceRow.prototype.onClicked_ = function() {
  this.parentView_.clearSelection();
  this.setSelected(true);
};

SourceRow.prototype.onMouseover_ = function() {
  this.setMouseoverStyle(true);
};

SourceRow.prototype.onMouseout_ = function() {
  this.setMouseoverStyle(false);
};

SourceRow.prototype.updateDescription_ = function() {
  this.description_ = this.sourceEntry_.getDescription();
  this.descriptionCell_.innerHTML = '';
  addTextNode(this.descriptionCell_, this.description_);
};

SourceRow.prototype.onCheckboxToggled_ = function() {
  this.setSelected(this.getSelectionCheckbox().checked);
};

SourceRow.prototype.getSelectionCheckbox = function() {
  return this.row_.childNodes[0].firstChild;
};

/**
 * Returns source ID of the entry whose row is currently above this one's.
 * Returns null if no such node exists.
 */
SourceRow.prototype.getPreviousNodeSourceId = function() {
  var prevNode = this.row_.previousSibling;
  if (prevNode == null)
    return null;
  return prevNode._id;
};

/**
 * Returns source ID of the entry whose row is currently below this one's.
 * Returns null if no such node exists.
 */
SourceRow.prototype.getNextNodeSourceId = function() {
  var nextNode = this.row_.nextSibling;
  if (nextNode == null)
    return null;
  return nextNode._id;
};

/**
 * Moves current object's row before |entry|'s row.
 */
SourceRow.prototype.moveBefore = function(entry) {
  this.row_.parentNode.insertBefore(this.row_, entry.row_);
};

/**
 * Moves current object's row after |entry|'s row.
 */
SourceRow.prototype.moveAfter = function(entry) {
  this.row_.parentNode.insertBefore(this.row_, entry.row_.nextSibling);
};

SourceRow.prototype.remove = function() {
  this.setSelected(false);
  this.setIsMatchedByFilter(false);
  this.row_.parentNode.removeChild(this.row_);
};
<|endoftext|>


<commit_before>const $ = require('jquery');
const { AppSetting } = require('../../core/app_setting');
const { BagItProfile } = require('../../bagit/bagit_profile');
const { Choice } = require('../../ui/forms/choice');
const { Constants } = require('../../core/constants');
const { Context } = require('../../core/context');
const { Field } = require('./field');
const { InternalSetting } = require('../../core/internal_setting');
const os = require('os');
const { RemoteRepository } = require('../../core/remote_repository');
const { StorageService } = require('../../core/storage_service');
const { Util } = require('../../core/util');

/**
 * SetupQuestion is a question to be asked by a setup module. DART presents
 * these questions one at a time, and the user must provide a valid answer
 * before proceeding to the next question.
 *
 * You'll typically want to copy the responses from these questions into
 * some persistent DART setting.
 *
 * @param {object} opts - An object containing setup options.
 *
 * @param {string} opts.question - The question to ask the user.
 *
 * @param {object} opts.mapsToProperty - An object that describes where the
 * initial value for this question should come from and where it should be
 * saved. See {@link SetupQuestion#mapsToProperty}, which includes a full
 * description and examples.
 *
 * @param {string} opts.error - An error message to display if the user's
 * response is invalid.
 *
 * @param {string} [opts.heading] - An optional heading to display above
 * the question.
 *
 * @param {Array<string>} [opts.options] - An optional list of allowed
 * values. If specified, the user will see an HTML select list instead of
 * a text input. The select list will contain these values and an empty
 * option.
 *
 * @param {string} [opts.dataType] - The type to which the user's response
 * should be cast. Valid values are 'number' and 'boolean'. If the dataType
 * is string you don't need to include this param, as values are strings
 * by default.
 *
 * @param {string} [opts.validation] - An object describing how to validate
 * the user's response to this question. See {@link SetupQuestion#validation}
 * for more info.
 *
 */
class SetupQuestion extends Field {
    constructor(opts) {
        let rand = 'setup_' + Math.random().toString().replace(/^0\./, '');
        super(rand, rand, opts.question, opts.initialValue)
        /**
         * The heading that will appear above the question on the
         * page that displays the question to the user.
         *
         * @type {string}
         */
        this.heading = opts.heading;
        /**
         * The data type to which the user's response should be cast.
         * Can be 'string', 'number', or 'boolean'.
         *
         * @type {string}
         * @default 'string'
         */
        this.dataType = opts.dataType || 'string';
        /**
         * The error message to display if the user provides an invalid
         * response to the question.
         *
         * @type {string}
         */
        this.error = opts.error || Context.y18n.__("The response is not valid.");
        /**
         * An object that describes how this question should be validated.
         * The object has the following properties.
         *
         *
         * required: Set this to true if the user must respond. Default is
         * false.
         *
         * pattern: A regular expression pattern against which to validate the
         * user's response. You can set this to a quoted pattern like "/\\w+/"
         * or use one of the values defined in Constants, such as:
         *
         * - {@link Constants.RE_DOMAIN}
         * - {@link Constants.RE_EMAIL}
         * - {@link Constants.RE_FILE_PATH_POSIX}
         * - {@link Constants.RE_FILE_PATH_WINDOWS}
         * - {@link Constants.RE_FILE_PATH_ANY_OS}
         * - {@link Constants.RE_IPV4}
         *
         * min: If the response should be numeric, this specifies that it must
         * be greater than or equal to this value.
         *
         * max: If the response should be numeric, this specifies that it must
         * be less than or equal to this value.
         *
         * @example
         *
         * @type {object}
         */
        this.validation = opts.validation;
        /**
         * An object that defines what setting value or BagItProfile tag
         * value the question maps to. When the question appears, it will
         * take its initial value from the specified setting/property, or
         * from the specified BagItProfile tag value. When the user provides
         * a valid response, the value of that response will be assigned to
         * the setting/property or tag value.
         *
         * @example
         *
         * // Map this question to the value property of an AppSetting
         * opts.mapsToProperty = {
         *    type: 'AppSetting',
         *    id: 'e8a7556f-ba6b-413f-9e8a-1682a5134ebb',
         *    property: 'value'
         * }
         *
         * // Map this question to the password property of a StorageService
         * opts.mapsToProperty = {
         *    type: 'StorageService',
         *    id: 'c329c0e9-8d0c-4a8b-b7e7-45ff084a90c1',
         *    property: 'password'
         * }
         *
         * // Map this property to the Source-Organization tag in the
         * // bag-info.txt file of a BagItProfile
         * opts.mapsToProperty = {
         *    type: 'BagItProfile',
         *    id: '271b9309-2c85-4821-aeb2-7ae209c7aaa8',
         *    tagFile: 'bag-info.txt',
         *    tagName: 'Source-Organization'
         * }
         *
         * @type {object}
         */
        this.mapsToProperty = opts.mapsToProperty;
        /**
         * An optional list of allowed values to choose from. These will
         * be converted into a list of Choice objects in the base
         * {@link Field} class.
         *
         * @type {Array<string>}
         */
        this.options;

        if (Array.isArray(opts.options)) {
            this.options = opts.options;
        }
    }

    /**
     * Given a list list of strings, this builds the list of
     * {@Link Choice} objects that will appear as options in the
     * HTML select list.
     *
     * @para {Array<string>} options
     */
    set options(options) {
        this.setInitialValue();
        this.choices = Choice.makeList(options, this.value, true);
    }

    /**
     * This returns the value that the user entered in the form input
     * field. The returned value will be cast to
     * @{link SetupQuestion#dataType}.
     *
     * @returns {string|number|boolean}
     */
    readUserInput() {
        let field = this;
        let formValue = $(`#${field.id}`).val() || '';
        return Util.cast(formValue.trim(), this.dataType);
    }

    /**
     * This reads the value the user input on the form. If the input
     * is valid, assigns the value of the input to the property or
     * profile tag to which this question is mapped.
     *
     * @returns {boolean}
     */
    processResponse() {
        this.value = this.readUserInput();
        if (this.validate()) {
            this.saveValue();
            return true;
        }
        return false;
    }

    /**
     * This sets the initial value in the HTML text input or select list.
     * The value of the setting comes from the value of the property of an
     * AppSetting, InternalSetting, RemoteRepository, StorageService, or
     * BagItProfile, as specified in  {@link SetupQuestion#mapsToProperty}
     *
     */
    setInitialValue() {
        let obj = this._getMappedObject();
        if (this.mapsToProperty.tagFile && this.mapsToProperty.tagName) {
            let tags = obj.getTagsFromFile(this.mapsToProperty.tagFile, this.mapsToProperty.tagName);
            if (!tags || tags.length == 0) {
                throw new Error(Context.y18n.__('BagIt Profile has no tags where tag file = %s and tag name = %s', this.mapsToProperty.tagFile, this.mapsToProperty.tagName));
            }
            this.value = tags[0].getValue();
        } else {
            this.value = obj[this.mapsToProperty.property];
        }
    }

    /**
     * This returns an object of type AppSetting, InternalSetting,
     * RemoteRepository, StorageService, or
     * BagItProfile, as specified in  {@link SetupQuestion#mapsToProperty}
     *
     * @private
     */
    _getMappedObject() {
        let obj;
        Context.logger.debug(`Looking for ${this.mapsToProperty.type} with id ${this.mapsToProperty.id}`);
        switch(this.mapsToProperty.type) {
        case 'AppSetting':
            obj = AppSetting.find(this.mapsToProperty.id);
            break;
        case 'BagItProfile':
            obj = BagItProfile.find(this.mapsToProperty.id);
            break;
        case 'InternalSetting':
            obj = AppSetting.find(this.mapsToProperty.id);
            break;
        case 'RemoteRepository':
            obj = RemoteRepository.find(this.mapsToProperty.id);
            break;
        case 'StorageService':
            obj = StorageService.find(this.mapsToProperty.id);
            break;
        default:
            throw new Error(Context.y18n.__("Unknown type: %s"), this.mapsToProperty.type);
        }
        if(!obj) {
            throw new Error(Context.y18n.__('Could not find object of type %s with id %s', this.mapsToProperty.type, this.mapsToProperty.id));
        }
        return obj;
    }

    /**
     * This saves the value the user entered into the HTML text input or
     * select list. The value is saved to the property of an
     * AppSetting, InternalSetting, RemoteRepository, StorageService, or
     * BagItProfile, as specified in {@link SetupQuestion#mapsToProperty}
     *
     */
    saveValue() {
        let obj = this._getMappedObject();
        if (this.mapsToProperty.tagFile && this.mapsToProperty.tagName) {
            let tags = obj.getTagsFromFile(this.mapsToProperty.tagFile, this.mapsToProperty.tagName);
            tags[0].userValue = this.value;
        } else {
            obj[this.mapsToProperty.property] = this.value;
        }
        obj.save();
    }

    /**
     * Returns true if this question's current {@link SetupQuestion#value} is
     * valid, false if not.
     *
     * @returns {boolean}
     */
    validate() {
        let validators = this.getValidators();
        for (let isValid of validators) {
            if (!isValid(this.value)) {
                return false;
            }
        }
        return true;
    }

    /**
     * This returns a list of validation functions based on the requirements
     * specified in this.validators. Each of the functions in the returned
     * array takes a single param, value, and returns true or false to indicate
     * whether the value is valid.
     *
     * @returns {Array<function>}
     */
    getValidators() {
        let validators = [];
        this._ensureMinMax();
        this._setValidationRegExp();
        if (this.validation) {
            if (this.validation.required) {
                validators.push(SetupQuestion.getRequiredValidator());
            }
            if (this.looksLikeNumericValidation()) {
                validators.push(SetupQuestion.getIntRangeValidator(
                    this.validation.min,
                    this.validation.max,
                    !this.validation.required))
            }
            if (this.validation.regexp) {
                validators.push(SetupQuestion.getPatternValidator(
                    this.validation.regexp,
                    !this.validation.required))
            }
        }
        return validators;
    }

    /**
     * This sets the RegExp object for validation if the validation object
     * includes a pattern property. If validation.pattern is set to any of
     * the following strings, this assigns the RegExp from the Constants
     * module: 'RE_DOMAIN', 'RE_IPV4', 'RE_FILE_PATH_POSIX',
     * 'RE_FILE_PATH_WINDOWS', 'RE_FILE_PATH_ANY_OS', 'RE_EMAIL'.
     *
     * If validation.pattern is set to any other string, this converts the
     * string to a regexp.
     *
     * The resulting RegExp is assigned to this.validation.regexp.
     *
     * @pattern
     */
    _setValidationRegExp() {
        if (this.validation && this.validation.pattern) {
            if (this.validation.pattern.startsWith('RE_')) {
                this.validation.regexp = Constants[this.validation.pattern];
            } else {
                this.validation.regexp = new RegExp(this.validation.pattern);
            }
        }
    }

    /**
     * If this question's validation requirements include either a min
     * or a max value, this ensures that it includes BOTH a min and max
     * value, so we can use the {@link SetupQuestion.getIntRangeValidator}
     * to validate the response.
     *
     * If this question does not have validation requirements, or the
     * validation requirements include neither min nor max, this method
     * does nothing.
     *
     * @private
     */
    _ensureMinMax() {
        if (this.looksLikeNumericValidation()) {
            if (typeof this.validation.min != 'number') {
                this.validation.min = Number.MIN_SAFE_INTEGER;
            }
            if (typeof this.validation.max != 'number') {
                this.validation.max = Number.MAX_SAFE_INTEGER;
            }
        }
    }

    /**
     * Returns true if this question has validation requirements that include
     * a min and or max value.
     *
     * @returns {boolean}
     */
    looksLikeNumericValidation() {
        return (this.validation && (typeof this.validation.min == 'number' || typeof this.validation.max == 'number'))
    }

    /**
     * Returns a validator function that tests whether a value is empty.
     * The validator function returns false if it gets an empty value,
     * true otherwise.
     *
     * @returns {function}
     */
    static getRequiredValidator() {
        return function(value) {
            return !Util.isEmpty(value);
        }
    }

    /**
     * Returns a validator function that tests whether a value matches
     * a regular expression pattern. The validator function returns true
     * if the value matches the pattern false otherwise.
     *
     * The Constants module contains a number of pre-defined patterns,
     * including:
     *
     * - {@link Constants.RE_DOMAIN}
     * - {@link Constants.RE_EMAIL}
     * - {@link Constants.RE_FILE_PATH_POSIX}
     * - {@link Constants.RE_FILE_PATH_WINDOWS}
     * - {@link Constants.RE_FILE_PATH_ANY_OS}
     * - {@link Constants.RE_IPV4}
     *
     * @param {RegExp} pattern - The pattern to test against.
     *
     * @param {boolean} emptyOk - Are empty response allowed? Default is false.
     *
     * @returns {function}
     */
    static getPatternValidator(pattern, emptyOk = false) {
        return function(value) {
            return emptyOk && Util.isEmpty(value) || value.match(pattern) !== null;
        }
    }

    /**
     * Returns a validator function that checks whether an interger value
     * is within a given range. The function returns true or false.
     *
     * @param {number} min - The minimum value. A valid integer must be
     * greater than or equal to this.
     *
     * @param {number} max - The maximum value. A valid integer must be
     * less than or equal to this.
     *
     * @param {boolean} emptyOk - Are empty response allowed? Default is false.
     *
     * @returns {function}
     */
    static getIntRangeValidator(min, max, emptyOk = false) {
        return function(value) {
            if (emptyOk && Util.isEmpty(value)) {
                return true;
            }
            let intValue = NaN;
            if (typeof value === 'string') {
                intValue = parseInt(value, 10);
            } else if (typeof value === 'number') {
                intValue = Math.floor(value);
            }
            if (isNaN(intValue)) {
                return false
            }
            return intValue >= min && intValue <= max;
        }
    }
}

module.exports.SetupQuestion = SetupQuestion;
<commit_msg>Added some examples of how to create a validator<commit_after>const $ = require('jquery');
const { AppSetting } = require('../../core/app_setting');
const { BagItProfile } = require('../../bagit/bagit_profile');
const { Choice } = require('../../ui/forms/choice');
const { Constants } = require('../../core/constants');
const { Context } = require('../../core/context');
const { Field } = require('./field');
const { InternalSetting } = require('../../core/internal_setting');
const os = require('os');
const { RemoteRepository } = require('../../core/remote_repository');
const { StorageService } = require('../../core/storage_service');
const { Util } = require('../../core/util');

/**
 * SetupQuestion is a question to be asked by a setup module. DART presents
 * these questions one at a time, and the user must provide a valid answer
 * before proceeding to the next question.
 *
 * You'll typically want to copy the responses from these questions into
 * some persistent DART setting.
 *
 * @param {object} opts - An object containing setup options.
 *
 * @param {string} opts.question - The question to ask the user.
 *
 * @param {object} opts.mapsToProperty - An object that describes where the
 * initial value for this question should come from and where it should be
 * saved. See {@link SetupQuestion#mapsToProperty}, which includes a full
 * description and examples.
 *
 * @param {string} opts.error - An error message to display if the user's
 * response is invalid.
 *
 * @param {string} [opts.heading] - An optional heading to display above
 * the question.
 *
 * @param {Array<string>} [opts.options] - An optional list of allowed
 * values. If specified, the user will see an HTML select list instead of
 * a text input. The select list will contain these values and an empty
 * option.
 *
 * @param {string} [opts.dataType] - The type to which the user's response
 * should be cast. Valid values are 'number' and 'boolean'. If the dataType
 * is string you don't need to include this param, as values are strings
 * by default.
 *
 * @param {string} [opts.validation] - An object describing how to validate
 * the user's response to this question. See {@link SetupQuestion#validation}
 * for more info.
 *
 */
class SetupQuestion extends Field {
    constructor(opts) {
        let rand = 'setup_' + Math.random().toString().replace(/^0\./, '');
        super(rand, rand, opts.question, opts.initialValue)
        /**
         * The heading that will appear above the question on the
         * page that displays the question to the user.
         *
         * @type {string}
         */
        this.heading = opts.heading;
        /**
         * The data type to which the user's response should be cast.
         * Can be 'string', 'number', or 'boolean'.
         *
         * @type {string}
         * @default 'string'
         */
        this.dataType = opts.dataType || 'string';
        /**
         * The error message to display if the user provides an invalid
         * response to the question.
         *
         * @type {string}
         */
        this.error = opts.error || Context.y18n.__("The response is not valid.");
        /**
         * An object that describes how this question should be validated.
         * The object has the following properties.
         *
         *
         * required: Set this to true if the user must respond. Default is
         * false.
         *
         * pattern: A regular expression pattern against which to validate the
         * user's response. You can set this to a quoted pattern like "/\\w+/"
         * or use one of the values defined in Constants, such as:
         *
         * - {@link Constants.RE_DOMAIN}
         * - {@link Constants.RE_EMAIL}
         * - {@link Constants.RE_FILE_PATH_POSIX}
         * - {@link Constants.RE_FILE_PATH_WINDOWS}
         * - {@link Constants.RE_FILE_PATH_ANY_OS}
         * - {@link Constants.RE_IPV4}
         *
         * min: If the response should be numeric, this specifies that it must
         * be greater than or equal to this value.
         *
         * max: If the response should be numeric, this specifies that it must
         * be less than or equal to this value.
         *
         * @example
         *
         * // Create a validator that requires an answer and will accept
         * // any answer (no min, max, or pattern).
         * opts.validator = {
         *     required: true
         * }
         *
         * // Validator that does not require a response, but if given, the
         * // response must be an email address. Note that RE_EMAIL comes
         * // from the Constants module.
         * opts.validator = {
         *     pattern: 'RE_EMAIL'
         * }
         *
         * // Validator that requires a string of 4 or more hex digits.
         * // The string pattern will be converted to a RegExp.
         * opts.validator = {
         *     required: true,
         *     pattern: '[a-fA-F0-9]{4,}'
         * }
         *
         * // Validator requiring a number between 1 and 100 inclusive.
         * opts.validator = {
         *     required: true,
         *     min: 1,
         *     max: 100
         * }
         *
         *
         * @type {object}
         */
        this.validation = opts.validation;
        /**
         * An object that defines what setting value or BagItProfile tag
         * value the question maps to. When the question appears, it will
         * take its initial value from the specified setting/property, or
         * from the specified BagItProfile tag value. When the user provides
         * a valid response, the value of that response will be assigned to
         * the setting/property or tag value.
         *
         * @example
         *
         * // Map this question to the value property of an AppSetting
         * opts.mapsToProperty = {
         *    type: 'AppSetting',
         *    id: 'e8a7556f-ba6b-413f-9e8a-1682a5134ebb',
         *    property: 'value'
         * }
         *
         * // Map this question to the password property of a StorageService
         * opts.mapsToProperty = {
         *    type: 'StorageService',
         *    id: 'c329c0e9-8d0c-4a8b-b7e7-45ff084a90c1',
         *    property: 'password'
         * }
         *
         * // Map this property to the Source-Organization tag in the
         * // bag-info.txt file of a BagItProfile
         * opts.mapsToProperty = {
         *    type: 'BagItProfile',
         *    id: '271b9309-2c85-4821-aeb2-7ae209c7aaa8',
         *    tagFile: 'bag-info.txt',
         *    tagName: 'Source-Organization'
         * }
         *
         * @type {object}
         */
        this.mapsToProperty = opts.mapsToProperty;
        /**
         * An optional list of allowed values to choose from. These will
         * be converted into a list of Choice objects in the base
         * {@link Field} class.
         *
         * @type {Array<string>}
         */
        this.options;

        if (Array.isArray(opts.options)) {
            this.options = opts.options;
        }
    }

    /**
     * Given a list list of strings, this builds the list of
     * {@Link Choice} objects that will appear as options in the
     * HTML select list.
     *
     * @para {Array<string>} options
     */
    set options(options) {
        this.setInitialValue();
        this.choices = Choice.makeList(options, this.value, true);
    }

    /**
     * This returns the value that the user entered in the form input
     * field. The returned value will be cast to
     * @{link SetupQuestion#dataType}.
     *
     * @returns {string|number|boolean}
     */
    readUserInput() {
        let field = this;
        let formValue = $(`#${field.id}`).val() || '';
        return Util.cast(formValue.trim(), this.dataType);
    }

    /**
     * This reads the value the user input on the form. If the input
     * is valid, assigns the value of the input to the property or
     * profile tag to which this question is mapped.
     *
     * @returns {boolean}
     */
    processResponse() {
        this.value = this.readUserInput();
        if (this.validate()) {
            this.saveValue();
            return true;
        }
        return false;
    }

    /**
     * This sets the initial value in the HTML text input or select list.
     * The value of the setting comes from the value of the property of an
     * AppSetting, InternalSetting, RemoteRepository, StorageService, or
     * BagItProfile, as specified in  {@link SetupQuestion#mapsToProperty}
     *
     */
    setInitialValue() {
        let obj = this._getMappedObject();
        if (this.mapsToProperty.tagFile && this.mapsToProperty.tagName) {
            let tags = obj.getTagsFromFile(this.mapsToProperty.tagFile, this.mapsToProperty.tagName);
            if (!tags || tags.length == 0) {
                throw new Error(Context.y18n.__('BagIt Profile has no tags where tag file = %s and tag name = %s', this.mapsToProperty.tagFile, this.mapsToProperty.tagName));
            }
            this.value = tags[0].getValue();
        } else {
            this.value = obj[this.mapsToProperty.property];
        }
    }

    /**
     * This returns an object of type AppSetting, InternalSetting,
     * RemoteRepository, StorageService, or
     * BagItProfile, as specified in  {@link SetupQuestion#mapsToProperty}
     *
     * @private
     */
    _getMappedObject() {
        let obj;
        Context.logger.debug(`Looking for ${this.mapsToProperty.type} with id ${this.mapsToProperty.id}`);
        switch(this.mapsToProperty.type) {
        case 'AppSetting':
            obj = AppSetting.find(this.mapsToProperty.id);
            break;
        case 'BagItProfile':
            obj = BagItProfile.find(this.mapsToProperty.id);
            break;
        case 'InternalSetting':
            obj = AppSetting.find(this.mapsToProperty.id);
            break;
        case 'RemoteRepository':
            obj = RemoteRepository.find(this.mapsToProperty.id);
            break;
        case 'StorageService':
            obj = StorageService.find(this.mapsToProperty.id);
            break;
        default:
            throw new Error(Context.y18n.__("Unknown type: %s"), this.mapsToProperty.type);
        }
        if(!obj) {
            throw new Error(Context.y18n.__('Could not find object of type %s with id %s', this.mapsToProperty.type, this.mapsToProperty.id));
        }
        return obj;
    }

    /**
     * This saves the value the user entered into the HTML text input or
     * select list. The value is saved to the property of an
     * AppSetting, InternalSetting, RemoteRepository, StorageService, or
     * BagItProfile, as specified in {@link SetupQuestion#mapsToProperty}
     *
     */
    saveValue() {
        let obj = this._getMappedObject();
        if (this.mapsToProperty.tagFile && this.mapsToProperty.tagName) {
            let tags = obj.getTagsFromFile(this.mapsToProperty.tagFile, this.mapsToProperty.tagName);
            tags[0].userValue = this.value;
        } else {
            obj[this.mapsToProperty.property] = this.value;
        }
        obj.save();
    }

    /**
     * Returns true if this question's current {@link SetupQuestion#value} is
     * valid, false if not.
     *
     * @returns {boolean}
     */
    validate() {
        let validators = this.getValidators();
        for (let isValid of validators) {
            if (!isValid(this.value)) {
                return false;
            }
        }
        return true;
    }

    /**
     * This returns a list of validation functions based on the requirements
     * specified in this.validators. Each of the functions in the returned
     * array takes a single param, value, and returns true or false to indicate
     * whether the value is valid.
     *
     * @returns {Array<function>}
     */
    getValidators() {
        let validators = [];
        this._ensureMinMax();
        this._setValidationRegExp();
        if (this.validation) {
            if (this.validation.required) {
                validators.push(SetupQuestion.getRequiredValidator());
            }
            if (this.looksLikeNumericValidation()) {
                validators.push(SetupQuestion.getIntRangeValidator(
                    this.validation.min,
                    this.validation.max,
                    !this.validation.required))
            }
            if (this.validation.regexp) {
                validators.push(SetupQuestion.getPatternValidator(
                    this.validation.regexp,
                    !this.validation.required))
            }
        }
        return validators;
    }

    /**
     * This sets the RegExp object for validation if the validation object
     * includes a pattern property. If validation.pattern is set to any of
     * the following strings, this assigns the RegExp from the Constants
     * module: 'RE_DOMAIN', 'RE_IPV4', 'RE_FILE_PATH_POSIX',
     * 'RE_FILE_PATH_WINDOWS', 'RE_FILE_PATH_ANY_OS', 'RE_EMAIL'.
     *
     * If validation.pattern is set to any other string, this converts the
     * string to a regexp.
     *
     * The resulting RegExp is assigned to this.validation.regexp.
     *
     * @pattern
     */
    _setValidationRegExp() {
        if (this.validation && this.validation.pattern) {
            if (this.validation.pattern.startsWith('RE_')) {
                this.validation.regexp = Constants[this.validation.pattern];
            } else {
                this.validation.regexp = new RegExp(this.validation.pattern);
            }
        }
    }

    /**
     * If this question's validation requirements include either a min
     * or a max value, this ensures that it includes BOTH a min and max
     * value, so we can use the {@link SetupQuestion.getIntRangeValidator}
     * to validate the response.
     *
     * If this question does not have validation requirements, or the
     * validation requirements include neither min nor max, this method
     * does nothing.
     *
     * @private
     */
    _ensureMinMax() {
        if (this.looksLikeNumericValidation()) {
            if (typeof this.validation.min != 'number') {
                this.validation.min = Number.MIN_SAFE_INTEGER;
            }
            if (typeof this.validation.max != 'number') {
                this.validation.max = Number.MAX_SAFE_INTEGER;
            }
        }
    }

    /**
     * Returns true if this question has validation requirements that include
     * a min and or max value.
     *
     * @returns {boolean}
     */
    looksLikeNumericValidation() {
        return (this.validation && (typeof this.validation.min == 'number' || typeof this.validation.max == 'number'))
    }

    /**
     * Returns a validator function that tests whether a value is empty.
     * The validator function returns false if it gets an empty value,
     * true otherwise.
     *
     * @returns {function}
     */
    static getRequiredValidator() {
        return function(value) {
            return !Util.isEmpty(value);
        }
    }

    /**
     * Returns a validator function that tests whether a value matches
     * a regular expression pattern. The validator function returns true
     * if the value matches the pattern false otherwise.
     *
     * The Constants module contains a number of pre-defined patterns,
     * including:
     *
     * - {@link Constants.RE_DOMAIN}
     * - {@link Constants.RE_EMAIL}
     * - {@link Constants.RE_FILE_PATH_POSIX}
     * - {@link Constants.RE_FILE_PATH_WINDOWS}
     * - {@link Constants.RE_FILE_PATH_ANY_OS}
     * - {@link Constants.RE_IPV4}
     *
     * @param {RegExp} pattern - The pattern to test against.
     *
     * @param {boolean} emptyOk - Are empty response allowed? Default is false.
     *
     * @returns {function}
     */
    static getPatternValidator(pattern, emptyOk = false) {
        return function(value) {
            return emptyOk && Util.isEmpty(value) || value.match(pattern) !== null;
        }
    }

    /**
     * Returns a validator function that checks whether an interger value
     * is within a given range. The function returns true or false.
     *
     * @param {number} min - The minimum value. A valid integer must be
     * greater than or equal to this.
     *
     * @param {number} max - The maximum value. A valid integer must be
     * less than or equal to this.
     *
     * @param {boolean} emptyOk - Are empty response allowed? Default is false.
     *
     * @returns {function}
     */
    static getIntRangeValidator(min, max, emptyOk = false) {
        return function(value) {
            if (emptyOk && Util.isEmpty(value)) {
                return true;
            }
            let intValue = NaN;
            if (typeof value === 'string') {
                intValue = parseInt(value, 10);
            } else if (typeof value === 'number') {
                intValue = Math.floor(value);
            }
            if (isNaN(intValue)) {
                return false
            }
            return intValue >= min && intValue <= max;
        }
    }
}

module.exports.SetupQuestion = SetupQuestion;
<|endoftext|>


<commit_before>/**
 * @license RequireJS domReady 2.0.1 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
 * Available via the MIT or new BSD license.
 * see: http://github.com/requirejs/domReady for details
 */
/*jslint */
/*global require: false, define: false, requirejs: false,
  window: false, clearInterval: false, document: false,
  self: false, setInterval: false */

'use strict';

var isTop, testDiv, scrollIntervalId,
  isBrowser = typeof window !== 'undefined' && window.document,
  isPageLoaded = !isBrowser,
  doc = isBrowser ? document : null,
  readyCalls = [];

function runCallbacks(callbacks) {
  var i;
  for (i = 0; i < callbacks.length; i += 1) {
    callbacks[i](doc);
  }
}

function callReady() {
  var callbacks = readyCalls;

  if (isPageLoaded) {
    //Call the DOM ready callbacks
    if (callbacks.length) {
      readyCalls = [];
      runCallbacks(callbacks);
    }
  }
}

/**
 * Sets the page as loaded.
 */
function pageLoaded() {
  if (!isPageLoaded) {
    isPageLoaded = true;
    if (scrollIntervalId) {
      clearInterval(scrollIntervalId);
    }

    callReady();
  }
}

if (isBrowser) {
  if (document.addEventListener) {
    //Standards. Hooray! Assumption here that if standards based,
    //it knows about DOMContentLoaded.
    document.addEventListener('DOMContentLoaded', pageLoaded, false);
    window.addEventListener('load', pageLoaded, false);
  } else if (window.attachEvent) {
    window.attachEvent('onload', pageLoaded);

    testDiv = document.createElement('div');
    try {
      isTop = window.frameElement === null;
    } catch (e) {}

    //DOMContentLoaded approximation that uses a doScroll, as found by
    //Diego Perini: http://javascript.nwbox.com/IEContentLoaded/,
    //but modified by other contributors, including jdalton
    if (testDiv.doScroll && isTop && window.external) {
      scrollIntervalId = setInterval(function() {
        try {
          testDiv.doScroll();
          pageLoaded();
        } catch (e) {}
      }, 30);
    }
  }

  //Check if document already complete, and if so, just trigger page load
  //listeners. Latest webkit browsers also use 'interactive', and
  //will fire the onDOMContentLoaded before 'interactive' but not after
  //entering 'interactive' or 'complete'. More details:
  //http://dev.w3.org/html5/spec/the-end.html#the-end
  //http://stackoverflow.com/questions/3665561/document-readystate-of-interactive-vs-ondomcontentloaded
  //Hmm, this is more complicated on further use, see 'firing too early'
  //bug: https://github.com/requirejs/domReady/issues/1
  //so removing the || document.readyState === 'interactive' test.
  //There is still a window.onload binding that should get fired if
  //DOMContentLoaded is missed.
  if (document.readyState === 'complete') {
    pageLoaded();
  }
}

/** START OF PUBLIC API **/

/**
 * Registers a callback for DOM ready. If DOM is already ready, the
 * callback is called immediately.
 * @param {Function} callback
 */
function domReady(callback) {
  if (isPageLoaded) {
    callback(doc);
  } else {
    readyCalls.push(callback);
  }
  return domReady;
}

domReady.version = '2.0.1';

/**
 * Loader Plugin API method
 */
domReady.load = function(name, req, onLoad, config) {
  if (config.isBuild) {
    onLoad(null);
  } else {
    domReady(onLoad);
  }
};

/** END OF PUBLIC API **/

module.exports = domReady;
<commit_msg>rm jshint meta from domready<commit_after>/**
 * @license RequireJS domReady 2.0.1 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
 * Available via the MIT or new BSD license.
 * see: http://github.com/requirejs/domReady for details
 */

'use strict';

var isTop, testDiv, scrollIntervalId,
  isBrowser = typeof window !== 'undefined' && window.document,
  isPageLoaded = !isBrowser,
  doc = isBrowser ? document : null,
  readyCalls = [];

function runCallbacks(callbacks) {
  var i;
  for (i = 0; i < callbacks.length; i += 1) {
    callbacks[i](doc);
  }
}

function callReady() {
  var callbacks = readyCalls;

  if (isPageLoaded) {
    //Call the DOM ready callbacks
    if (callbacks.length) {
      readyCalls = [];
      runCallbacks(callbacks);
    }
  }
}

/**
 * Sets the page as loaded.
 */
function pageLoaded() {
  if (!isPageLoaded) {
    isPageLoaded = true;
    if (scrollIntervalId) {
      clearInterval(scrollIntervalId);
    }

    callReady();
  }
}

if (isBrowser) {
  if (document.addEventListener) {
    //Standards. Hooray! Assumption here that if standards based,
    //it knows about DOMContentLoaded.
    document.addEventListener('DOMContentLoaded', pageLoaded, false);
    window.addEventListener('load', pageLoaded, false);
  } else if (window.attachEvent) {
    window.attachEvent('onload', pageLoaded);

    testDiv = document.createElement('div');
    try {
      isTop = window.frameElement === null;
    } catch (e) {}

    //DOMContentLoaded approximation that uses a doScroll, as found by
    //Diego Perini: http://javascript.nwbox.com/IEContentLoaded/,
    //but modified by other contributors, including jdalton
    if (testDiv.doScroll && isTop && window.external) {
      scrollIntervalId = setInterval(function() {
        try {
          testDiv.doScroll();
          pageLoaded();
        } catch (e) {}
      }, 30);
    }
  }

  //Check if document already complete, and if so, just trigger page load
  //listeners. Latest webkit browsers also use 'interactive', and
  //will fire the onDOMContentLoaded before 'interactive' but not after
  //entering 'interactive' or 'complete'. More details:
  //http://dev.w3.org/html5/spec/the-end.html#the-end
  //http://stackoverflow.com/questions/3665561/document-readystate-of-interactive-vs-ondomcontentloaded
  //Hmm, this is more complicated on further use, see 'firing too early'
  //bug: https://github.com/requirejs/domReady/issues/1
  //so removing the || document.readyState === 'interactive' test.
  //There is still a window.onload binding that should get fired if
  //DOMContentLoaded is missed.
  if (document.readyState === 'complete') {
    pageLoaded();
  }
}

/** START OF PUBLIC API **/

/**
 * Registers a callback for DOM ready. If DOM is already ready, the
 * callback is called immediately.
 * @param {Function} callback
 */
function domReady(callback) {
  if (isPageLoaded) {
    callback(doc);
  } else {
    readyCalls.push(callback);
  }
  return domReady;
}

domReady.version = '2.0.1';

/**
 * Loader Plugin API method
 */
domReady.load = function(name, req, onLoad, config) {
  if (config.isBuild) {
    onLoad(null);
  } else {
    domReady(onLoad);
  }
};

/** END OF PUBLIC API **/

module.exports = domReady;
<|endoftext|>


<commit_before>"use strict";

function Game() {

    var plugins = [];
    var collisionDetector = new CollisionDetector;

    this.play = function() {
        for (var p in plugins) {
            plugins[p].initialize();
            plugins[p].play();
        }
    }

    this.pause = function() {
        for (var p in plugins) {
            plugins[p].pause();
        }
    }

    this.reset = function() {
        for (var p in plugins) {
            plugins[p].reset();
        }
    }

    this.getPluginInstances = function() {
        return plugins;
    }

    this.addPluginInstance = function(instance) {
        if (instance.type == 'object')
            collisionDetector.addObject(instance);

        plugins.push(instance);
    }

    this.draw = function(drawInfo) {
        plugins.sort(this.sortPluginsByZIndex);
        for (var p in plugins) {
            if (plugins[p].type == 'object') {
                if (plugins[p].settings.collides.value) {
                    collisionDetector.findCollisions(plugins[p]);
                }
                plugins[p].draw(drawInfo);
            }
        }
    }

    this.getPluginById = function(id) {
        for (var p in plugins) {
            if (plugins[p].settings.id.value == id) {
                return plugins[p];
            }
        }
    }

    this.sortPluginsByZIndex = function(a, b) {
        return (a.settings.zIndex.value < b.settings.zIndex.value) ? -1 : 1;
    }

}
<commit_msg>Only plugins of type object should be sorted by zIndex and get draw() called<commit_after>"use strict";

function Game() {

    var plugins = [];
    var collisionDetector = new CollisionDetector;

    this.play = function() {
        for (var p in plugins) {
            plugins[p].initialize();
            plugins[p].play();
        }
    }

    this.pause = function() {
        for (var p in plugins) {
            plugins[p].pause();
        }
    }

    this.reset = function() {
        for (var p in plugins) {
            plugins[p].reset();
        }
    }

    this.getPluginInstances = function() {
        return plugins;
    }

    this.getPluginInstancesByType = function(objectType) {
        var pluginsByType = [];
        for (var p in plugins) {
            if (plugins[p].type == objectType) {
                pluginsByType.push(plugins[p]);
            }
        }
        return pluginsByType;
    }

    this.addPluginInstance = function(instance) {
        if (instance.type == 'object')
            collisionDetector.addObject(instance);

        plugins.push(instance);
    }

    this.draw = function(drawInfo) {
        var objectsToDraw = this.getPluginInstancesByType('object');
        objectsToDraw.sort(this.sortPluginsByZIndex);
        for (var p in objectsToDraw) {
            if (objectsToDraw[p].type == 'object') {
                if (objectsToDraw[p].settings.collides.value) {
                    collisionDetector.findCollisions(objectsToDraw[p]);
                }
                objectsToDraw[p].draw(drawInfo);
            }
        }
    }

    this.getPluginById = function(id) {
        for (var p in plugins) {
            if (plugins[p].settings.id.value == id) {
                return plugins[p];
            }
        }
    }

    this.sortPluginsByZIndex = function(a, b) {
        if (a.settings.zIndex && b.settings.zIndex) {
            return (a.settings.zIndex.value < b.settings.zIndex.value) ? -1 : 1;
        }
    }

}
<|endoftext|>


<commit_before>import {InteractionType, PublicClientApplication} from "@azure/msal-browser";
import {MsalAuthenticationTemplate, MsalProvider} from "@azure/msal-react";
import React from 'react';
import ReactDOM from 'react-dom';
import ReactNotification from 'react-notifications-component';
import {BrowserRouter as Router} from "react-router-dom";

import {msalConfig} from "./authConfig";
import {App} from './components/app/App';
import RequestInterceptor from "./components/request-interceptor/RequestInterceptor";
import {AppInfoProvider} from "./contexts/app-info-context/appInfoContext";
import reportWebVitals from './reportWebVitals';

const msalInstance = new PublicClientApplication(msalConfig);

ReactDOM.render(
    <React.StrictMode>
        <MsalProvider instance={msalInstance}>
            <Router>
                <ReactNotification isMobile={true}/>
                <AppInfoProvider>
                    <MsalAuthenticationTemplate interactionType={InteractionType.Redirect}>
                        <RequestInterceptor>
                            <App/>
                        </RequestInterceptor>
                    </MsalAuthenticationTemplate>
                </AppInfoProvider>
            </Router>
        </MsalProvider>
    </React.StrictMode>,
    document.getElementById('root'),
);

reportWebVitals();
<commit_msg>https://jira.fingo.info/browse/IJ-276 If user is logged on microsoft account he is automated logged in apk (#160)<commit_after>import {InteractionType, PublicClientApplication} from "@azure/msal-browser";
import {MsalAuthenticationTemplate, MsalProvider} from "@azure/msal-react";
import React from 'react';
import ReactDOM from 'react-dom';
import ReactNotification from 'react-notifications-component';
import {BrowserRouter as Router} from "react-router-dom";

import {loginRequest, msalConfig} from "./authConfig";
import {App} from './components/app/App';
import RequestInterceptor from "./components/request-interceptor/RequestInterceptor";
import {AppInfoProvider} from "./contexts/app-info-context/appInfoContext";
import reportWebVitals from './reportWebVitals';

const msalInstance = new PublicClientApplication(msalConfig);

const signInRequest = {
    prompt: 'select_account',
    ...loginRequest
}

ReactDOM.render(
    <React.StrictMode>
        <MsalProvider instance={msalInstance}>
            <Router>
                <ReactNotification isMobile={true}/>
                <AppInfoProvider>
                    <MsalAuthenticationTemplate interactionType={InteractionType.Redirect} authenticationRequest={signInRequest}>
                        <RequestInterceptor>
                            <App/>
                        </RequestInterceptor>
                    </MsalAuthenticationTemplate>
                </AppInfoProvider>
            </Router>
        </MsalProvider>
    </React.StrictMode>,
    document.getElementById('root'),
);

reportWebVitals();
<|endoftext|>


<commit_before>/*! @license ©2015 Ruben Verborgh - Multimedia Lab / iMinds / Ghent University */
// jQuery widget for triple pattern fragments query execution

(function ($) {
  $.widget('ldf.queryui', {
    // Default widget options
    options: {
      startFragments: [],
      queries: [],
    },

    // Initializes the widget
    _create: function () {
      var options = this.options,
          $element = this.element,
          $log = this.$log = $('.log', $element);
          $stop = this.$stop = $('.stop', $element);
          $start = this.$start = $('.start', $element);
          $query = this.$query = $('.queryText', $element);
          $queries = this.$queries = $('.query', $element);
          $results = this.$results = $('.results', $element);
          $startFragments = this.$startFragments = $('.startFragment', $element);

      // Replace non-existing elements by an empty text box
      if (!$startFragments.length) $startFragments = this.$startFragments = $('<input>');

      // When a start fragment is selected, load the corresponding query set
      $startFragments.combobox({ valueKey: 'url', labelKey: 'name' });
      this._on($startFragments, { change: '_loadSelectedQuerySet' });

      // When a query is selected, load it into the editor
      $query.edited = $query.val() !== '';
      $query.change(function () { options.query = $query.val(); $query.edited = true; });
      $queries.combobox({ valueKey: 'sparql', labelKey: 'name', onlyLabelTerms: true });
      $queries.change(function (query) {
        if (query = $queries.val())
          $query.val(options.query = query).edited = false;
      });

      // Set up starting and stopping
      this._on($start, { click: '_execute' });
      this._on($stop,  { click: '_stopExecution' });

      // Add log lines to the log element
      var logger = this._logger = new ldf.Logger();
      ldf.Logger.setLevel('info');
      logger._print = function (items) { appendText($log, items.slice(2).join(' ').trim() + '\n'); };

      // Apply all options
      for (var key in options)
        this._setOption(key, options[key]);
    },

    // Sets a specific widget option
    _setOption: function (key, value) {
      var self = this, $startFragments = this.$startFragments, $queries = this.$queries;
      this.options[key] = value;
      switch (key) {
      // Set the start fragment
      case 'startFragment':
        if (value !== $startFragments.val())
          $startFragments.val(value).change();
        break;
      // Set the list of start fragments
      case 'startFragments':
        $startFragments.combobox('option', 'options', value);
        value[0] && this._setOption('startFragment', value[0].url);
        break;
      // Set the query
      case 'query':
        if (value !== $queries.val())
          $queries.val(value).change();
        break;
      // Set the list of queries
      case 'queries':
        $queries.combobox('option', 'options', value);
        // Automatically load the first query if the current query was not edited
        if (!this.$query.edited)
          value[0] && this._setOption('query', value[0].sparql);
        break;
      // Set start fragments and query sets
      case 'queryCollection':
        // If the collection is given as a string, fetch through HTTP
        if (typeof value === 'string')
          return $.getJSON(value, function (querySet) { self._setOption(key, querySet); });
        // Load the start fragments, which will trigger query loading
        this._setOption('startFragments', value.startFragments);
        if (!$startFragments.val() && value.startFragments.length)
          $startFragments.val(value.startFragments[0].url).change();
        break;
      }
    },

    // Displays the query set that corresponds to the selected start fragment
    _loadSelectedQuerySet: function () {
      var queryCollection = this.options.queryCollection,
          querySets = queryCollection && queryCollection.querySets;
      if (querySets) {
        // Find the corresponding query set
        var startFragmentUrl = this.$startFragments.val(), querySet;
        queryCollection.startFragments.some(function (startFragment) {
          if (startFragment.url === startFragmentUrl && startFragment.querySet in querySets)
            return querySet = startFragment.querySet;
        }, this);
        querySet = querySet || 'default';
        // Load the query set if not already loaded
        if (querySet !== this._querySet) {
          this._querySet = querySet;
          this._setOption('queries', querySets[this._querySet] || []);
          this.element.trigger('changeQuerySet');
        }
      }
    },

    // Starts query execution
    _execute: function () {
      // Clear results and log, and scroll page to the results
      $('html,body').animate({ scrollTop: $start.offset().top });
      this.$stop.show();
      this.$start.hide();
      this.$log.empty();
      this.$results.empty();

      // Create a client to fetch the fragments through HTTP
      var config = { prefixes: prefixes, logger: this._logger };
      config.fragmentsClient = new ldf.FragmentsClient($startFragments.val(), config);

      // Create the iterator to solve the query
      var resultsIterator;
      try { resultsIterator = new ldf.SparqlIterator(this.$query.val(), config); }
      catch (error) { return this._stopExecution(error); }
      this._resultsIterator = resultsIterator;
      resultsIterator.on('end', $.proxy(this._stopExecution, this));
      resultsIterator.on('error', $.proxy(this._stopExecution, this));

      // Read the iterator's results, and write them depending on the query type
      switch (resultsIterator.queryType) {
        // For SELECT queries, write a JSON array representation of the rows
        case 'SELECT':
          var resultCount = 0;
          resultsIterator.on('data', function (row) {
            resultCount++;
            var lines = [];
            $.each(row, function (k, v) { if (v !== undefined) lines.push(k + ': ' + v); });
            appendText($results, lines.join('\n'), '\n\n');
          });
          resultsIterator.on('end', function () {
            resultCount || appendText($results, '(This query has no results.)');
          });
        break;
        // For CONSTRUCT queries, write a Turtle representation of all results
        case 'CONSTRUCT':
          var writer = new N3.Writer({ write: function (chunk, encoding, done) {
            appendText($results, chunk), done && done();
          }}, config);
          resultsIterator.on('data', function (triple) { writer.addTriple(triple); })
                         .on('end',  function () { writer.end(); });
        break;
        // Other queries are not supported at the moment
        default:
          throw new Error('Unsupported query type: ' + resultsIterator.queryType);
      }
    },

    // Stops query execution
    _stopExecution: function (error) {
      this.$stop.hide();
      this.$start.show();
      this._resultsIterator && this._resultsIterator.removeAllListeners();
      error && error.message && $results.text(error.message);
    },
  });

  // Appends text to the given element
  function appendText($element) {
    for (var i = 1, l = arguments.length; i < l; i++)
      $element.append((arguments[i] + '').replace(/(<)|(>)|(&)|(https?:\/\/[^\s<>]+)/g, escape));
    $element.scrollTop(1E10);
  }
  // Escapes special HTML characters and convert URLs into links
  function escape(match, lt, gt, amp, url) {
    return lt && '<' || gt && '>' || amp && '&' ||
           $('<a>', { href: url, target: '_blank', text: url })[0].outerHTML;
  }
})(jQuery);
<commit_msg>Correctly store the URL of custom fragments.<commit_after>/*! @license ©2015 Ruben Verborgh - Multimedia Lab / iMinds / Ghent University */
// jQuery widget for triple pattern fragments query execution

(function ($) {
  $.widget('ldf.queryui', {
    // Default widget options
    options: {
      startFragments: [],
      queries: [],
    },

    // Initializes the widget
    _create: function () {
      var options = this.options,
          $element = this.element,
          $log = this.$log = $('.log', $element);
          $stop = this.$stop = $('.stop', $element);
          $start = this.$start = $('.start', $element);
          $query = this.$query = $('.queryText', $element);
          $queries = this.$queries = $('.query', $element);
          $results = this.$results = $('.results', $element);
          $startFragments = this.$startFragments = $('.startFragment', $element);

      // Replace non-existing elements by an empty text box
      if (!$startFragments.length) $startFragments = this.$startFragments = $('<input>');

      // When a start fragment is selected, load the corresponding query set
      $startFragments.combobox({ valueKey: 'url', labelKey: 'name' });
      this._on($startFragments, { change: function (startFragment) {
        if (startFragment = $startFragments.val())
          this._loadQuerySet(options.startFragment = startFragment);
      }});

      // When a query is selected, load it into the editor
      $query.edited = $query.val() !== '';
      $query.change(function () { options.query = $query.val(); $query.edited = true; });
      $queries.combobox({ valueKey: 'sparql', labelKey: 'name', onlyLabelTerms: true });
      $queries.change(function (query) {
        if (query = $queries.val())
          $query.val(options.query = query).edited = false;
      });

      // Set up starting and stopping
      this._on($start, { click: '_execute' });
      this._on($stop,  { click: '_stopExecution' });

      // Add log lines to the log element
      var logger = this._logger = new ldf.Logger();
      ldf.Logger.setLevel('info');
      logger._print = function (items) { appendText($log, items.slice(2).join(' ').trim() + '\n'); };

      // Apply all options
      for (var key in options)
        this._setOption(key, options[key]);
    },

    // Sets a specific widget option
    _setOption: function (key, value) {
      var self = this, $startFragments = this.$startFragments, $queries = this.$queries;
      this.options[key] = value;
      switch (key) {
      // Set the start fragment
      case 'startFragment':
        if (value !== $startFragments.val())
          $startFragments.val(value).change();
        break;
      // Set the list of start fragments
      case 'startFragments':
        $startFragments.combobox('option', 'options', value);
        value[0] && this._setOption('startFragment', value[0].url);
        break;
      // Set the query
      case 'query':
        if (value !== $queries.val())
          $queries.val(value).change();
        break;
      // Set the list of queries
      case 'queries':
        $queries.combobox('option', 'options', value);
        // Automatically load the first query if the current query was not edited
        if (!this.$query.edited)
          value[0] && this._setOption('query', value[0].sparql);
        break;
      // Set start fragments and query sets
      case 'queryCollection':
        // If the collection is given as a string, fetch through HTTP
        if (typeof value === 'string')
          return $.getJSON(value, function (querySet) { self._setOption(key, querySet); });
        // Load the start fragments, which will trigger query loading
        this._setOption('startFragments', value.startFragments);
        if (!$startFragments.val() && value.startFragments.length)
          $startFragments.val(value.startFragments[0].url).change();
        break;
      }
    },

    // Loads the query set corresponding to the given fragment
    _loadQuerySet: function (startFragmentUrl) {
      var queryCollection = this.options.queryCollection,
          querySets = queryCollection && queryCollection.querySets, querySet;
      if (querySets) {
        // Find the corresponding query set
        queryCollection.startFragments.some(function (startFragment) {
          if (startFragment.url === startFragmentUrl && startFragment.querySet in querySets)
            return querySet = startFragment.querySet;
        }, this);
        querySet = querySet || 'default';
        // Load the query set if not already loaded
        if (querySet !== this._querySet) {
          this._querySet = querySet;
          this._setOption('queries', querySets[this._querySet] || []);
          this.element.trigger('changeQuerySet');
        }
      }
    },

    // Starts query execution
    _execute: function () {
      // Clear results and log, and scroll page to the results
      $('html,body').animate({ scrollTop: $start.offset().top });
      this.$stop.show();
      this.$start.hide();
      this.$log.empty();
      this.$results.empty();

      // Create a client to fetch the fragments through HTTP
      var config = { prefixes: prefixes, logger: this._logger };
      config.fragmentsClient = new ldf.FragmentsClient($startFragments.val(), config);

      // Create the iterator to solve the query
      var resultsIterator;
      try { resultsIterator = new ldf.SparqlIterator(this.$query.val(), config); }
      catch (error) { return this._stopExecution(error); }
      this._resultsIterator = resultsIterator;
      resultsIterator.on('end', $.proxy(this._stopExecution, this));
      resultsIterator.on('error', $.proxy(this._stopExecution, this));

      // Read the iterator's results, and write them depending on the query type
      switch (resultsIterator.queryType) {
        // For SELECT queries, write a JSON array representation of the rows
        case 'SELECT':
          var resultCount = 0;
          resultsIterator.on('data', function (row) {
            resultCount++;
            var lines = [];
            $.each(row, function (k, v) { if (v !== undefined) lines.push(k + ': ' + v); });
            appendText($results, lines.join('\n'), '\n\n');
          });
          resultsIterator.on('end', function () {
            resultCount || appendText($results, '(This query has no results.)');
          });
        break;
        // For CONSTRUCT queries, write a Turtle representation of all results
        case 'CONSTRUCT':
          var writer = new N3.Writer({ write: function (chunk, encoding, done) {
            appendText($results, chunk), done && done();
          }}, config);
          resultsIterator.on('data', function (triple) { writer.addTriple(triple); })
                         .on('end',  function () { writer.end(); });
        break;
        // Other queries are not supported at the moment
        default:
          throw new Error('Unsupported query type: ' + resultsIterator.queryType);
      }
    },

    // Stops query execution
    _stopExecution: function (error) {
      this.$stop.hide();
      this.$start.show();
      this._resultsIterator && this._resultsIterator.removeAllListeners();
      error && error.message && $results.text(error.message);
    },
  });

  // Appends text to the given element
  function appendText($element) {
    for (var i = 1, l = arguments.length; i < l; i++)
      $element.append((arguments[i] + '').replace(/(<)|(>)|(&)|(https?:\/\/[^\s<>]+)/g, escape));
    $element.scrollTop(1E10);
  }
  // Escapes special HTML characters and convert URLs into links
  function escape(match, lt, gt, amp, url) {
    return lt && '<' || gt && '>' || amp && '&' ||
           $('<a>', { href: url, target: '_blank', text: url })[0].outerHTML;
  }
})(jQuery);
<|endoftext|>


<commit_before>var _ = require('underscore');
var SimpleStyleDefaults = require('./simple-style-defaults');

module.exports = _.defaults({

  generateAttributes: function (geometryType) {
    return _.extend(
      this._getStyleTypeAttrs(),
      this._getFillAttrs(geometryType),
      this._getStrokeAttrs(geometryType),
      {
        blending: 'lighter'
      },
      this._getAggrAttrs(geometryType),
      this._getAnimatedAttrs(geometryType)
    );
  },

  _getStyleTypeAttrs: function () {
    return {
      style: 'simple'
    };
  },

  _getAnimatedAttrs: function (geometryType) {
    return {
      animated: {
        attribute: null,
        // TODO: animated cumulative
        overlap: false,
        duration: 30,
        steps: 256,
        trails: 2,
        resolution: 4
      }
    };
  },

  _getAggrAttrs: function (geometryType) {
    return {
      aggregation: {}
    };
  }
}, SimpleStyleDefaults);
<commit_msg>remove comment<commit_after>var _ = require('underscore');
var SimpleStyleDefaults = require('./simple-style-defaults');

module.exports = _.defaults({

  generateAttributes: function (geometryType) {
    return _.extend(
      this._getStyleTypeAttrs(),
      this._getFillAttrs(geometryType),
      this._getStrokeAttrs(geometryType),
      {
        blending: 'lighter'
      },
      this._getAggrAttrs(geometryType),
      this._getAnimatedAttrs(geometryType)
    );
  },

  _getStyleTypeAttrs: function () {
    return {
      style: 'simple'
    };
  },

  _getAnimatedAttrs: function (geometryType) {
    return {
      animated: {
        attribute: null,
        overlap: false,
        duration: 30,
        steps: 256,
        trails: 2,
        resolution: 4
      }
    };
  },

  _getAggrAttrs: function (geometryType) {
    return {
      aggregation: {}
    };
  }
}, SimpleStyleDefaults);
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  up: function (queryInterface, Sequelize) {
    return queryInterface.createTable(
          'Quizzes',
          { id: { type: Sequelize.INTEGER, allowNull: false,
                  primaryKey: true, autoincrement: true,
                  unique: true},
            question: { type: Sequelize.STRING,
                        validate: {notEmpty: {msg: 'Falta pregunta'} } },
            answer: { type: Sequelize.STRING,
                        validate: {notEmpty: {msg: 'Falta respueta'} } },
            createdAt: { type: Sequelize.DATE, allowNull: false },
            updatedAt: { type: Sequelize.DATE, allowNull: false },

          },
          {
            sync: {forced: true}
          }
    );
  },

  down: function (queryInterface, Sequelize) {
    return queryInterface.dropTable('Quizzes');
  }
};
<commit_msg>Arreglo fallos<commit_after>'use strict';

module.exports = {
  up: function (queryInterface, Sequelize) {
      return queryInterface.createTable(
           'Quizzes',
           { id:        { type: Sequelize.INTEGER,  allowNull: false,
                          primaryKey: true,         autoIncrement: true,
                          unique: true },
             question:  { type: Sequelize.STRING,
                          validate: { notEmpty: {msg: "Falta Pregunta"} } },
             answer:    { type: Sequelize.STRING,
                          validate: { notEmpty: {msg: "Falta Respuesta"} } },
             createdAt: { type: Sequelize.DATE,     allowNull: false },
             updatedAt: { type: Sequelize.DATE,     allowNull: false }
           },
           { sync: {force: true}
           }
      );
  },
  down: function (queryInterface, Sequelize) {
        return queryInterface.dropTable('Quizzes');
  }
};<|endoftext|>


<commit_before>/**
 * @module  lang

 * - language enhancement for javascript
 * - adapter for mootools
 */

;(function(K){

// bind the this pointer of a function
function bind_method(fn, bind){
	return function(){
		return fn.apply(bind, arguments);
	}
};

/**
 * transform functions that have the signature fn(key, value)
 * to
 * functions that could accept object arguments
 * @adapter
 */
function batch_setter(fn){
	return fn.overloadSetter();
};

/**
 * memoize static result of a complicated method to save time
 */
function memoizeMethod(fn){
	var stack = {};

	return function(){
		var arg = array_slice.call(arguments).join('~^_^~');

		return (arg in stack) ? stack[arg] : (stack[arg] = fn.apply(null, arguments));
	}
};

/**
 * transform constructor functions to functions that could change a method of a instance or singleton
 */
function overload_for_instance_method(fn){
	var self = this;

	return function(methodname, instance){
		var arg = arguments;

		return K.isFunction(methodname) ?
			fn.apply(self, arg)
		:	instance[methodname] = fn.call(instance, instance[methodname], instance);
	};
};

function toQueryString(obj, splitter){
	var key, ret = [];

	for(key in obj){
		ret.push(key + '=' + obj[key]);
	}

	return ret.join(splitter || '&');
};

var	array_slice = Array.prototype.slice,

	/**
	 * a simple and faster typeOf method, and an adapter for mootools
	 * @adapter
	 */
	type = function(){

		/**
		 * @param useOrigin {Boolean} use origin method (of mootools)
		 */
		function _type(obj, useOrigin){
			return !useOrigin && type_map[ toString.call(obj) ] || adapter(obj);
		};

		var type_map = {},
			_K = K,

			// adapter for mootools 1.3
			adapter = typeOf,
			toString = Object.prototype.toString;

		'Boolean Number String Function Array Date RegExp Object'.split(' ').each(function(name){
			var nl = name.toLowerCase();

			type_map[ '[object ' + name + ']' ] = nl;

			_K['is' + name] = function(o){
				return _type(o) === nl;
			}
		});

		return _type;
	}();

K.mix(K, {

	/**
	 * @adapter
	 * ----------------------------------------------------------------------------------------------- /
	 * encapsulation for mootools.core
	 */

	/**
	 * Never use KM._type to test for a certain type in your javascript for business,
	 * since the returned string may be subject to change in a future version

	 * use KM.isXXX instead
	 */
	_type: type,

	merge: Object.merge, // Object.merge

	random: Number.random, // Number.random

	now: function(){
		return + new Date;
	},

	lambda: Function.from,

	/**
	 * language enhancement
	 * --------------------------------------------------------------------------------------------- */

	/**
	 * bind 'this' pointer for a function
	 * or bind a method for a constructor
	 * @usage:
	 *
	   1. KM.bind(myFunction, {a:1});
	   2. KM.bind('method', {a:1, method: function(){ alert(this.a) }});

	 *
	 *
	 */
	bind: function(fn, bind){
		return K.isFunction(fn) ?
			bind_method(fn, bind)
		:
			(bind[fn] = bind_method(bind[fn], bind));
	},

	/**
	 * method to encapsulate the delayed function
	 */
	delay: function(fn, delay, isInterval){
		var timer;

		return {
			start: function(){
				this.cancel();
				return timer = isInterval ? setInterval(fn, delay) : setTimeout(fn, delay);
			},
			cancel: function(){
				isInterval ? clearInterval(timer) : clearTimeout(timer);
				return this;
			}
		}
	},

	/**
	 *
	 */
	each: function(obj, fn, stop){

	},

	makeArray: function(obj){
		return K.isArray(obj) ? obj : [obj];
	},

	toQueryString: function(obj, splitter){
		return K.isObject(obj) ? toQueryString(obj, splitter) : obj;
	},

	/**
	 * OOP Enhancement
	 * --------------------------------------------------------------------------------------------- */

	/**
	 * overload a setter function or a setter method of a instance
	 */
	_overloadSetter: overload_for_instance_method( batch_setter ),

	/**
	 *
	 */
	_overloadInstanceMethod: overload_for_instance_method,

	/**
	 * lazy execute the initialization method before the real method called
	 *
	 * @example
	 * if KM.Overlay::show is the public api to show the overlay
	 * but it has a initialization method, which we want to be called just before the overlay shows, not the very moment when the instance of KM.Overlay created,
	 * so,
	 * we apply:
	 * initialization method 	-> KM.Overlay::show
	 * real show method 		-> KM.Overlay::_show
	 * and then:
	 *
	 * @usage

		 	KM._lazyInit('show', '_show', KM.Overlay.prototype);

	 */
	_lazyInit: function(init_method_name, real_method_name, belong){
		var init = belong[init_method_name],
			real = belong[real_method_name];

		belong[init_method_name] = function(){
			init.call(this);
			real.apply(this, arguments);

			belong[init_method_name] = real;
		};

		delete belong[real_method_name];
	},

	/**
	 @usage

			funcion myMethod(string){....}
			var memoizedMyMethod = KM._memoize(myMethod);

	 */
	_memoize: overload_for_instance_method( memoizeMethod )

});

})(KM);

/**
 * TODO:

 * change log:
 * 2011-06-09  Kael:
 * - 增加 KM.each
 * 2011-04-19  Kael:
 * - 增加 lazyInit 方法，修正 overloadInstanceMethod 的一个bug
 * - 增加 类型判断方法，将 KM._type 从主要api中移除
 * 2011-04-15  Kael:
 * - 增加 overloadSetter的adapter, 为普通函数与instance的方法，重构 overload 和 bind 的实现
 * 2011-04-1   Kael Zhang: 去除lang中的domready方法，增加KM.bind
 * 2010-12-31  Kael Zhang:
 * - 迁移domready方法由mt.js到lang.js，修改部分实现。
 * - 迁移data和delay方法至此
 */<commit_msg>optimize KM._memoize method, using Array.prototype.join instead of slice<commit_after>/**
 * @module  lang

 * - language enhancement for javascript
 * - adapter for mootools
 */

;(function(K){

// bind the this pointer of a function
function bind_method(fn, bind){
	return function(){
		return fn.apply(bind, arguments);
	}
};

/**
 * transform functions that have the signature fn(key, value)
 * to
 * functions that could accept object arguments
 * @adapter
 */
function batch_setter(fn){
	return fn.overloadSetter();
};

/**
 * memoize static result of a complicated method to save time
 */
function memoizeMethod(fn){
	var stack = {};

	return function(){
		var arg = array_join.call(arguments, '~^_^~');

		return (arg in stack) ? stack[arg] : (stack[arg] = fn.apply(null, arguments));
	}
};

/**
 * transform constructor functions to functions that could change a method of a instance or singleton
 */
function overload_for_instance_method(fn){
	var self = this;

	return function(methodname, instance){
		var arg = arguments;

		return K.isFunction(methodname) ?
			fn.apply(self, arg)
		:	instance[methodname] = fn.call(instance, instance[methodname], instance);
	};
};

function toQueryString(obj, splitter){
	var key, ret = [];

	for(key in obj){
		ret.push(key + '=' + obj[key]);
	}

	return ret.join(splitter || '&');
};

var	array_join = Array.prototype.join,

	/**
	 * a simple and faster typeOf method, and an adapter for mootools
	 * @adapter
	 */
	type = function(){

		/**
		 * @param useOrigin {Boolean} use origin method (of mootools)
		 */
		function _type(obj, useOrigin){
			return !useOrigin && type_map[ toString.call(obj) ] || adapter(obj);
		};

		var type_map = {},
			_K = K,

			// adapter for mootools 1.3
			adapter = typeOf,
			toString = Object.prototype.toString;

		'Boolean Number String Function Array Date RegExp Object'.split(' ').each(function(name){
			var nl = name.toLowerCase();

			type_map[ '[object ' + name + ']' ] = nl;

			_K['is' + name] = function(o){
				return _type(o) === nl;
			}
		});

		return _type;
	}();

K.mix(K, {

	/**
	 * @adapter
	 * ----------------------------------------------------------------------------------------------- /
	 * encapsulation for mootools.core
	 */

	/**
	 * Never use KM._type to test for a certain type in your javascript for business,
	 * since the returned string may be subject to change in a future version

	 * use KM.isXXX instead
	 */
	_type: type,

	merge: Object.merge, // Object.merge

	random: Number.random, // Number.random

	now: function(){
		return + new Date;
	},

	lambda: Function.from,

	/**
	 * language enhancement
	 * --------------------------------------------------------------------------------------------- */

	/**
	 * bind 'this' pointer for a function
	 * or bind a method for a constructor
	 * @usage:
	 *
	   1. KM.bind(myFunction, {a:1});
	   2. KM.bind('method', {a:1, method: function(){ alert(this.a) }});

	 *
	 *
	 */
	bind: function(fn, bind){
		return K.isFunction(fn) ?
			bind_method(fn, bind)
		:
			(bind[fn] = bind_method(bind[fn], bind));
	},

	/**
	 * method to encapsulate the delayed function
	 */
	delay: function(fn, delay, isInterval){
		var timer;

		return {
			start: function(){
				this.cancel();
				return timer = isInterval ? setInterval(fn, delay) : setTimeout(fn, delay);
			},
			cancel: function(){
				isInterval ? clearInterval(timer) : clearTimeout(timer);
				return this;
			}
		}
	},

	/**
	 *
	 */
	each: function(obj, fn, stop){

	},

	makeArray: function(obj){
		return K.isArray(obj) ? obj : [obj];
	},

	toQueryString: function(obj, splitter){
		return K.isObject(obj) ? toQueryString(obj, splitter) : obj;
	},

	/**
	 * OOP Enhancement
	 * --------------------------------------------------------------------------------------------- */

	/**
	 * overload a setter function or a setter method of a instance
	 */
	_overloadSetter: overload_for_instance_method( batch_setter ),

	/**
	 *
	 */
	_overloadInstanceMethod: overload_for_instance_method,

	/**
	 * lazy execute the initialization method before the real method called
	 *
	 * @example
	 * if KM.Overlay::show is the public api to show the overlay
	 * but it has a initialization method, which we want to be called just before the overlay shows, not the very moment when the instance of KM.Overlay created,
	 * so,
	 * we apply:
	 * initialization method 	-> KM.Overlay::show
	 * real show method 		-> KM.Overlay::_show
	 * and then:
	 *
	 * @usage

		 	KM._lazyInit('show', '_show', KM.Overlay.prototype);

	 */
	_lazyInit: function(init_method_name, real_method_name, belong){
		var init = belong[init_method_name],
			real = belong[real_method_name];

		belong[init_method_name] = function(){
			init.call(this);
			real.apply(this, arguments);

			belong[init_method_name] = real;
		};

		delete belong[real_method_name];
	},

	/**
	 @usage

			funcion myMethod(string){....}
			var memoizedMyMethod = KM._memoize(myMethod);

	 */
	_memoize: overload_for_instance_method( memoizeMethod )

});

})(KM);

/**
 * TODO:

 * change log:
 * 2011-06-09  Kael:
 * - 增加 KM.each
 * 2011-04-19  Kael:
 * - 增加 lazyInit 方法，修正 overloadInstanceMethod 的一个bug
 * - 增加 类型判断方法，将 KM._type 从主要api中移除
 * 2011-04-15  Kael:
 * - 增加 overloadSetter的adapter, 为普通函数与instance的方法，重构 overload 和 bind 的实现
 * 2011-04-1   Kael Zhang: 去除lang中的domready方法，增加KM.bind
 * 2010-12-31  Kael Zhang:
 * - 迁移domready方法由mt.js到lang.js，修改部分实现。
 * - 迁移data和delay方法至此
 */<|endoftext|>