<commit_before>/*
 * This binds the node-validator library to the req object so that
 * the validation / sanitization methods can be called on parameter
 * names rather than the actual strings.
 *
 *
 * 1. To validate parameters, use `req.check(param_name, [err_message])`
 *        e.g. req.check('param1').len(1, 6).isInt();
 *        e.g. req.checkHeader('referer').contains('mydomain.com');
 *
 *    Each call to `check()` will throw an exception by default. To
 *    specify a custom err handler, use `req.onValidationError(errback)`
 *    where errback receives a parameter containing the error message
 *
 * 2. To sanitize parameters, use `req.sanitize(param_name)`
 *        e.g. req.sanitize('param1').toBoolean();
 *        e.g. req.sanitize('param2').toInt();
 *
 * 3. Done! Access your validated and sanitized paramaters through the
 *    `req.params` object
 */

var validator = require('validator');

var expressValidator = function(options) {
  options = options || {};

  var _options = {};

  _options.errorFormatter = options.errorFormatter || function(param, msg, value) {
    return {
      param : param,
      msg   : msg,
      value : value
    };
  };

  var sanitizers = ['trim', 'ltrim', 'rtrim', 'escape', 'whitelist',
  'blacklist'];

  var sanitize = function(request, param, value) {
    var methods = {};

    Object.keys(validator).forEach(function(methodName) {
      if (methodName.match(/^to/) || methodName in sanitizers) {
        methods[methodName] = function() {
          var arguments = Array.prototype.slice.call(arguments);

          var args = [value].concat(arguments);
          var result = validator[methodName].apply(validator, args);
          request.updateParam(param, result);
        }
      }
    });

    return methods;
  }

  function checkParam(req, getter) {
    return function(param, failMsg) {

      var value;

      // If param is not an array, then split by dot notation
      // returning an array. It will return an array even if
      // param doesn't have the dot notation.
      //      'blogpost' = ['blogpost']
      //      'login.username' = ['login', 'username']
      // For regex matches you can access the parameters using numbers.
      if (!Array.isArray(param)) {
        param = typeof param === 'number' ?
                [param] :
                param.split('.').filter(function(e){
                  return e !== '';
                });
      }

      // Extract value from params
      param.map(function(item) {
          if (value === undefined) {
            value = getter(item)
          } else {
            value = value[item];
          }
      });
      param = param.join('.');

      var errorHandler = function(msg) {
        var error = _options.errorFormatter(param, msg, value);

        if (req._validationErrors === undefined) {
          req._validationErrors = [];
        }
        req._validationErrors.push(error);

        if (req.onErrorCallback) {
          req.onErrorCallback(msg);
        }
        return this;
      }

      var methods = [];

      Object.keys(validator).forEach(function(methodName) {
        if (!methodName.match(/^to/) && !(methodName in sanitizers)) {
          methods[methodName] = function() {
            var arguments = Array.prototype.slice.call(arguments);

            var args = [value].concat(arguments);
            var isCorrect = validator[methodName].apply(validator, args);

            if (!isCorrect) {
              errorHandler(failMsg || 'Ivalid value');
            }

            return methods;
          }
        }
      });

      methods['notEmpty'] = function() {
        return methods.isLength(1);
      }

      methods['len'] = function() {
        return methods.isLength.apply(methods.isLength, Array.prototype.slice.call(arguments));
      }

      return methods;
    }
  }
  return function(req, res, next) {

    req.updateParam = function(name, value) {
      // route params like /user/:id
      if (this.params && this.params.hasOwnProperty(name) &&
          undefined !== this.params[name]) {
        return this.params[name] = value;
      }
      // query string params
      if (undefined !== this.query[name]) {
        return this.query[name] = value;
      }
      // request body params via connect.bodyParser
      if (this.body && undefined !== this.body[name]) {
        return this.body[name] = value;
      }
      return false;
    };

    req.check = checkParam(req, function(item) {
      return req.param(item);
    });

    req.checkBody = checkParam(req, function(item) {
      return req.body && req.body[item];
    });

    req.checkParams = checkParam(req, function(item) {
      return req.params && req.params[item];
    });

    req.checkQuery = checkParam(req, function(item) {
      return req.query && req.query[item];
    });

    req.checkHeader = checkParam(req, function(header) {
        var toCheck;

        if (header === 'referrer' || header === 'referer') {
          toCheck = this.headers.referer;
        } else {
          toCheck = this.headers[header];
        }
        return toCheck || '';
    });

    req.onValidationError = function(errback) {
      req.onErrorCallback = errback;
    };

    req.validationErrors = function(mapped) {
      if (req._validationErrors === undefined) {
        return null;
      }
      if (mapped) {
        var errors = {};
        req._validationErrors.forEach(function(err) {
          errors[err.param] = err;
        });
        return errors;
      }
      return req._validationErrors;
    }

    req.filter = function(param) {
      return sanitize(this, param, this.param(param));
    };

    // Create some aliases - might help with code readability
    req.sanitize = req.filter;
    req.assert = req.check;
    req.validate = req.check;

    return next();
  };
}
module.exports = expressValidator;
module.exports.validator = validator;
<commit_msg> - fixed typo<commit_after>/*
 * This binds the node-validator library to the req object so that
 * the validation / sanitization methods can be called on parameter
 * names rather than the actual strings.
 *
 *
 * 1. To validate parameters, use `req.check(param_name, [err_message])`
 *        e.g. req.check('param1').len(1, 6).isInt();
 *        e.g. req.checkHeader('referer').contains('mydomain.com');
 *
 *    Each call to `check()` will throw an exception by default. To
 *    specify a custom err handler, use `req.onValidationError(errback)`
 *    where errback receives a parameter containing the error message
 *
 * 2. To sanitize parameters, use `req.sanitize(param_name)`
 *        e.g. req.sanitize('param1').toBoolean();
 *        e.g. req.sanitize('param2').toInt();
 *
 * 3. Done! Access your validated and sanitized paramaters through the
 *    `req.params` object
 */

var validator = require('validator');

var expressValidator = function(options) {
  options = options || {};

  var _options = {};

  _options.errorFormatter = options.errorFormatter || function(param, msg, value) {
    return {
      param : param,
      msg   : msg,
      value : value
    };
  };

  var sanitizers = ['trim', 'ltrim', 'rtrim', 'escape', 'whitelist',
  'blacklist'];

  var sanitize = function(request, param, value) {
    var methods = {};

    Object.keys(validator).forEach(function(methodName) {
      if (methodName.match(/^to/) || methodName in sanitizers) {
        methods[methodName] = function() {
          var arguments = Array.prototype.slice.call(arguments);

          var args = [value].concat(arguments);
          var result = validator[methodName].apply(validator, args);
          request.updateParam(param, result);
        }
      }
    });

    return methods;
  }

  function checkParam(req, getter) {
    return function(param, failMsg) {

      var value;

      // If param is not an array, then split by dot notation
      // returning an array. It will return an array even if
      // param doesn't have the dot notation.
      //      'blogpost' = ['blogpost']
      //      'login.username' = ['login', 'username']
      // For regex matches you can access the parameters using numbers.
      if (!Array.isArray(param)) {
        param = typeof param === 'number' ?
                [param] :
                param.split('.').filter(function(e){
                  return e !== '';
                });
      }

      // Extract value from params
      param.map(function(item) {
          if (value === undefined) {
            value = getter(item)
          } else {
            value = value[item];
          }
      });
      param = param.join('.');

      var errorHandler = function(msg) {
        var error = _options.errorFormatter(param, msg, value);

        if (req._validationErrors === undefined) {
          req._validationErrors = [];
        }
        req._validationErrors.push(error);

        if (req.onErrorCallback) {
          req.onErrorCallback(msg);
        }
        return this;
      }

      var methods = [];

      Object.keys(validator).forEach(function(methodName) {
        if (!methodName.match(/^to/) && !(methodName in sanitizers)) {
          methods[methodName] = function() {
            var arguments = Array.prototype.slice.call(arguments);

            var args = [value].concat(arguments);
            var isCorrect = validator[methodName].apply(validator, args);

            if (!isCorrect) {
              errorHandler(failMsg || 'Invalid value');
            }

            return methods;
          }
        }
      });

      methods['notEmpty'] = function() {
        return methods.isLength(1);
      }

      methods['len'] = function() {
        return methods.isLength.apply(methods.isLength, Array.prototype.slice.call(arguments));
      }

      return methods;
    }
  }
  return function(req, res, next) {

    req.updateParam = function(name, value) {
      // route params like /user/:id
      if (this.params && this.params.hasOwnProperty(name) &&
          undefined !== this.params[name]) {
        return this.params[name] = value;
      }
      // query string params
      if (undefined !== this.query[name]) {
        return this.query[name] = value;
      }
      // request body params via connect.bodyParser
      if (this.body && undefined !== this.body[name]) {
        return this.body[name] = value;
      }
      return false;
    };

    req.check = checkParam(req, function(item) {
      return req.param(item);
    });

    req.checkBody = checkParam(req, function(item) {
      return req.body && req.body[item];
    });

    req.checkParams = checkParam(req, function(item) {
      return req.params && req.params[item];
    });

    req.checkQuery = checkParam(req, function(item) {
      return req.query && req.query[item];
    });

    req.checkHeader = checkParam(req, function(header) {
        var toCheck;

        if (header === 'referrer' || header === 'referer') {
          toCheck = this.headers.referer;
        } else {
          toCheck = this.headers[header];
        }
        return toCheck || '';
    });

    req.onValidationError = function(errback) {
      req.onErrorCallback = errback;
    };

    req.validationErrors = function(mapped) {
      if (req._validationErrors === undefined) {
        return null;
      }
      if (mapped) {
        var errors = {};
        req._validationErrors.forEach(function(err) {
          errors[err.param] = err;
        });
        return errors;
      }
      return req._validationErrors;
    }

    req.filter = function(param) {
      return sanitize(this, param, this.param(param));
    };

    // Create some aliases - might help with code readability
    req.sanitize = req.filter;
    req.assert = req.check;
    req.validate = req.check;

    return next();
  };
}
module.exports = expressValidator;
module.exports.validator = validator;
<|endoftext|>


