action.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. 'use strict';
  5. module.metadata = {
  6. 'stability': 'experimental',
  7. 'engines': {
  8. 'Firefox': '> 28'
  9. }
  10. };
  11. // Because Firefox Holly, we still need to check if `CustomizableUI` is
  12. // available. Once Australis will officially land, we can safely remove it.
  13. // See Bug 959142
  14. try {
  15. require('chrome').Cu.import('resource:///modules/CustomizableUI.jsm', {});
  16. }
  17. catch (e) {
  18. throw Error('Unsupported Application: The module ' + module.id +
  19. ' does not support this application.');
  20. }
  21. const { Class } = require('../../core/heritage');
  22. const { merge } = require('../../util/object');
  23. const { Disposable } = require('../../core/disposable');
  24. const { on, off, emit, setListeners } = require('../../event/core');
  25. const { EventTarget } = require('../../event/target');
  26. const view = require('./view');
  27. const { buttonContract, stateContract } = require('./contract');
  28. const { properties, render, state, register, unregister,
  29. getDerivedStateFor } = require('../state');
  30. const { events: stateEvents } = require('../state/events');
  31. const { events: viewEvents } = require('./view/events');
  32. const events = require('../../event/utils');
  33. const { getActiveTab } = require('../../tabs/utils');
  34. const buttons = new Map();
  35. const ActionButton = Class({
  36. extends: EventTarget,
  37. implements: [
  38. properties(stateContract),
  39. state(stateContract),
  40. Disposable
  41. ],
  42. setup: function setup(options) {
  43. let state = merge({
  44. disabled: false
  45. }, buttonContract(options));
  46. register(this, state);
  47. // Setup listeners.
  48. setListeners(this, options);
  49. buttons.set(options.id, this);
  50. view.create(state);
  51. },
  52. dispose: function dispose() {
  53. buttons.delete(this.id);
  54. off(this);
  55. view.dispose(this.id);
  56. unregister(this);
  57. },
  58. get id() this.state().id,
  59. click: function click() { view.click(this.id) }
  60. });
  61. exports.ActionButton = ActionButton;
  62. let actionButtonStateEvents = events.filter(stateEvents,
  63. e => e.target instanceof ActionButton);
  64. let actionButtonViewEvents = events.filter(viewEvents,
  65. e => buttons.has(e.target));
  66. let clickEvents = events.filter(actionButtonViewEvents, e => e.type === 'click');
  67. let updateEvents = events.filter(actionButtonViewEvents, e => e.type === 'update');
  68. on(clickEvents, 'data', ({target: id, window}) => {
  69. let button = buttons.get(id);
  70. let state = getDerivedStateFor(button, getActiveTab(window));
  71. emit(button, 'click', state);
  72. });
  73. on(updateEvents, 'data', ({target: id, window}) => {
  74. render(buttons.get(id), window);
  75. });
  76. on(actionButtonStateEvents, 'data', ({target, window, state}) => {
  77. let { id } = target;
  78. view.setIcon(id, window, state.icon);
  79. view.setLabel(id, window, state.label);
  80. view.setDisabled(id, window, state.disabled);
  81. });