<commit_before>'use strict';

var OO = require('../basics/oo');
var _ = require('../basics/helpers');
var Component = require('./component');
var Clipboard = require('./surface/clipboard');
var ToolManager = require('./tool_manager');
var Registry = require('../basics/registry');
var Logger = require ('../basics/logger');
var Selection = require('../document/selection');

/**
 * Controls Substance infrastructure. Needs to be supplied as a top level instance
 * to serve editors, commands and tools as a context.
 * 
 * @class
 * @memberof module:ui
 */

function Controller() {
  Component.apply(this, arguments);
  if (!this.props.doc) throw new Error('Controller requires a Substance document instance');
  this.surfaces = {};
  this.focusedSurface = null;
  this.stack = [];
  this.logger = new Logger();
  this._initializeComponentRegistry(this.props.config.components);
  this._initializeCommandRegistry(this.props.config.commands.controller);
  this.clipboard = new Clipboard(this, this.props.doc.getClipboardImporter(), this.props.doc.getClipboardExporter());
  this.toolManager = new ToolManager(this);
  this.props.doc.connect(this, {
    'document:changed': this.onDocumentChanged,
    'transaction:started': this.onTransactionStarted
  }, {
    // Use lower priority so that everyting is up2date
    // when we render the selection
    priority: -10
  });
}

