all files / ui/ CommandManager.js

55.38% Statements 36/65
35.71% Branches 10/28
66.67% Functions 6/9
55.56% Lines 35/63
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156                      206×       206× 206× 206×         206× 206× 1640×     1640×     206× 206×               557× 557×               763× 763× 763× 763×               763× 763× 345× 345× 345×   345×                     6088× 763× 763× 6088× 6088×             763× 763×                                                                                               763×         763× 763× 763× 763× 763×                    
import { forEach, Registry } from '../util'
 
/*
  Listens to changes on the document and selection and updates the commandStates
  accordingly.
 
  @class CommandManager
*/
class CommandManager {
 
  constructor(context, commands) {
    Iif (!context.editorSession) {
      throw new Error('EditorSession required.')
    }
 
    this.editorSession = context.editorSession
    this.doc = this.editorSession.getDocument()
    this.context = Object.assign({}, context, {
      // for convenienve we provide access to the doc directly
      doc: this.doc
    })
    // Set up command registry
    this.commandRegistry = new Registry()
    forEach(commands, function(command) {
      Iif(!command._isCommand) {
        throw new Error("Expecting instances of ui/Command.")
      }
      this.commandRegistry.add(command.name, command)
    }.bind(this))
 
    this.editorSession.onUpdate(this.onSessionUpdate, this)
    this.updateCommandStates(this.editorSession)
  }
 
  dispose() {
    this.editorSession.off(this)
  }
 
  onSessionUpdate(editorSession) {
    Eif (editorSession.hasChanged('change') || editorSession.hasChanged('selection')) {
      this.updateCommandStates(editorSession)
    }
  }
 
  /*
    Compute new command states object
  */
  updateCommandStates(editorSession) {
    const commandContext = this.getCommandContext()
    const params = this._getCommandParams()
    const surface = params.surface
    const commandRegistry = this.commandRegistry
 
    // EXPERIMENTAL:
    // We want to control which commands are available
    // in each surface
    // Trying out a white-list and a black list
    // TODO: discuss, and maybe think about optimizing this
    // by caching the result...
    let commandNames = commandRegistry.names.slice()
    if (surface) {
      let included = surface.props.commands
      let excluded = surface.props.excludedCommands
      Iif (included) {
        commandNames = included
      } else Iif (excluded) {
        excluded = excluded.slice(0)
        for (let i = commandNames.length - 1; i >= 0; i--) {
          let idx = excluded.indexOf(commandNames[i])
          if (idx >= 0) {
            excluded.splice(idx, 1)
            commandNames.splice(i, 1)
          }
        }
      }
    }
    const commands = commandNames.map(name => commandRegistry.get(name))
    let commandStates = {}
    commands.forEach((cmd) => {
      Eif (cmd) {
        commandStates[cmd.getName()] = cmd.getCommandState(params, commandContext)
      }
    })
    // NOTE: We previously did a check if commandStates were actually changed
    // before updating them. However, we currently have complex objects
    // in the command state (e.g. EditInlineNodeCommand) so we had to remove it.
    // See Issue #1004
    this.commandStates = commandStates
    editorSession.setCommandStates(commandStates)
  }
 
  /*
    Execute a command, given a context and arguments.
 
    Commands are run async if cmd.isAsync() returns true.
  */
  executeCommand(commandName, userParams, cb) {
    let cmd = this.commandRegistry.get(commandName)
    if (!cmd) {
      console.warn('command', commandName, 'not registered')
      return
    }
    let commandState = this.commandStates[commandName]
    let params = Object.assign(this._getCommandParams(), userParams, {
      commandState: commandState
    })
 
    if (cmd.isAsync) {
      // TODO: Request UI lock here
      this.editorSession.lock()
      cmd.execute(params, this.getCommandContext(), (err, info) => {
        if (err) {
          if (cb) {
            cb(err)
          } else {
            console.error(err)
          }
        } else {
          if (cb) cb(null, info)
        }
        this.editorSession.unlock()
      })
    } else {
      let info = cmd.execute(params, this.getCommandContext())
      return info
    }
  }
 
  /*
    Exposes the current commandStates object
  */
  getCommandStates() {
    return this.commandStates
  }
 
  getCommandContext() {
    return this.context
  }
 
  // TODO: while we need it here this should go into the flow thingie later
  _getCommandParams() {
    let editorSession = this.context.editorSession
    let selectionState = editorSession.getSelectionState()
    let sel = selectionState.getSelection()
    let surface = this.context.surfaceManager.getFocusedSurface()
    return {
      editorSession: editorSession,
      selectionState: selectionState,
      surface: surface,
      selection: sel,
    }
  }
}
 
export default CommandManager