<commit_before>function storeInChrome(newWord) {
    //convert newWord into a key-value pair
    var w = {};
    w[newWord.word] = newWord.properties;
    chrome.storage.local.set(w, function() {
        console.debug("Saved word ", newWord.word);
    });
}
<commit_msg>Added function to fetch all words from storage<commit_after>function storeInChrome(newWord) {
    //convert newWord into a key-value pair
    var w = {};
    w[newWord.word] = newWord.properties;
    chrome.storage.local.set(w, function() {
        console.debug("Saved word ", newWord.word);
    });
}

function fetchAllWordsFromChrome() {
    chrome.storage.local.get(null, function(wordObjects) {
        var words = Object.keys(wordObjects);
        for (var i = 0; i < words.length; i++) {
            var wordToAdd = {};
            wordToAdd.word = words[i];
            wordToAdd.properties = wordObjects[words[i]];
            if (wordToAdd.properties.count < 0) {
                newWords.push(wordToAdd);
            } else if (wordToAdd.properties.count < 4) {
                learningWords.push(wordToAdd);
            } else {
                masteredWords.push(wordToAdd);
            }
        }
    });
}
<|endoftext|>


<commit_before>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('mirosubs.Dialog');

/**
 * @constructor
 *
 */
mirosubs.Dialog = function(videoSource) {
    goog.ui.Dialog.call(this, 'mirosubs-modal-widget', true);
    this.setBackgroundElementOpacity(0.8);
    this.setButtonSet(null);
    this.setDisposeOnHide(true);
    this.setEscapeToCancel(false);
    /**
     * This only becomes non-null on finish, when the server sends back
     * new contents for the drop-down menu.
     * @type {mirosubs.widget.DropDownContents}
     */
    this.dropDownContents_ = null;
    this.controlledVideoPlayer_ = videoSource.createControlledPlayer();
    this.videoPlayer_ = this.controlledVideoPlayer_.getPlayer();
    this.timelinePanel_ = null;
    this.captioningArea_ = null;
    this.rightPanelContainer_ = null;
    this.rightPanel_ = null;
    this.bottomPanelContainer_ = null;
};
goog.inherits(mirosubs.Dialog, goog.ui.Dialog);
mirosubs.Dialog.prototype.createDom = function() {
    mirosubs.Dialog.superClass_.createDom.call(this);
    var leftColumn = new goog.ui.Component();
    leftColumn.addChild(this.controlledVideoPlayer_, true);
    leftColumn.getElement().className = 'mirosubs-left';
    leftColumn.addChild(this.timelinePanel_ = new goog.ui.Component(), true);
    leftColumn.addChild(this.captioningArea_ = new goog.ui.Component(), true);
    this.captioningArea_.getElement().className = 'mirosubs-captioningArea';
    this.addChild(leftColumn, true);
    this.addChild(
        this.rightPanelContainer_ = new goog.ui.Component(), true);
    this.rightPanelContainer_.getElement().className = 'mirosubs-right';
    this.getContentElement().appendChild(this.getDomHelper().createDom(
        'div', 'mirosubs-clear'));
    this.addChild(
        this.bottomPanelContainer_ = new goog.ui.Component(), true);
};
mirosubs.Dialog.prototype.enterDocument = function() {
    mirosubs.Dialog.superClass_.enterDocument.call(this);
    this.getHandler().
        listen(mirosubs.ClosingWindow.getInstance(),
               mirosubs.ClosingWindow.BEFORE_UNLOAD,
               this.onWindowUnload_).
        listen(mirosubs.userEventTarget,
               mirosubs.EventType.LOGIN,
               this.updateLoginState);
};
/**
 * Used to display a temporary overlay, for example the instructional
 * video panel in between subtitling steps.
 * @protected
 * @param {goog.ui.Component} panel Something with absolute positioning
 *
 */
mirosubs.Dialog.prototype.showTemporaryPanel = function(panel) {
    this.hideTemporaryPanel();
    this.temporaryPanel_ = panel;
    this.addChild(panel, true);
};
/**
 * Hides and disposes the panel displayed in showTemporaryPanel.
 * @protected
 */
mirosubs.Dialog.prototype.hideTemporaryPanel = function() {
    if (this.temporaryPanel_) {
        this.temporaryPanel_.stopVideo();
        this.removeChild(this.temporaryPanel_, true);
        this.temporaryPanel_.dispose();
        this.temporaryPanel_ = null;
    }
};
mirosubs.Dialog.prototype.getVideoPlayerInternal = function() {
    return this.videoPlayer_;
};
mirosubs.Dialog.prototype.getTimelinePanelInternal = function() {
    return this.timelinePanel_;
};
mirosubs.Dialog.prototype.getCaptioningAreaInternal = function() {
    return this.captioningArea_;
};
mirosubs.Dialog.prototype.setRightPanelInternal = function(rightPanel) {
    this.rightPanel_ = rightPanel;
    this.rightPanelContainer_.removeChildren(true);
    this.rightPanelContainer_.addChild(rightPanel, true);
};
mirosubs.Dialog.prototype.getRightPanelInternal = function() {
    return this.rightPanel_;
};
mirosubs.Dialog.prototype.getBottomPanelContainerInternal = function() {
    return this.bottomPanelContainer_;
};
mirosubs.Dialog.prototype.updateLoginState = function() {
    this.rightPanel_.updateLoginState();
};
/**
 * Returns true if there's no work, or if there has been work
 * but it was saved.
 * @protected
 */
mirosubs.Dialog.prototype.isWorkSaved = goog.abstractMethod;
/**
 * @protected
 * @param {boolean} closeAfterSave
 */
mirosubs.Dialog.prototype.saveWork = function(closeAfterSave) {
    if (mirosubs.currentUsername == null && !mirosubs.isLoginAttemptInProgress())
        mirosubs.login()
    else
        this.saveWorkInternal(closeAfterSave);
};
mirosubs.Dialog.prototype.saveWorkInternal = function(closeAfterSave) {
    goog.abstractMethod();
};
mirosubs.Dialog.prototype.onWindowUnload_ = function(event) {
    if (!this.isWorkSaved())
        event.message = "You have unsaved work.";
};
mirosubs.Dialog.prototype.setVisible = function(visible) {
    if (visible)
        mirosubs.Dialog.superClass_.setVisible.call(this, true);
    else {
        if (this.isWorkSaved())
            this.hideDialogImpl_();
        else {
            this.showSaveWorkDialog_();
        }
    }
};
/**
 * @protected
 * @param {mirosubs.widget.DropDownContents} dropDownContents
 */
mirosubs.Dialog.prototype.setDropDownContentsInternal = function(dropDownContents) {
    this.dropDownContents_ = dropDownContents;
};
mirosubs.Dialog.prototype.getDropDownContents = function() {
    return this.dropDownContents_;
};
mirosubs.Dialog.prototype.showSaveWorkDialog_ = function() {
    var that = this;
    var unsavedWarning = new mirosubs.UnsavedWarning(function(submit) {
        if (submit)
            that.saveWork(true);
        else
            that.hideDialogImpl_(false);
    });
    unsavedWarning.setVisible(true);
};
mirosubs.Dialog.prototype.hideDialogImpl_ = function() {
    mirosubs.Dialog.superClass_.setVisible.call(this, false);
    if (mirosubs.returnURL != null) {
        goog.Timer.callOnce(function() {
            window.location.replace(mirosubs.returnURL);
        });
    }
};
mirosubs.Dialog.prototype.disposeInternal = function() {
    mirosubs.Dialog.superClass_.disposeInternal.call(this);
    this.videoPlayer_.dispose();
};
<commit_msg>Scrolls to top of document when opening sub dialog. For 14908.<commit_after>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('mirosubs.Dialog');

/**
 * @constructor
 *
 */
mirosubs.Dialog = function(videoSource) {
    goog.ui.Dialog.call(this, 'mirosubs-modal-widget', true);
    this.setBackgroundElementOpacity(0.8);
    this.setButtonSet(null);
    this.setDisposeOnHide(true);
    this.setEscapeToCancel(false);
    /**
     * This only becomes non-null on finish, when the server sends back
     * new contents for the drop-down menu.
     * @type {mirosubs.widget.DropDownContents}
     */
    this.dropDownContents_ = null;
    this.controlledVideoPlayer_ = videoSource.createControlledPlayer();
    this.videoPlayer_ = this.controlledVideoPlayer_.getPlayer();
    this.timelinePanel_ = null;
    this.captioningArea_ = null;
    this.rightPanelContainer_ = null;
    this.rightPanel_ = null;
    this.bottomPanelContainer_ = null;
};
goog.inherits(mirosubs.Dialog, goog.ui.Dialog);
mirosubs.Dialog.prototype.createDom = function() {
    mirosubs.Dialog.superClass_.createDom.call(this);
    var leftColumn = new goog.ui.Component();
    leftColumn.addChild(this.controlledVideoPlayer_, true);
    leftColumn.getElement().className = 'mirosubs-left';
    leftColumn.addChild(this.timelinePanel_ = new goog.ui.Component(), true);
    leftColumn.addChild(this.captioningArea_ = new goog.ui.Component(), true);
    this.captioningArea_.getElement().className = 'mirosubs-captioningArea';
    this.addChild(leftColumn, true);
    this.addChild(
        this.rightPanelContainer_ = new goog.ui.Component(), true);
    this.rightPanelContainer_.getElement().className = 'mirosubs-right';
    this.getContentElement().appendChild(this.getDomHelper().createDom(
        'div', 'mirosubs-clear'));
    this.addChild(
        this.bottomPanelContainer_ = new goog.ui.Component(), true);
};
mirosubs.Dialog.prototype.enterDocument = function() {
    mirosubs.Dialog.superClass_.enterDocument.call(this);
    this.getHandler().
        listen(mirosubs.ClosingWindow.getInstance(),
               mirosubs.ClosingWindow.BEFORE_UNLOAD,
               this.onWindowUnload_).
        listen(mirosubs.userEventTarget,
               mirosubs.EventType.LOGIN,
               this.updateLoginState);
};
/**
 * Used to display a temporary overlay, for example the instructional
 * video panel in between subtitling steps.
 * @protected
 * @param {goog.ui.Component} panel Something with absolute positioning
 *
 */
