summaryrefslogtreecommitdiffstats
path: root/devtools/client/webaudioeditor/views/automation.js
blob: 2fab262bd5d5fb97e7145377185e31409e499ea1 (plain)
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
157
158
159
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

/**
 * Functions handling the audio node inspector UI.
 */

var AutomationView = {

  /**
   * Initialization function called when the tool starts up.
   */
  initialize: function () {
    this._buttons = $("#automation-param-toolbar-buttons");
    this.graph = new LineGraphWidget($("#automation-graph"), { avg: false });
    this.graph.selectionEnabled = false;

    this._onButtonClick = this._onButtonClick.bind(this);
    this._onNodeSet = this._onNodeSet.bind(this);
    this._onResize = this._onResize.bind(this);

    this._buttons.addEventListener("click", this._onButtonClick);
    window.on(EVENTS.UI_INSPECTOR_RESIZE, this._onResize);
    window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
  },

  /**
   * Destruction function called when the tool cleans up.
   */
  destroy: function () {
    this._buttons.removeEventListener("click", this._onButtonClick);
    window.off(EVENTS.UI_INSPECTOR_RESIZE, this._onResize);
    window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
  },

  /**
   * Empties out the props view.
   */
  resetUI: function () {
    this._currentNode = null;
  },

  /**
   * On a new node selection, create the Automation panel for
   * that specific node.
   */
  build: Task.async(function* () {
    let node = this._currentNode;

    let props = yield node.getParams();
    let params = props.filter(({ flags }) => flags && flags.param);

    this._createParamButtons(params);

    this._selectedParamName = params[0] ? params[0].param : null;
    this.render();
  }),

  /**
   * Renders the graph for specified `paramName`. Called when
   * the parameter view is changed, or when new param data events
   * are fired for the currently specified param.
   */
  render: Task.async(function* () {
    let node = this._currentNode;
    let paramName = this._selectedParamName;
    // Escape if either node or parameter name does not exist.
    if (!node || !paramName) {
      this._setState("no-params");
      window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, null);
      return;
    }

    let { values, events } = yield node.getAutomationData(paramName);
    this._setState(events.length ? "show" : "no-events");
    yield this.graph.setDataWhenReady(values);
    window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, node.id);
  }),

  /**
   * Create the buttons for each AudioParam, that when clicked,
   * render the graph for that AudioParam.
   */
  _createParamButtons: function (params) {
    this._buttons.innerHTML = "";
    params.forEach((param, i) => {
      let button = document.createElement("toolbarbutton");
      button.setAttribute("class", "devtools-toolbarbutton automation-param-button");
      button.setAttribute("data-param", param.param);
      // Set label to the parameter name, should not be L10N'd
      button.setAttribute("label", param.param);

      // If first button, set to 'selected' for styling
      if (i === 0) {
        button.setAttribute("selected", true);
      }

      this._buttons.appendChild(button);
    });
  },

  /**
   * Internally sets the current audio node and rebuilds appropriate
   * views.
   */
  _setAudioNode: function (node) {
    this._currentNode = node;
    if (this._currentNode) {
      this.build();
    }
  },

  /**
   * Toggles the subviews to display messages whether or not
   * the audio node has no AudioParams, no automation events, or
   * shows the graph.
   */
  _setState: function (state) {
    let contentView = $("#automation-content");
    let emptyView = $("#automation-empty");

    let graphView = $("#automation-graph-container");
    let noEventsView = $("#automation-no-events");

    contentView.hidden = state === "no-params";
    emptyView.hidden = state !== "no-params";

    graphView.hidden = state !== "show";
    noEventsView.hidden = state !== "no-events";
  },

  /**
   * Event handlers
   */

  _onButtonClick: function (e) {
    Array.forEach($$(".automation-param-button"), $btn => $btn.removeAttribute("selected"));
    let paramName = e.target.getAttribute("data-param");
    e.target.setAttribute("selected", true);
    this._selectedParamName = paramName;
    this.render();
  },

  /**
   * Called when the inspector is resized.
   */
  _onResize: function () {
    this.graph.refresh();
  },

  /**
   * Called when the inspector view determines a node is selected.
   */
  _onNodeSet: function (_, id) {
    this._setAudioNode(id != null ? gAudioNodes.get(id) : null);
  }
};