Controller.Prototype = function() {
  /**
   * Defines the child context
   * 
   * @return {object} the child context
   * @method getChildContext
   * @memberof module:ui.Controller.prototype
   */
  this.getChildContext = function() {
    return {
      config: this.props.config,
      controller: this,
      componentRegistry: this.componentRegistry,
      toolManager: this.toolManager
    };
  };

  /**
   * Get the associated ToolManager instance
   * 
   * @return {module:ui.ToolManager} the ToolManager instance
   * @method getToolManager
   * @memberof module:ui.Controller.prototype
   */
  this.getToolManager = function() {
    return this.toolManager;
  };

  this._initializeComponentRegistry = function(components) {
    var componentRegistry = new Registry();
    _.each(components, function(ComponentClass, name) {
      componentRegistry.add(name, ComponentClass);
    });
    this.componentRegistry = componentRegistry;
  };

  this._initializeCommandRegistry = function(commands) {
    var commandRegistry = new Registry();
    _.each(commands, function(CommandClass) {
      var cmd = new CommandClass(this);
      commandRegistry.add(CommandClass.static.name, cmd);
    }, this);
    this.commandRegistry = commandRegistry;
  };

  /**
   * Get registered controller command by name
   * 
   * @param commandName {String} the command name
   * @return {module:ui.commands.ControllerCommand} A controller command
   * @method getCommand
   * @memberof module:ui.Controller.prototype
   */
  this.getCommand = function(commandName) {
    return this.commandRegistry.get(commandName);
  };

  /**
   * Execute command with given name if registered
   * 
   * @param commandName {String} the command name
   * @return {module:ui.commands.ControllerCommand} A controller command
   * @method getCommand
   * @memberof module:ui.Controller.prototype
   */
  this.executeCommand = function(commandName) {
    var cmd = this.getCommand(commandName);
    if (!cmd) {
      console.warn('command', commandName, 'not registered on controller');
      return;
    }
    // Run command
    var info = cmd.execute();
    if (info) {
      this.emit('command:executed', info, commandName, cmd);
      // TODO: We want to replace this with a more specific, scoped event
      // but for that we need an improved EventEmitter API
    } else if (info === undefined) {
      console.warn('command ', commandName, 'must return either an info object or true when handled or false when not handled');
    }
  };

  this.getLogger = function() {
    return this.logger;
  };

  this.getClipboard = function() {
    return this.clipboard;
  };

  /**
   * Get document instance
   *
   * @return {module:document.Document} The document instance owned by the controller
   * @method getDocument
   * @memberof module:ui.Controller.prototype
   */
  this.getDocument = function() {
    return this.props.doc;
  };

  /**
   * Get Surface instance
   *
   * @method getSurface
   * @param name {String} Name under which the surface is registered
   * @return {module:ui.surface.Surface} The surface instance
   * @memberof module:ui.Controller.prototype
   */
  this.getSurface = function(name) {
    if (name) {
      return this.surfaces[name];
    } else {
      // console.warn('Deprecated: Use getFocusedSurface. Always provide a name for getSurface otherwise.');
      return this.focusedSurface || this.surfaces[this.props.config.defaultSurface];
    }
  };

  /**
   * Get the currently focused Surface
   *
   * @method getFocusedSurface
   * @return {module:ui.surface.Surface} The surface instance
   * @memberof module:ui.Controller.prototype
   */
  this.getFocusedSurface = function() {
    return this.getSurface();
  };

  /**
   * Get selection of currently focused surface. We recomment to use getSelection on Surface
   * instances directly when possible.
   *
   * @method getSelection
   * @return {module:document.Document.Selection} the current Document.Selection derived from the surface.
   * @memberof module:ui.Controller.prototype
   */
  this.getSelection = function() {
    var surface = this.getSurface();
    if (surface) {
      return surface.getSelection();  
    } else {
      return Selection.nullSelection;
    }
  };

  /**<commit_msg>Allow provision of controller config via static.config<commit_after>'use strict';

var OO = require('../basics/oo');
var _ = require('../basics/helpers');
var Component = require('./component');
var Clipboard = require('./surface/clipboard');
var ToolManager = require('./tool_manager');
var Registry = require('../basics/registry');
var Logger = require ('../basics/logger');
var Selection = require('../document/selection');

// Setup default I18n
var I18n = require('./i18n');
I18n.instance.load(require('./i18n/en'));

/**
 * Controls Substance infrastructure. Needs to be supplied as a top level instance
 * to serve editors, commands and tools as a context.
 * 
 * @class
 * @memberof module:ui
 */

function Controller() {
  Component.apply(this, arguments);
  if (!this.props.doc) throw new Error('Controller requires a Substance document instance');
  this.surfaces = {};
  this.focusedSurface = null;
  this.stack = [];
  this.logger = new Logger();

  var config = this.getConfig();

  this._initializeComponentRegistry(config.controller.components);
  this._initializeCommandRegistry(config.controller.commands);
  this.clipboard = new Clipboard(this, this.props.doc.getClipboardImporter(), this.props.doc.getClipboardExporter());
  this.toolManager = new ToolManager(this);
  this.props.doc.connect(this, {
    'document:changed': this.onDocumentChanged,
    'transaction:started': this.onTransactionStarted
  }, {
    // Use lower priority so that everyting is up2date
    // when we render the selection
    priority: -10
  });
}

Controller.Prototype = function() {

  // Use static config if available, otherwise try to fetch it from props
  this.getConfig = function() {
    return this.constructor.static.config || this.props.config;
  };

  /**
   * Defines the child context
   * 
   * @return {object} the child context
   * @method getChildContext
   * @memberof module:ui.Controller.prototype
   */
  this.getChildContext = function() {
    return {
      config: this.getConfig(),
      controller: this,
      componentRegistry: this.componentRegistry,
      toolManager: this.toolManager,
      i18n: I18n.instance
    };
  };

  /**
   * Get the associated ToolManager instance
   * 
   * @return {module:ui.ToolManager} the ToolManager instance
   * @method getToolManager
   * @memberof module:ui.Controller.prototype
   */
  this.getToolManager = function() {
    return this.toolManager;
  };

  this._initializeComponentRegistry = function(components) {
    var componentRegistry = new Registry();
    _.each(components, function(ComponentClass, name) {
      componentRegistry.add(name, ComponentClass);
    });
    this.componentRegistry = componentRegistry;
  };

  this._initializeCommandRegistry = function(commands) {
    var commandRegistry = new Registry();
    _.each(commands, function(CommandClass) {
      var cmd = new CommandClass(this);
      commandRegistry.add(CommandClass.static.name, cmd);
    }, this);
    this.commandRegistry = commandRegistry;
  };

  /**
   * Get registered controller command by name
   * 
   * @param commandName {String} the command name
   * @return {module:ui.commands.ControllerCommand} A controller command
   * @method getCommand
   * @memberof module:ui.Controller.prototype
   */
  this.getCommand = function(commandName) {
    return this.commandRegistry.get(commandName);
  };

  /**
   * Execute command with given name if registered
   * 
   * @param commandName {String} the command name
   * @return {module:ui.commands.ControllerCommand} A controller command
   * @method getCommand
   * @memberof module:ui.Controller.prototype
   */
  this.executeCommand = function(commandName) {
    var cmd = this.getCommand(commandName);
    if (!cmd) {
      console.warn('command', commandName, 'not registered on controller');
      return;
    }
    // Run command
    var info = cmd.execute();
    if (info) {
      this.emit('command:executed', info, commandName, cmd);
      // TODO: We want to replace this with a more specific, scoped event
      // but for that we need an improved EventEmitter API
    } else if (info === undefined) {
      console.warn('command ', commandName, 'must return either an info object or true when handled or false when not handled');
    }
  };

  this.getLogger = function() {
    return this.logger;
  };

  this.getClipboard = function() {
    return this.clipboard;
  };

  /**
   * Get document instance
   *
   * @return {module:document.Document} The document instance owned by the controller
   * @method getDocument
   * @memberof module:ui.Controller.prototype
   */
  this.getDocument = function() {
    return this.props.doc;
  };

  /**
   * Get Surface instance
   *
   * @method getSurface
   * @param name {String} Name under which the surface is registered
   * @return {module:ui.surface.Surface} The surface instance
   * @memberof module:ui.Controller.prototype
   */
  this.getSurface = function(name) {
    if (name) {
      return this.surfaces[name];
    } else {
      console.warn('Deprecated: Use getFocusedSurface. Always provide a name for getSurface otherwise.');
      return this.getFocusedSurface();
    }
  };

  /**
   * Get the currently focused Surface
   *
   * @method getFocusedSurface
   * @return {module:ui.surface.Surface} The surface instance
   * @memberof module:ui.Controller.prototype
   */
  this.getFocusedSurface = function() {
    return this.focusedSurface;
  };

  /**
   * Get selection of currently focused surface. We recomment to use getSelection on Surface
   * instances directly when possible.
   *
   * @method getSelection
   * @return {module:document.Document.Selection} the current Document.Selection derived from the surface.
   * @memberof module:ui.Controller.prototype
   */
  this.getSelection = function() {
    var surface = this.getSurface();
    if (surface) {
      return surface.getSelection();  
    } else {
      return Selection.nullSelection;
    }
  };

  /**
{'fraction_non_alphanumeric': 0.11802685950413223, 'fraction_numerical': 0.001721763085399449, 'mean_word_length': 3.3937216338880485, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4355219', 'n_tokens_mistral': 3269, 'n_tokens_neox': 3207, 'n_words': 872}