mirosubs.Dialog.prototype.showTemporaryPanel = function(panel) {
    this.hideTemporaryPanel();
    this.temporaryPanel_ = panel;
    this.addChild(panel, true);
};
/**
 * Hides and disposes the panel displayed in showTemporaryPanel.
 * @protected
 */
mirosubs.Dialog.prototype.hideTemporaryPanel = function() {
    if (this.temporaryPanel_) {
        this.temporaryPanel_.stopVideo();
        this.removeChild(this.temporaryPanel_, true);
        this.temporaryPanel_.dispose();
        this.temporaryPanel_ = null;
    }
};
mirosubs.Dialog.prototype.getVideoPlayerInternal = function() {
    return this.videoPlayer_;
};
mirosubs.Dialog.prototype.getTimelinePanelInternal = function() {
    return this.timelinePanel_;
};
mirosubs.Dialog.prototype.getCaptioningAreaInternal = function() {
    return this.captioningArea_;
};
mirosubs.Dialog.prototype.setRightPanelInternal = function(rightPanel) {
    this.rightPanel_ = rightPanel;
    this.rightPanelContainer_.removeChildren(true);
    this.rightPanelContainer_.addChild(rightPanel, true);
};
mirosubs.Dialog.prototype.getRightPanelInternal = function() {
    return this.rightPanel_;
};
mirosubs.Dialog.prototype.getBottomPanelContainerInternal = function() {
    return this.bottomPanelContainer_;
};
mirosubs.Dialog.prototype.updateLoginState = function() {
    this.rightPanel_.updateLoginState();
};
/**
 * Returns true if there's no work, or if there has been work
 * but it was saved.
 * @protected
 */
mirosubs.Dialog.prototype.isWorkSaved = goog.abstractMethod;
/**
 * @protected
 * @param {boolean} closeAfterSave
 */
mirosubs.Dialog.prototype.saveWork = function(closeAfterSave) {
    if (mirosubs.currentUsername == null && !mirosubs.isLoginAttemptInProgress())
        mirosubs.login()
    else
        this.saveWorkInternal(closeAfterSave);
};
mirosubs.Dialog.prototype.saveWorkInternal = function(closeAfterSave) {
    goog.abstractMethod();
};
mirosubs.Dialog.prototype.onWindowUnload_ = function(event) {
    if (!this.isWorkSaved())
        event.message = "You have unsaved work.";
};
mirosubs.Dialog.prototype.setVisible = function(visible) {
    if (visible) {
        mirosubs.Dialog.superClass_.setVisible.call(this, true);
        goog.dom.getDocumentScrollElement().scrollTop = 0;
    }
    else {
        if (this.isWorkSaved())
            this.hideDialogImpl_();
        else {
            this.showSaveWorkDialog_();
        }
    }
};
/**
 * @protected
 * @param {mirosubs.widget.DropDownContents} dropDownContents
 */
mirosubs.Dialog.prototype.setDropDownContentsInternal = function(dropDownContents) {
    this.dropDownContents_ = dropDownContents;
};
mirosubs.Dialog.prototype.getDropDownContents = function() {
    return this.dropDownContents_;
};
mirosubs.Dialog.prototype.showSaveWorkDialog_ = function() {
    var that = this;
    var unsavedWarning = new mirosubs.UnsavedWarning(function(submit) {
        if (submit)
            that.saveWork(true);
        else
            that.hideDialogImpl_(false);
    });
    unsavedWarning.setVisible(true);
};
mirosubs.Dialog.prototype.hideDialogImpl_ = function() {
    mirosubs.Dialog.superClass_.setVisible.call(this, false);
    if (mirosubs.returnURL != null) {
        goog.Timer.callOnce(function() {
            window.location.replace(mirosubs.returnURL);
        });
    }
};
mirosubs.Dialog.prototype.disposeInternal = function() {
    mirosubs.Dialog.superClass_.disposeInternal.call(this);
    this.videoPlayer_.dispose();
};
<|endoftext|>


<commit_before>'use strict';

var ProtectedValue = require('../crypto/protected-value'),
    XmlNames = require('./../defs/xml-names'),
    XmlUtils = require('./../utils/xml-utils'),
    Consts = require('../defs/consts'),
    KdbxUuid = require('./kdbx-uuid'),
    KdbxTimes = require('./kdbx-times');

var tagsSplitRegex = /\s*[;,:]\s*/;

/**
 * Entry
 * @constructor
 */
var KdbxEntry = function() {
    this.uuid = undefined;
    this.icon = undefined;
    this.customIcon = undefined;
    this.fgColor = undefined;
    this.bgColor = undefined;
    this.overrideUrl = undefined;
    this.tags = [];
    this.times = new KdbxTimes();
    this.fields = {};
    this.binaries = {};
    this.autoType = {
        enabled: undefined, obfuscation: undefined, defaultSequence: undefined, items: []
    };
    this.history = [];
    this.parentGroup = undefined;
    Object.preventExtensions(this);
};

KdbxEntry.prototype._readNode = function(node) {
    switch (node.tagName) {
        case XmlNames.Elem.Uuid:
            this.uuid = XmlUtils.getUuid(node);
            break;
        case XmlNames.Elem.Icon:
            this.icon = XmlUtils.getNumber(node);
            break;
        case XmlNames.Elem.CustomIconID:
            this.customIcon = XmlUtils.getUuid(node);
            break;
        case XmlNames.Elem.FgColor:
            this.fgColor = XmlUtils.getText(node);
            break;
        case XmlNames.Elem.BgColor:
            this.bgColor = XmlUtils.getText(node);
            break;
        case XmlNames.Elem.OverrideUrl:
            this.overrideUrl = XmlUtils.getText(node);
            break;
        case XmlNames.Elem.Tags:
            this.tags = this._stringToTags(XmlUtils.getText(node));
            break;
        case XmlNames.Elem.Times:
            this.times = KdbxTimes.read(node);
            break;
        case XmlNames.Elem.String:
            this._readField(node);
            break;
        case XmlNames.Elem.Binary:
            this._readBinary(node);
            break;
        case XmlNames.Elem.AutoType:
            this._readAutoType(node);
            break;
        case XmlNames.Elem.History:
            this._readHistory(node);
            break;
    }
};

KdbxEntry.prototype._readField = function(node) {
    var keyNode = XmlUtils.getChildNode(node, XmlNames.Elem.Key),
        valueNode = XmlUtils.getChildNode(node, XmlNames.Elem.Value),
        key = XmlUtils.getText(keyNode),
        value = XmlUtils.getProtectedText(valueNode);
    if (key && value) {
        this.fields[key] = value;
    }
};

KdbxEntry.prototype._writeFields = function(parentNode) {
    var fields = this.fields;
    Object.keys(fields).forEach(function(field) {
        var value = fields[field];
        if (value !== undefined && value !== null) {
            var node = XmlUtils.addChildNode(parentNode, XmlNames.Elem.String);
            XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.Key), field);
            XmlUtils.setProtectedText(XmlUtils.addChildNode(node, XmlNames.Elem.Value), value);
        }
    });
};

KdbxEntry.prototype._readBinary = function(node) {
    var keyNode = XmlUtils.getChildNode(node, XmlNames.Elem.Key),
        valueNode = XmlUtils.getChildNode(node, XmlNames.Elem.Value),
        key = XmlUtils.getText(keyNode),
        value = XmlUtils.getProtectedBinary(valueNode);
    if (key && value) {
        this.binaries[key] = value;
    }
};

KdbxEntry.prototype._writeBinaries = function(parentNode) {
    var binaries = this.binaries;
    Object.keys(binaries).forEach(function(id) {
        var data = binaries[id];
        if (data) {
            var node = XmlUtils.addChildNode(parentNode, XmlNames.Elem.Binary);
            XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.Key), id);
            XmlUtils.setProtectedBinary(XmlUtils.addChildNode(node, XmlNames.Elem.Value), data);
        }
    });
};

KdbxEntry.prototype._stringToTags = function(str) {
    if (!str) {
        return [];
    }
    return str.split(tagsSplitRegex).filter(function(s) { return s; });
};

KdbxEntry.prototype._readAutoType = function(node) {
    for (var i = 0, cn = node.childNodes, len = cn.length; i < len; i++) {
        var childNode = cn[i];
        switch (childNode.tagName) {
            case XmlNames.Elem.AutoTypeEnabled:
                this.autoType.enabled = XmlUtils.getBoolean(childNode);
                break;
            case XmlNames.Elem.AutoTypeObfuscation:
                this.autoType.obfuscation = XmlUtils.getNumber(childNode);
                break;
            case XmlNames.Elem.AutoTypeDefaultSeq:
                this.autoType.defaultSequence = XmlUtils.getText(childNode);
                break;
            case XmlNames.Elem.AutoTypeItem:
                this._readAutoTypeItem(childNode);
                break;
        }
    }
};

