|
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 |
|
6 module.metadata = { |
|
7 'stability': 'experimental', |
|
8 'engines': { |
|
9 'Firefox': '> 28' |
|
10 } |
|
11 }; |
|
12 |
|
13 const { Class } = require('../../core/heritage'); |
|
14 const { merge } = require('../../util/object'); |
|
15 const { Disposable } = require('../../core/disposable'); |
|
16 const { on, off, emit, setListeners } = require('../../event/core'); |
|
17 const { EventTarget } = require('../../event/target'); |
|
18 const { getNodeView } = require('../../view/core'); |
|
19 |
|
20 const view = require('./view'); |
|
21 const { buttonContract, stateContract } = require('./contract'); |
|
22 const { properties, render, state, register, unregister, |
|
23 getDerivedStateFor } = require('../state'); |
|
24 const { events: stateEvents } = require('../state/events'); |
|
25 const { events: viewEvents } = require('./view/events'); |
|
26 const events = require('../../event/utils'); |
|
27 |
|
28 const { getActiveTab } = require('../../tabs/utils'); |
|
29 |
|
30 const { id: addonID } = require('../../self'); |
|
31 const { identify } = require('../id'); |
|
32 |
|
33 const buttons = new Map(); |
|
34 |
|
35 const toWidgetId = id => |
|
36 ('action-button--' + addonID.toLowerCase()+ '-' + id). |
|
37 replace(/[^a-z0-9_-]/g, ''); |
|
38 |
|
39 const ActionButton = Class({ |
|
40 extends: EventTarget, |
|
41 implements: [ |
|
42 properties(stateContract), |
|
43 state(stateContract), |
|
44 Disposable |
|
45 ], |
|
46 setup: function setup(options) { |
|
47 let state = merge({ |
|
48 disabled: false |
|
49 }, buttonContract(options)); |
|
50 |
|
51 let id = toWidgetId(options.id); |
|
52 |
|
53 register(this, state); |
|
54 |
|
55 // Setup listeners. |
|
56 setListeners(this, options); |
|
57 |
|
58 buttons.set(id, this); |
|
59 |
|
60 view.create(merge({}, state, { id: id })); |
|
61 }, |
|
62 |
|
63 dispose: function dispose() { |
|
64 let id = toWidgetId(this.id); |
|
65 buttons.delete(id); |
|
66 |
|
67 off(this); |
|
68 |
|
69 view.dispose(id); |
|
70 |
|
71 unregister(this); |
|
72 }, |
|
73 |
|
74 get id() this.state().id, |
|
75 |
|
76 click: function click() { view.click(toWidgetId(this.id)) } |
|
77 }); |
|
78 exports.ActionButton = ActionButton; |
|
79 |
|
80 identify.define(ActionButton, ({id}) => toWidgetId(id)); |
|
81 |
|
82 getNodeView.define(ActionButton, button => |
|
83 view.nodeFor(toWidgetId(button.id)) |
|
84 ); |
|
85 |
|
86 let actionButtonStateEvents = events.filter(stateEvents, |
|
87 e => e.target instanceof ActionButton); |
|
88 |
|
89 let actionButtonViewEvents = events.filter(viewEvents, |
|
90 e => buttons.has(e.target)); |
|
91 |
|
92 let clickEvents = events.filter(actionButtonViewEvents, e => e.type === 'click'); |
|
93 let updateEvents = events.filter(actionButtonViewEvents, e => e.type === 'update'); |
|
94 |
|
95 on(clickEvents, 'data', ({target: id, window}) => { |
|
96 let button = buttons.get(id); |
|
97 let state = getDerivedStateFor(button, getActiveTab(window)); |
|
98 |
|
99 emit(button, 'click', state); |
|
100 }); |
|
101 |
|
102 on(updateEvents, 'data', ({target: id, window}) => { |
|
103 render(buttons.get(id), window); |
|
104 }); |
|
105 |
|
106 on(actionButtonStateEvents, 'data', ({target, window, state}) => { |
|
107 let id = toWidgetId(target.id); |
|
108 view.setIcon(id, window, state.icon); |
|
109 view.setLabel(id, window, state.label); |
|
110 view.setDisabled(id, window, state.disabled); |
|
111 }); |