KdbxEntry.prototype._readAutoTypeItem = function(node) {
    var item = {};
    for (var i = 0, cn = node.childNodes, len = cn.length; i < len; i++) {
        var childNode = cn[i];
        switch (childNode.tagName) {
            case XmlNames.Elem.Window:
                item.window = XmlUtils.getText(childNode);
                break;
            case XmlNames.Elem.KeystrokeSequence:
                item.keystrokeSequence = XmlUtils.getText(childNode);
                break;
        }
    }
    this.autoType.items.push(item);
};

KdbxEntry.prototype._writeAutoType = function(parentNode) {
    var node = XmlUtils.addChildNode(parentNode, XmlNames.Elem.AutoType);
    XmlUtils.setBoolean(XmlUtils.addChildNode(node, XmlNames.Elem.AutoTypeEnabled), this.autoType.enabled);
    XmlUtils.setNumber(XmlUtils.addChildNode(node, XmlNames.Elem.AutoTypeObfuscation), this.autoType.obfuscation);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.AutoTypeDefaultSeq), this.autoType.defaultSequence);
    for (var i = 0; i < this.autoType.items.length; i++) {
        var item = this.autoType.items[i];
        var itemNode = XmlUtils.addChildNode(node, XmlNames.Elem.AutoTypeItem);
        XmlUtils.setText(XmlUtils.addChildNode(itemNode, XmlNames.Elem.Window), item.window);
        XmlUtils.setText(XmlUtils.addChildNode(itemNode, XmlNames.Elem.KeystrokeSequence), item.keystrokeSequence);
    }
};

KdbxEntry.prototype._readHistory = function(node) {
    for (var i = 0, cn = node.childNodes, len = cn.length; i < len; i++) {
        var childNode = cn[i];
        switch (childNode.tagName) {
            case XmlNames.Elem.Entry:
                this.history.push(KdbxEntry.read(childNode));
                break;
        }
    }
};

KdbxEntry.prototype._writeHistory = function(parentNode) {
    var historyNode = XmlUtils.addChildNode(parentNode, XmlNames.Elem.History);
    for (var i = 0; i < this.history.length; i++) {
        this.history[i].write(historyNode);
    }
};

KdbxEntry.prototype._setField = function(name, str, secure) {
    this.fields[name] = secure ? ProtectedValue.fromString(str) : str;
};

/**
 * Write to stream
 * @param {Node} parentNode - xml document node
 */
KdbxEntry.prototype.write = function(parentNode) {
    var node = XmlUtils.addChildNode(parentNode, XmlNames.Elem.Entry);
    XmlUtils.setUuid(XmlUtils.addChildNode(node, XmlNames.Elem.Uuid), this.uuid);
    XmlUtils.setNumber(XmlUtils.addChildNode(node, XmlNames.Elem.Icon), this.icon);
    XmlUtils.setUuid(XmlUtils.addChildNode(node, XmlNames.Elem.CustomIconID), this.customIcon);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.FgColor), this.fgColor);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.BgColor), this.bgColor);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.OverrideUrl), this.overrideUrl);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.Tags), this.tags.join(','));
    this.times.write(node);
    this._writeFields(node);
    this._writeBinaries(node);
    this._writeAutoType(node);
    this._writeHistory(node);
};

/**
 * Push current entry state to the top of history
 */
KdbxEntry.prototype.pushHistory = function() {
    var historyEntry = new KdbxEntry();
    historyEntry.copyFrom(this);
    this.history.push(historyEntry);
};

/**
 * Clone entry state from another entry, or history entry
 */
KdbxEntry.prototype.copyFrom = function(entry) {
    this.uuid = entry.uuid;
    this.icon = entry.icon;
    this.customIcon = entry.customIcon;
    this.fgColor = entry.fgColor;
    this.bgColor = entry.bgColor;
    this.overrideUrl = entry.overrideUrl;
    this.tags = entry.tags.slice();
    this.times = entry.times.clone();
    Object.keys(entry.fields).forEach(function(name) {
        if (entry.fields[name] instanceof ProtectedValue) {
            this.fields[name] = entry.fields[name].clone();
        } else {
            this.fields[name] = entry.fields[name];
        }
    }, this);
    Object.keys(entry.binaries).forEach(function(name) {
        if (entry.binaries[name] instanceof ProtectedValue) {
            this.binaries[name] = entry.binaries[name].clone();
        } else if (entry.binaries[name] && entry.binaries[name].ref) {
            this.binaries[name] = { ref: entry.binaries[name].ref };
        } else {
            this.binaries[name] = entry.binaries[name];
        }
    }, this);
    this.autoType = JSON.parse(JSON.stringify(entry.autoType));
};

/**
 * Creates new entry
 * @param {KdbxMeta} meta - db metadata
 * @param {KdbxGroup} parentGroup - parent group
 * @returns {KdbxEntry}
 */
KdbxEntry.create = function(meta, parentGroup) {
    var entry = new KdbxEntry(parentGroup);
    entry.uuid = KdbxUuid.random();
    entry.icon = Consts.Icons.Key;
    entry.times = KdbxTimes.create();
    entry.parentGroup = parentGroup;
    entry._setField('Title', '', meta.memoryProtection.title);
    entry._setField('UserName', meta.defaultUser || '', meta.memoryProtection.userName);
    entry._setField('Password', '', meta.memoryProtection.password);
    entry._setField('URL', '', meta.memoryProtection.url);
    entry._setField('Notes', '', meta.memoryProtection.notes);
    // TODO: set autoType
    return entry;
};

/**
 * Read entry from xml
 * @param {Node} xmlNode
 * @param {KdbxGroup} [parentGroup]
 * @return {KdbxEntry}
 */
KdbxEntry.read = function(xmlNode, parentGroup) {
    var entry = new KdbxEntry();
    for (var i = 0, cn = xmlNode.childNodes, len = cn.length; i < len; i++) {
        var childNode = cn[i];
        if (childNode.tagName) {
            entry._readNode(childNode);
        }
    }
    entry.parentGroup = parentGroup;
    return entry;
};

module.exports = KdbxEntry;
<commit_msg>fix entry history write<commit_after>'use strict';

var ProtectedValue = require('../crypto/protected-value'),
    XmlNames = require('./../defs/xml-names'),
    XmlUtils = require('./../utils/xml-utils'),
    Consts = require('../defs/consts'),
    KdbxUuid = require('./kdbx-uuid'),
    KdbxTimes = require('./kdbx-times');

var tagsSplitRegex = /\s*[;,:]\s*/;

/**
 * Entry
 * @constructor
 */
var KdbxEntry = function() {
    this.uuid = undefined;
    this.icon = undefined;
    this.customIcon = undefined;
    this.fgColor = undefined;
    this.bgColor = undefined;
    this.overrideUrl = undefined;
    this.tags = [];
    this.times = new KdbxTimes();
    this.fields = {};
    this.binaries = {};
    this.autoType = {
        enabled: undefined, obfuscation: undefined, defaultSequence: undefined, items: []
    };
    this.history = [];
    this.parentGroup = undefined;
    Object.preventExtensions(this);
};

KdbxEntry.prototype._readNode = function(node) {
    switch (node.tagName) {
        case XmlNames.Elem.Uuid:
            this.uuid = XmlUtils.getUuid(node);
            break;
        case XmlNames.Elem.Icon:
            this.icon = XmlUtils.getNumber(node);
            break;
        case XmlNames.Elem.CustomIconID:
            this.customIcon = XmlUtils.getUuid(node);
            break;
        case XmlNames.Elem.FgColor:
            this.fgColor = XmlUtils.getText(node);
            break;
        case XmlNames.Elem.BgColor:
            this.bgColor = XmlUtils.getText(node);
            break;
        case XmlNames.Elem.OverrideUrl:
            this.overrideUrl = XmlUtils.getText(node);
            break;
        case XmlNames.Elem.Tags:
            this.tags = this._stringToTags(XmlUtils.getText(node));
            break;
        case XmlNames.Elem.Times:
            this.times = KdbxTimes.read(node);
            break;
        case XmlNames.Elem.String:
            this._readField(node);
            break;
        case XmlNames.Elem.Binary:
            this._readBinary(node);
            break;
        case XmlNames.Elem.AutoType:
            this._readAutoType(node);
            break;
        case XmlNames.Elem.History:
            this._readHistory(node);
            break;
    }
};

KdbxEntry.prototype._readField = function(node) {
    var keyNode = XmlUtils.getChildNode(node, XmlNames.Elem.Key),
        valueNode = XmlUtils.getChildNode(node, XmlNames.Elem.Value),
        key = XmlUtils.getText(keyNode),
        value = XmlUtils.getProtectedText(valueNode);
    if (key && value) {
        this.fields[key] = value;
    }
};

KdbxEntry.prototype._writeFields = function(parentNode) {
    var fields = this.fields;
    Object.keys(fields).forEach(function(field) {
        var value = fields[field];
        if (value !== undefined && value !== null) {
            var node = XmlUtils.addChildNode(parentNode, XmlNames.Elem.String);
            XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.Key), field);
            XmlUtils.setProtectedText(XmlUtils.addChildNode(node, XmlNames.Elem.Value), value);
        }
    });
};

KdbxEntry.prototype._readBinary = function(node) {
    var keyNode = XmlUtils.getChildNode(node, XmlNames.Elem.Key),
        valueNode = XmlUtils.getChildNode(node, XmlNames.Elem.Value),
        key = XmlUtils.getText(keyNode),
        value = XmlUtils.getProtectedBinary(valueNode);
    if (key && value) {
        this.binaries[key] = value;
    }
};

KdbxEntry.prototype._writeBinaries = function(parentNode) {
    var binaries = this.binaries;
    Object.keys(binaries).forEach(function(id) {
        var data = binaries[id];
        if (data) {
            var node = XmlUtils.addChildNode(parentNode, XmlNames.Elem.Binary);
            XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.Key), id);
            XmlUtils.setProtectedBinary(XmlUtils.addChildNode(node, XmlNames.Elem.Value), data);
        }
    });
};

KdbxEntry.prototype._stringToTags = function(str) {
    if (!str) {
        return [];
    }
    return str.split(tagsSplitRegex).filter(function(s) { return s; });
};

KdbxEntry.prototype._readAutoType = function(node) {
    for (var i = 0, cn = node.childNodes, len = cn.length; i < len; i++) {
        var childNode = cn[i];
        switch (childNode.tagName) {
            case XmlNames.Elem.AutoTypeEnabled:
                this.autoType.enabled = XmlUtils.getBoolean(childNode);
                break;
            case XmlNames.Elem.AutoTypeObfuscation:
                this.autoType.obfuscation = XmlUtils.getNumber(childNode);
                break;
            case XmlNames.Elem.AutoTypeDefaultSeq:
                this.autoType.defaultSequence = XmlUtils.getText(childNode);
                break;
            case XmlNames.Elem.AutoTypeItem:
                this._readAutoTypeItem(childNode);
                break;
        }
    }
};

KdbxEntry.prototype._readAutoTypeItem = function(node) {
    var item = {};
    for (var i = 0, cn = node.childNodes, len = cn.length; i < len; i++) {
        var childNode = cn[i];
        switch (childNode.tagName) {
            case XmlNames.Elem.Window:
                item.window = XmlUtils.getText(childNode);
                break;
            case XmlNames.Elem.KeystrokeSequence:
                item.keystrokeSequence = XmlUtils.getText(childNode);
                break;
        }
    }
    this.autoType.items.push(item);
};

KdbxEntry.prototype._writeAutoType = function(parentNode) {
    var node = XmlUtils.addChildNode(parentNode, XmlNames.Elem.AutoType);
    XmlUtils.setBoolean(XmlUtils.addChildNode(node, XmlNames.Elem.AutoTypeEnabled), this.autoType.enabled);
    XmlUtils.setNumber(XmlUtils.addChildNode(node, XmlNames.Elem.AutoTypeObfuscation), this.autoType.obfuscation);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.AutoTypeDefaultSeq), this.autoType.defaultSequence);
    for (var i = 0; i < this.autoType.items.length; i++) {
        var item = this.autoType.items[i];
        var itemNode = XmlUtils.addChildNode(node, XmlNames.Elem.AutoTypeItem);
        XmlUtils.setText(XmlUtils.addChildNode(itemNode, XmlNames.Elem.Window), item.window);
        XmlUtils.setText(XmlUtils.addChildNode(itemNode, XmlNames.Elem.KeystrokeSequence), item.keystrokeSequence);
    }
};

KdbxEntry.prototype._readHistory = function(node) {
    for (var i = 0, cn = node.childNodes, len = cn.length; i < len; i++) {
        var childNode = cn[i];
        switch (childNode.tagName) {
            case XmlNames.Elem.Entry:
                this.history.push(KdbxEntry.read(childNode));
                break;
        }
    }
};

KdbxEntry.prototype._writeHistory = function(parentNode) {
    var historyNode = XmlUtils.addChildNode(parentNode, XmlNames.Elem.History);
    for (var i = 0; i < this.history.length; i++) {
        this.history[i].write(historyNode);
    }
};

KdbxEntry.prototype._setField = function(name, str, secure) {
    this.fields[name] = secure ? ProtectedValue.fromString(str) : str;
};

/**
 * Write to stream
 * @param {Node} parentNode - xml document node
 */
KdbxEntry.prototype.write = function(parentNode) {
    var node = XmlUtils.addChildNode(parentNode, XmlNames.Elem.Entry);
    XmlUtils.setUuid(XmlUtils.addChildNode(node, XmlNames.Elem.Uuid), this.uuid);
    XmlUtils.setNumber(XmlUtils.addChildNode(node, XmlNames.Elem.Icon), this.icon);
    XmlUtils.setUuid(XmlUtils.addChildNode(node, XmlNames.Elem.CustomIconID), this.customIcon);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.FgColor), this.fgColor);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.BgColor), this.bgColor);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.OverrideUrl), this.overrideUrl);
    XmlUtils.setText(XmlUtils.addChildNode(node, XmlNames.Elem.Tags), this.tags.join(','));
    this.times.write(node);
    this._writeFields(node);
    this._writeBinaries(node);
    this._writeAutoType(node);
    if (parentNode.tagName !== XmlNames.Elem.History) {
        this._writeHistory(node);
    }
};

/**
 * Push current entry state to the top of history
 */
KdbxEntry.prototype.pushHistory = function() {
    var historyEntry = new KdbxEntry();
    historyEntry.copyFrom(this);
    this.history.push(historyEntry);
};

/**
 * Clone entry state from another entry, or history entry
 */
KdbxEntry.prototype.copyFrom = function(entry) {
    this.uuid = entry.uuid;
    this.icon = entry.icon;
    this.customIcon = entry.customIcon;
    this.fgColor = entry.fgColor;
    this.bgColor = entry.bgColor;
    this.overrideUrl = entry.overrideUrl;
    this.tags = entry.tags.slice();
    this.times = entry.times.clone();
    Object.keys(entry.fields).forEach(function(name) {
        if (entry.fields[name] instanceof ProtectedValue) {
            this.fields[name] = entry.fields[name].clone();
        } else {
            this.fields[name] = entry.fields[name];
        }
    }, this);
    Object.keys(entry.binaries).forEach(function(name) {
        if (entry.binaries[name] instanceof ProtectedValue) {
            this.binaries[name] = entry.binaries[name].clone();
        } else if (entry.binaries[name] && entry.binaries[name].ref) {
            this.binaries[name] = { ref: entry.binaries[name].ref };
        } else {
            this.binaries[name] = entry.binaries[name];
        }
    }, this);
    this.autoType = JSON.parse(JSON.stringify(entry.autoType));
};

/**
 * Creates new entry
 * @param {KdbxMeta} meta - db metadata
 * @param {KdbxGroup} parentGroup - parent group
 * @returns {KdbxEntry}
 */
KdbxEntry.create = function(meta, parentGroup) {
    var entry = new KdbxEntry(parentGroup);
    entry.uuid = KdbxUuid.random();
    entry.icon = Consts.Icons.Key;
    entry.times = KdbxTimes.create();
    entry.parentGroup = parentGroup;
    entry._setField('Title', '', meta.memoryProtection.title);
    entry._setField('UserName', meta.defaultUser || '', meta.memoryProtection.userName);
    entry._setField('Password', '', meta.memoryProtection.password);
    entry._setField('URL', '', meta.memoryProtection.url);
    entry._setField('Notes', '', meta.memoryProtection.notes);
    // TODO: set autoType
    return entry;
};

/**
 * Read entry from xml
 * @param {Node} xmlNode
 * @param {KdbxGroup} [parentGroup]
 * @return {KdbxEntry}
 */
KdbxEntry.read = function(xmlNode, parentGroup) {
    var entry = new KdbxEntry();
    for (var i = 0, cn = xmlNode.childNodes, len = cn.length; i < len; i++) {
        var childNode = cn[i];
        if (childNode.tagName) {
            entry._readNode(childNode);
        }
    }
    entry.parentGroup = parentGroup;
    return entry;
};

module.exports = KdbxEntry;
<|endoftext|>


<commit_before>'use strict';

var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var XRegExp = require('xregexp').XRegExp;

var BuildView = require('./build-view');
var MessageBubble = require ("./messageBubble");

module.exports = {
  config: {
    panelVisibility: {
      title: 'Panel Visibility',
      description: 'Set when the build panel should be visible.',
      type: 'string',
      default: 'Toggle',
      enum: [ 'Toggle', 'Keep Visible', 'Show on Error' ],
      order: 1
    },
    monocleHeight: {
      title: 'Monocle Height',
      description: 'How much of the workspace to use for build panel when it is "maximized".',
      type: 'number',
      default: 0.75,
      minimum: 0.1,
      maximum: 0.9,
      order: 4
    },
    minimizedHeight: {
      title: 'Minimized Height',
      description: 'How much of the workspace to use for build panel when it is "minimized".',
      type: 'number',
      default: 0.15,
      minimum: 0.1,
      maximum: 0.9,
      order: 5
    }
  },

  activate: function(state) {
    this.debug = true;
    console.log("Activating F* interactive!");
    this.buildView = new BuildView();
    this.cmd = {};
    this.match = [];
    this.stdout = new Buffer(0);
    this.stderr = new Buffer(0);
    atom.commands.add('atom-workspace', 'fstar-interactive:trigger',      this.checkSelection.bind(this));
    atom.commands.add('atom-workspace', 'fstar-interactive:next-error',   this.nextError.bind(this));
    atom.commands.add('atom-workspace', 'fstar-interactive:stop',         this.stop.bind(this));
    atom.commands.add('atom-workspace', 'fstar-interactive:all-errors',   this.showBuildView.bind(this));
  },

  deactivate: function() {
    if (this.child) {
      this.child.kill('SIGKILL');
    }
    clearTimeout(this.finishedTimer);
  },

  cwd : function () {
     var textEditor = atom.workspace.getActiveTextEditor();
     if (textEditor && 'untitled' !== textEditor.getTitle()) {
         var activeFile = fs.realpathSync(textEditor.getPath());
         return path.dirname(activeFile)
     }
  },

  // Parse this block out of the current file
  //(*--F*-interactive:
  //    ['foo.fst bar.fst --admit_fsi Bar']
  //  --*)
  fstarArgs : function () { //TODO
    return ['--in'];
  },

  buildCommand: function (batch) {
    return {
      exec:'fstar',
      args:this.fstarArgs(),
      cwd:this.cwd()
    };
  },

  onStdout: function (buffer) {
    this.stdout = Buffer.concat([ this.stdout, buffer ]);
    this.buildView.append(buffer);
    if (buffer && this.lastDecoration) {
      var str = buffer.toString().trim();
      if (str.endsWith("#ok")) {
        this.lastDecoration.setProperties({type:'line', class:'line-checked'});
      } else if (str.endsWith("#fail")) {
        this.lastDecoration.setProperties({type:'line', class:'line-check-failed'});
        var marker = this.lastDecoration.getMarker();
        var _this = this;
        this.highlightErrors();
        setTimeout(function () { _this.popMarker(_this, marker)({check_failed:true}); },
                   500);
      }
    }
    if (this.debug) {
      console.log(buffer.toString());
    }
  },

  onStderr: function (buffer) {
    this.stderr = Buffer.concat([ this.stderr, buffer ]);
    this.buildView.append(buffer);
    this.buildView.show();
  },

  onError: function(err) {
    this.buildView.append((this.cmd.sh ? 'Unable to execute with sh: ' : 'Unable to execute: ') + this.cmd.exec);
    this.buildView.append(/\s/.test(this.cmd.exec) ? '`cmd` cannot contain space. Use `args` for arguments.' : '');
    this.buildView.show();
  },

  onClose: function(exitCode) {
    this.buildView.buildFinished(0 === exitCode);
    if (0 === exitCode) {
      this.finishedTimer = setTimeout(function() {
        this.buildView.detach();
      }.bind(this), 1000);
    }
    this.child = null;
    this.clearAllMarkers();
  },

  startChildProcess: function(batch) {
    var cmd = this.buildCommand(batch);
    this.child = child_process.spawn(
      cmd.exec,
      cmd.args,
      { cwd: cmd.cwd }
    );

    this.stdout = new Buffer(0);
    this.stderr = new Buffer(0);

    this.child.stdout.on('data', this.onStdout.bind(this));
    this.child.stderr.on('data', this.onStderr.bind(this));
    this.child.on('error', this.onError.bind(this));
    this.child.on('close', this.onClose.bind(this));

    if (this.debug) {
      console.log(this.cmd.exec + [ ' ' ].concat(this.cmd.args).join(' '));
    }
  },

  clearAllMarkers: function () {
      if (this.markers) {
        this.markers.forEach(function (marker) { if (marker && !marker.isDestroyed()) { marker.destroy(); } } );
        this.markers=undefined;
      }
  },

  popMarker : function (_this, marker) {
    return function (ev) {

       if (marker.already_popped) {
         return;
       }
       if (!ev.textChanged && !ev.check_failed) {
         return;
       }
       if (!_this.child) {
         return;
       }
      //  _this.buildView.append("Calling popMarker on " +marker);
       while (true) {
         var m = _this.markers.shift();
         _this.child.stdin.write("#pop\n");
         if (!m.isDestroyed()) {
              m.already_popped = true;
              m.destroy();
         }
         if (m === marker) {
           return;
         }
       }
    };
  },

  //The main function
  checkSelection: function() {
    this.stdout = new Buffer(0);
    this.stderr = new Buffer(0);
    this.buildView.reset();
    if (!this.child) {
      this.startChildProcess();
    }
    var editor = atom.workspace.getActiveTextEditor();
    if (!this.markers) {
      var start = [[0,0], [0,0]];
      this.markers = [editor.markBufferRange(start, {invalidate:'never'})];
    }

    var lastMarker = this.markers[0];
    var currentPos = editor.getCursorBufferPosition();

    if (this.debug) {
      console.log("currentPos is " +currentPos);
      console.log("lastMarker end is " +lastMarker.getEndBufferPosition());
    }

    var nextRange = [lastMarker.getEndBufferPosition(), currentPos];

    if (this.debug) {
      console.log("nextRange is " +nextRange.toString());
    }

    var nextMarker = editor.markBufferRange(nextRange, {invalidate:'inside', persistent:'false'});
    this.markers.unshift(nextMarker);
    nextMarker.onDidChange(this.popMarker(this, nextMarker));
    var decoration = editor.decorateMarker(nextMarker, {type:'line', class:'line-checking'});
    var code = editor.getTextInBufferRange(nextRange);
    this.lastDecoration = decoration;

    if (this.debug) {
      console.log("Sending selection to process:\n" + code);
    }

    this.child.stdin.write("#push\n");
    this.child.stdin.write(code);
    this.child.stdin.write("\n");
    this.child.stdin.write("#end #ok #fail\n");
  },

  parseAllErrors : function () {
    var lines = this.stdout.toString().split("\n");
    var errors=[];
    var err =
      XRegExp('(?<source>  [^\\(]+) \\(' +
              '(?<lstart>   [0-9]+) ,' +
              '(?<rstart>   [0-9]+) -' +
              '(?<lend>     [0-9]+) ,' +
              '(?<rend>     [0-9]+) \\):' +
              '(?<message>  .*)', 'x');
    lines.forEach(function (line) {
        var e = XRegExp.exec(line, err, 0, true);
        if (e) {
          errors.push(e);
        } else {
          console.log("Not an error line : <" +line+ ">");
        }});
    this.errors = errors;
  },

  logError : function (err) {
      console.log("ERROR!:\n" +err.toString());
  },

  errorBufferRange : function (err) {
    var base = this.markers[0];
    if (!base) return;
    var basePos = base.getStartBufferPosition();
    var start = basePos.translate([+err.lstart - 1, +err.rstart]);
    var end = basePos.translate([+err.lend - 1, +err.rend]);
    return [start, end];
  },

  highlightErrors : function () {
    this.parseAllErrors();
    var base = this.markers[0];
    if (!base) return;
    var editor = atom.workspace.getActiveTextEditor();
    var _this = this;
    this.errors.forEach(function (err) {
        if (err.source = "<input>") {
            _this.logError(err);
            var errRange = _this.errorBufferRange(err);
            var errMarker = editor.markBufferRange(errRange, {invalidate:'touch'});
            errMarker.onDidChange(function () { errMarker.destroy(); });
            console.log("Marking region: " +(errRange) + "\n marker range is " + errMarker.getBufferRange());
            console.log("Text of region is [" + editor.getTextInBufferRange(errMarker.getBufferRange()) +  "]");
            editor.decorateMarker(errMarker,
              {type:'highlight', class:'highlight-squiggly'});
            editor.decorateMarker(errMarker,
              {type:'line-number', class:'line-number-red'});
            var mb = new MessageBubble();
            mb.append(err.message.trim());
            editor.decorateMarker(errMarker,
              {type:'overlay', item: mb});
        }
    });
  },

  nextError : function () {
      var nextError = this.errors.shift();
      var rng = this.errorBufferRange(nextError);
      this.errors.push(nextError);
      var textEditor = atom.workspace.getActiveTextEditor();
      if (nextError.source === "<input>") { //current file
          textEditor.setCursorBufferPosition(rng[0]);
      }
      else {
       atom.workspace.open(this.cwd() + "/" + nextError.source, {
          initialLine: rng[0][0],
          initialColumn: rng[0][1]
        });
      }
  },

  showBuildView : function () {
    this.buildView.show();
  },

  abort: function(cb) {
    this.child.removeAllListeners('close');
    this.child.on('close', function() {
      this.child = null;
      if (cb) {
        cb();
      }
    }.bind(this));
    this.child.kill();
    this.child.killed = true;
    this.clearAllMarkers();
  },

  stop: function() {
    clearTimeout(this.finishedTimer);
    this.clearAllMarkers();
    if (this.child) {
      if (this.child.killed){
        // This child has been killed, but hasn't terminated. Hide it from user.
        this.child.removeAllListeners();
        this.child = null;
        this.buildView.buildAborted();
        return;
      }

      this.abort(this.buildView.buildAborted.bind(this.buildView));

      this.buildView.buildAbortInitiated();
    } else {
      this.buildView.reset();
    }
  }
};
<commit_msg>exe extension<commit_after>'use strict';

var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var XRegExp = require('xregexp').XRegExp;

var BuildView = require('./build-view');
var MessageBubble = require ("./messageBubble");

module.exports = {
  config: {
    panelVisibility: {
      title: 'Panel Visibility',
      description: 'Set when the build panel should be visible.',
      type: 'string',
      default: 'Toggle',
      enum: [ 'Toggle', 'Keep Visible', 'Show on Error' ],
      order: 1
    },
    monocleHeight: {
      title: 'Monocle Height',
      description: 'How much of the workspace to use for build panel when it is "maximized".',
      type: 'number',
      default: 0.75,
      minimum: 0.1,
      maximum: 0.9,
      order: 4
    },
    minimizedHeight: {
      title: 'Minimized Height',
      description: 'How much of the workspace to use for build panel when it is "minimized".',
      type: 'number',
      default: 0.15,
      minimum: 0.1,
      maximum: 0.9,
      order: 5
    }
  },

  activate: function(state) {
    this.debug = true;
    console.log("Activating F* interactive!");
    this.buildView = new BuildView();
    this.cmd = {};
    this.match = [];
    this.stdout = new Buffer(0);
    this.stderr = new Buffer(0);
    atom.commands.add('atom-workspace', 'fstar-interactive:trigger',      this.checkSelection.bind(this));
    atom.commands.add('atom-workspace', 'fstar-interactive:next-error',   this.nextError.bind(this));
    atom.commands.add('atom-workspace', 'fstar-interactive:stop',         this.stop.bind(this));
    atom.commands.add('atom-workspace', 'fstar-interactive:all-errors',   this.showBuildView.bind(this));
  },

  deactivate: function() {
    if (this.child) {
      this.child.kill('SIGKILL');
    }
    clearTimeout(this.finishedTimer);
  },

  cwd : function () {
     var textEditor = atom.workspace.getActiveTextEditor();
     if (textEditor && 'untitled' !== textEditor.getTitle()) {
         var activeFile = fs.realpathSync(textEditor.getPath());
         return path.dirname(activeFile)
     }
  },

  // Parse this block out of the current file
  //(*--F*-interactive:
  //    ['foo.fst bar.fst --admit_fsi Bar']
  //  --*)
  fstarArgs : function () { //TODO
    return ['--in'];
  },

  buildCommand: function (batch) {
    return {
      exec:'fstar.exe',
      args:this.fstarArgs(),
      cwd:this.cwd()
    };
  },

  onStdout: function (buffer) {
    this.stdout = Buffer.concat([ this.stdout, buffer ]);
    this.buildView.append(buffer);
    if (buffer && this.lastDecoration) {
      var str = buffer.toString().trim();
      if (str.endsWith("#ok")) {
        this.lastDecoration.setProperties({type:'line', class:'line-checked'});
      } else if (str.endsWith("#fail")) {
        this.lastDecoration.setProperties({type:'line', class:'line-check-failed'});
        var marker = this.lastDecoration.getMarker();
        var _this = this;
        this.highlightErrors();
        setTimeout(function () { _this.popMarker(_this, marker)({check_failed:true}); },
                   500);
      }
    }
    if (this.debug) {
      console.log(buffer.toString());
    }
  },

  onStderr: function (buffer) {
    this.stderr = Buffer.concat([ this.stderr, buffer ]);
    this.buildView.append(buffer);
    this.buildView.show();
  },

  onError: function(err) {
    this.buildView.append((this.cmd.sh ? 'Unable to execute with sh: ' : 'Unable to execute: ') + this.cmd.exec);
    this.buildView.append(/\s/.test(this.cmd.exec) ? '`cmd` cannot contain space. Use `args` for arguments.' : '');
    this.buildView.show();
  },

  onClose: function(exitCode) {
    this.buildView.buildFinished(0 === exitCode);
    if (0 === exitCode) {
      this.finishedTimer = setTimeout(function() {
        this.buildView.detach();
      }.bind(this), 1000);
    }
    this.child = null;
    this.clearAllMarkers();
  },

  startChildProcess: function(batch) {
    var cmd = this.buildCommand(batch);
    this.child = child_process.spawn(
      cmd.exec,
      cmd.args,
      { cwd: cmd.cwd }
    );

    this.stdout = new Buffer(0);
    this.stderr = new Buffer(0);

    this.child.stdout.on('data', this.onStdout.bind(this));
    this.child.stderr.on('data', this.onStderr.bind(this));
    this.child.on('error', this.onError.bind(this));
    this.child.on('close', this.onClose.bind(this));

    if (this.debug) {
      console.log(this.cmd.exec + [ ' ' ].concat(this.cmd.args).join(' '));
    }
  },

  clearAllMarkers: function () {
      if (this.markers) {
        this.markers.forEach(function (marker) { if (marker && !marker.isDestroyed()) { marker.destroy(); } } );
        this.markers=undefined;
      }
  },

  popMarker : function (_this, marker) {
    return function (ev) {

       if (marker.already_popped) {
         return;
       }
       if (!ev.textChanged && !ev.check_failed) {
         return;
       }
       if (!_this.child) {
         return;
       }
      //  _this.buildView.append("Calling popMarker on " +marker);
       while (true) {
         var m = _this.markers.shift();
         _this.child.stdin.write("#pop\n");
         if (!m.isDestroyed()) {
              m.already_popped = true;
              m.destroy();
         }
         if (m === marker) {
           return;
         }
       }
    };
  },

  //The main function
  checkSelection: function() {
    this.stdout = new Buffer(0);
    this.stderr = new Buffer(0);
    this.buildView.reset();
    if (!this.child) {
      this.startChildProcess();
    }
    var editor = atom.workspace.getActiveTextEditor();
    if (!this.markers) {
      var start = [[0,0], [0,0]];
      this.markers = [editor.markBufferRange(start, {invalidate:'never'})];
    }

    var lastMarker = this.markers[0];
    var currentPos = editor.getCursorBufferPosition();

    if (this.debug) {
      console.log("currentPos is " +currentPos);
      console.log("lastMarker end is " +lastMarker.getEndBufferPosition());
    }

    var nextRange = [lastMarker.getEndBufferPosition(), currentPos];

    if (this.debug) {
      console.log("nextRange is " +nextRange.toString());
    }

    var nextMarker = editor.markBufferRange(nextRange, {invalidate:'inside', persistent:'false'});
    this.markers.unshift(nextMarker);
    nextMarker.onDidChange(this.popMarker(this, nextMarker));
    var decoration = editor.decorateMarker(nextMarker, {type:'line', class:'line-checking'});
    var code = editor.getTextInBufferRange(nextRange);
    this.lastDecoration = decoration;

    if (this.debug) {
      console.log("Sending selection to process:\n" + code);
    }

    this.child.stdin.write("#push\n");
    this.child.stdin.write(code);
    this.child.stdin.write("\n");
    this.child.stdin.write("#end #ok #fail\n");
  },

  parseAllErrors : function () {
    var lines = this.stdout.toString().split("\n");
    var errors=[];
    var err =
      XRegExp('(?<source>  [^\\(]+) \\(' +
              '(?<lstart>   [0-9]+) ,' +
              '(?<rstart>   [0-9]+) -' +
              '(?<lend>     [0-9]+) ,' +
              '(?<rend>     [0-9]+) \\):' +
              '(?<message>  .*)', 'x');
    lines.forEach(function (line) {
        var e = XRegExp.exec(line, err, 0, true);
        if (e) {
          errors.push(e);
        } else {
          console.log("Not an error line : <" +line+ ">");
        }});
    this.errors = errors;
  },

  logError : function (err) {
      console.log("ERROR!:\n" +err.toString());
  },

  errorBufferRange : function (err) {
    var base = this.markers[0];
    if (!base) return;
    var basePos = base.getStartBufferPosition();
    var start = basePos.translate([+err.lstart - 1, +err.rstart]);
    var end = basePos.translate([+err.lend - 1, +err.rend]);
    return [start, end];
  },

  highlightErrors : function () {
    this.parseAllErrors();
    var base = this.markers[0];
    if (!base) return;
    var editor = atom.workspace.getActiveTextEditor();
    var _this = this;
    this.errors.forEach(function (err) {
        if (err.source = "<input>") {
            _this.logError(err);
            var errRange = _this.errorBufferRange(err);
            var errMarker = editor.markBufferRange(errRange, {invalidate:'touch'});
            errMarker.onDidChange(function () { errMarker.destroy(); });
            console.log("Marking region: " +(errRange) + "\n marker range is " + errMarker.getBufferRange());
            console.log("Text of region is [" + editor.getTextInBufferRange(errMarker.getBufferRange()) +  "]");
            editor.decorateMarker(errMarker,
              {type:'highlight', class:'highlight-squiggly'});
            editor.decorateMarker(errMarker,
              {type:'line-number', class:'line-number-red'});
            var mb = new MessageBubble();
            mb.append(err.message.trim());
            editor.decorateMarker(errMarker,
              {type:'overlay', item: mb});
        }
    });
  },

  nextError : function () {
      var nextError = this.errors.shift();
      var rng = this.errorBufferRange(nextError);
      this.errors.push(nextError);
      var textEditor = atom.workspace.getActiveTextEditor();
      if (nextError.source === "<input>") { //current file
          textEditor.setCursorBufferPosition(rng[0]);
      }
      else {
       atom.workspace.open(this.cwd() + "/" + nextError.source, {
          initialLine: rng[0][0],
          initialColumn: rng[0][1]
        });
      }
  },

  showBuildView : function () {
    this.buildView.show();
  },

  abort: function(cb) {
    this.child.removeAllListeners('close');
    this.child.on('close', function() {
      this.child = null;
      if (cb) {
        cb();
      }
    }.bind(this));
    this.child.kill();
    this.child.killed = true;
    this.clearAllMarkers();
  },

  stop: function() {
    clearTimeout(this.finishedTimer);
    this.clearAllMarkers();
    if (this.child) {
      if (this.child.killed){
        // This child has been killed, but hasn't terminated. Hide it from user.
        this.child.removeAllListeners();
        this.child = null;
        this.buildView.buildAborted();
        return;
      }

      this.abort(this.buildView.buildAborted.bind(this.buildView));

      this.buildView.buildAbortInitiated();
    } else {
      this.buildView.reset();
    }
  }
};
<|endoftext|>


<commit_before>// @flow
import React from 'react'

import {toPrettyTerm, expandYear} from '@gob/school-st-olaf-college'
import {Card} from '../../components/card'
import {LabelledSelect} from './labelled-select'
import {FlatButton} from '../../components/button'
import toPairs from 'lodash/toPairs'
import Loading from '../../components/loading'
import {
	SORT_BY,
	GROUP_BY,
	type SORT_BY_KEY,
	type GROUP_BY_KEY,
} from './constants'
import {CourseResultsList} from './results-list'
import {Querent} from './querent'

import './searcher.scss'

type Props = {
	onCloseSearcher?: ?() => mixed,
	term?: ?number,
	studentId?: string,
}

type State = {
	query: string,
	groupBy: GROUP_BY_KEY,
	sortBy: SORT_BY_KEY,
	limitTo: string,
	filterBy: string,
	hasQueried: boolean,
}

export class CourseSearcher extends React.Component<Props, State> {
	state = {
		groupBy: 'term',
		sortBy: 'title',
		limitTo: '',
		filterBy: '',
		query: '',
		hasQueried: false,
	}

	handleSortChange = (ev: SyntheticEvent<HTMLSelectElement>) => {
		let value: string = ev.currentTarget.value
		this.setState(() => ({sortBy: (value: any)}))
	}

	handleGroupByChange = (ev: SyntheticEvent<HTMLSelectElement>) => {
		let value: string = ev.currentTarget.value
		this.setState(() => ({groupBy: (value: any), filterBy: ''}))
	}

	handleFilterByChange = (ev: SyntheticEvent<HTMLSelectElement>) => {
		let value: string = ev.currentTarget.value
		this.setState(() => ({filterBy: (value: any)}))
	}

	handleLimitToChange = (ev: SyntheticEvent<HTMLSelectElement>) => {
		let value: string = ev.currentTarget.value
		this.setState(() => ({limitTo: (value: any)}))
	}

	updateQuery = (query: string) => {
		this.setState(() => ({query}))
	}

	handleQueryChange = (ev: SyntheticKeyboardEvent<HTMLInputElement>) => {
		this.updateQuery(ev.currentTarget.value)
	}

	render() {
		let {groupBy, query, sortBy, filterBy, limitTo} = this.state

		let {onCloseSearcher, studentId, term} = this.props

		// This tells React to unmount and recreate the search hierarchy, so that it all updates as we type
		let termKey = String(term)
		let key = `${query}-${termKey}-${groupBy}-${sortBy}-${filterBy}-${limitTo}`

		return (
			<>
				<Card as="header" className="sidebar-heading">

						Course Search
						{term && (
							<>

({toPrettyTerm(term)})
							</>
						)}

					{onCloseSearcher && (
						<FlatButton
							className="close-sidebar"
							title="Close Search"
							onClick={onCloseSearcher}
						>
							Close
						</FlatButton>
					)}

					<input
						type="search"
						className="search-box"
						value={query}
						placeholder="Search for a course or phrase"
						onChange={this.handleQueryChange}
						// autoFocus={true}
					/>
				</Card>

				<Querent
					key={key}
					query={query}
					groupBy={groupBy}
					sortBy={sortBy}
					term={term}
					filterBy={filterBy}
					limitTo={limitTo}
				>
					{({error, inProgress, results, didSearch, keys, years}) => {
						if (error) {
							return (
								<Card className="course-results--notice">
									Something broke :-(
								</Card>
							)
						}

						let potentialFilters = keys.map(k => [k, k])
						potentialFilters.unshift(['', 'No Filter'])

						let potentialYearLimits = years
							.map(k => [String(k), expandYear(k)])
							.toArray()
						potentialYearLimits.unshift(['', 'All Years'])

						let filters = (
							<Card className="search-filters">
								<LabelledSelect
									label="Limit to:"
									options={potentialYearLimits}
									onChange={this.handleLimitToChange}
									value={limitTo}
								/>

								<LabelledSelect
									label="Sort by:"
									options={toPairs(SORT_BY)}
									onChange={this.handleSortChange}
									value={sortBy}
								/>

								<LabelledSelect
									label="Group by:"
									options={toPairs(GROUP_BY)}
									onChange={this.handleGroupByChange}
									value={groupBy}
								/>

								<LabelledSelect
									label="Filter by:"
									options={potentialFilters}
									onChange={this.handleFilterByChange}
									value={filterBy}
								/>
							</Card>
						)

						if (inProgress) {
							return (
								<>
									{filters}
									<Card className="course-results--notice">
										<Loading>Searching…</Loading>
									</Card>
								</>
							)
						}

						if (results.size === 0) {
							if (!didSearch) {
								return (
									<Card className="course-results--notice">
										Search for something!
									</Card>
								)
							}
							return (
								<>
									{filters}
									<Card className="course-results--notice">
										No Results Found
									</Card>
								</>
							)
						}

						return (
							<>
								{filters}

								<CourseResultsList
									groupedBy={groupBy}
									studentId={studentId}
									results={results}
								/>
							</>
						)
					}}
				</Querent>
			</>
		)
	}
}
<commit_msg>ensure that the selected limiting year is shown in the dropdown<commit_after>// @flow
import React from 'react'

import {toPrettyTerm, expandYear} from '@gob/school-st-olaf-college'
import {Card} from '../../components/card'
import {LabelledSelect} from './labelled-select'
import {FlatButton} from '../../components/button'
import toPairs from 'lodash/toPairs'
import Loading from '../../components/loading'
import {
	SORT_BY,
	GROUP_BY,
	type SORT_BY_KEY,
	type GROUP_BY_KEY,
} from './constants'
import {CourseResultsList} from './results-list'
import {Querent} from './querent'

import './searcher.scss'

type Props = {
	onCloseSearcher?: ?() => mixed,
	term?: ?number,
	studentId?: string,
}

type State = {
	query: string,
	groupBy: GROUP_BY_KEY,
	sortBy: SORT_BY_KEY,
	limitTo: string,
	filterBy: string,
	hasQueried: boolean,
}

export class CourseSearcher extends React.Component<Props, State> {
	state = {
		groupBy: 'term',
		sortBy: 'title',
		limitTo: '',
		filterBy: '',
		query: '',
		hasQueried: false,
	}

	handleSortChange = (ev: SyntheticEvent<HTMLSelectElement>) => {
		let value: string = ev.currentTarget.value
		this.setState(() => ({sortBy: (value: any)}))
	}

	handleGroupByChange = (ev: SyntheticEvent<HTMLSelectElement>) => {
		let value: string = ev.currentTarget.value
		this.setState(() => ({groupBy: (value: any), filterBy: ''}))
	}

	handleFilterByChange = (ev: SyntheticEvent<HTMLSelectElement>) => {
		let value: string = ev.currentTarget.value
		this.setState(() => ({filterBy: (value: any)}))
	}

	handleLimitToChange = (ev: SyntheticEvent<HTMLSelectElement>) => {
		let value: string = ev.currentTarget.value
		this.setState(() => ({limitTo: (value: any)}))
	}

	updateQuery = (query: string) => {
		this.setState(() => ({query}))
	}

	handleQueryChange = (ev: SyntheticKeyboardEvent<HTMLInputElement>) => {
		this.updateQuery(ev.currentTarget.value)
	}

	render() {
		let {groupBy, query, sortBy, filterBy, limitTo} = this.state

		let {onCloseSearcher, studentId, term} = this.props

		// This tells React to unmount and recreate the search hierarchy, so that it all updates as we type
		let termKey = String(term)
		let key = `${query}-${termKey}-${groupBy}-${sortBy}-${filterBy}-${limitTo}`

		return (
			<>
				<Card as="header" className="sidebar-heading">

						Course Search
						{term && (
							<>

({toPrettyTerm(term)})
							</>
						)}

					{onCloseSearcher && (
						<FlatButton
							className="close-sidebar"
							title="Close Search"
							onClick={onCloseSearcher}
						>
							Close
						</FlatButton>
					)}

					<input
						type="search"
						className="search-box"
						value={query}
						placeholder="Search for a course or phrase"
						onChange={this.handleQueryChange}
						// autoFocus={true}
					/>
				</Card>

				<Querent
					key={key}
					query={query}
					groupBy={groupBy}
					sortBy={sortBy}
					term={term}
					filterBy={filterBy}
					limitTo={limitTo}
				>
					{({error, inProgress, results, didSearch, keys, years}) => {
						if (error) {
							return (
								<Card className="course-results--notice">
									Something broke :-(
								</Card>
							)
						}

						let potentialFilters = keys.map(k => [k, k])
						potentialFilters.unshift(['', 'No Filter'])

						let potentialYearLimits = years
							.map(k => [String(k), expandYear(k)])
							.toArray()
						potentialYearLimits.unshift(['', 'All Years'])

						if (limitTo && !years.has(parseInt(limitTo, 10))) {
							potentialYearLimits.push([
								limitTo,
								expandYear(limitTo),
							])
						}

						let filters = (
							<Card className="search-filters">
								<LabelledSelect
									label="Limit to:"
									options={potentialYearLimits}
									onChange={this.handleLimitToChange}
									value={limitTo}
								/>

								<LabelledSelect
									label="Sort by:"
									options={toPairs(SORT_BY)}
									onChange={this.handleSortChange}
									value={sortBy}
								/>

								<LabelledSelect
									label="Group by:"
									options={toPairs(GROUP_BY)}
									onChange={this.handleGroupByChange}
									value={groupBy}
								/>

								<LabelledSelect
									label="Filter by:"
									options={potentialFilters}
									onChange={this.handleFilterByChange}
									value={filterBy}
								/>
							</Card>
						)

						if (inProgress) {
							return (
								<>
									{filters}
									<Card className="course-results--notice">
										<Loading>Searching…</Loading>
									</Card>
								</>
							)
						}

						if (results.size === 0) {
							if (!didSearch) {
								return (
									<Card className="course-results--notice">
										Search for something!
									</Card>
								)
							}
							return (
								<>
									{filters}
									<Card className="course-results--notice">
										No Results Found
									</Card>
								</>
							)
						}

						return (
							<>
								{filters}

								<CourseResultsList
									groupedBy={groupBy}
									studentId={studentId}
									results={results}
								/>
							</>
						)
					}}
				</Querent>
			</>
		)
	}
}
<|endoftext|>