michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: 'use strict'; michael@0: michael@0: module.metadata = { michael@0: 'stability': 'experimental', michael@0: 'engines': { michael@0: 'Firefox': '> 28' michael@0: } michael@0: }; michael@0: michael@0: const { Class } = require('../../core/heritage'); michael@0: const { merge } = require('../../util/object'); michael@0: const { Disposable } = require('../../core/disposable'); michael@0: const { on, off, emit, setListeners } = require('../../event/core'); michael@0: const { EventTarget } = require('../../event/target'); michael@0: const { getNodeView } = require('../../view/core'); michael@0: michael@0: const view = require('./view'); michael@0: const { toggleButtonContract, toggleStateContract } = require('./contract'); michael@0: const { properties, render, state, register, unregister, michael@0: setStateFor, getStateFor, getDerivedStateFor } = require('../state'); michael@0: const { events: stateEvents } = require('../state/events'); michael@0: const { events: viewEvents } = require('./view/events'); michael@0: const events = require('../../event/utils'); michael@0: michael@0: const { getActiveTab } = require('../../tabs/utils'); michael@0: michael@0: const { id: addonID } = require('../../self'); michael@0: const { identify } = require('../id'); michael@0: michael@0: const buttons = new Map(); michael@0: michael@0: const toWidgetId = id => michael@0: ('toggle-button--' + addonID.toLowerCase()+ '-' + id). michael@0: replace(/[^a-z0-9_-]/g, ''); michael@0: michael@0: const ToggleButton = Class({ michael@0: extends: EventTarget, michael@0: implements: [ michael@0: properties(toggleStateContract), michael@0: state(toggleStateContract), michael@0: Disposable michael@0: ], michael@0: setup: function setup(options) { michael@0: let state = merge({ michael@0: disabled: false, michael@0: checked: false michael@0: }, toggleButtonContract(options)); michael@0: michael@0: let id = toWidgetId(options.id); michael@0: michael@0: register(this, state); michael@0: michael@0: // Setup listeners. michael@0: setListeners(this, options); michael@0: michael@0: buttons.set(id, this); michael@0: michael@0: view.create(merge({ type: 'checkbox' }, state, { id: id })); michael@0: }, michael@0: michael@0: dispose: function dispose() { michael@0: let id = toWidgetId(this.id); michael@0: buttons.delete(id); michael@0: michael@0: off(this); michael@0: michael@0: view.dispose(id); michael@0: michael@0: unregister(this); michael@0: }, michael@0: michael@0: get id() this.state().id, michael@0: michael@0: click: function click() view.click(toWidgetId(this.id)) michael@0: }); michael@0: exports.ToggleButton = ToggleButton; michael@0: michael@0: identify.define(ToggleButton, ({id}) => toWidgetId(id)); michael@0: michael@0: getNodeView.define(ToggleButton, button => michael@0: view.nodeFor(toWidgetId(button.id)) michael@0: ); michael@0: michael@0: let toggleButtonStateEvents = events.filter(stateEvents, michael@0: e => e.target instanceof ToggleButton); michael@0: michael@0: let toggleButtonViewEvents = events.filter(viewEvents, michael@0: e => buttons.has(e.target)); michael@0: michael@0: let clickEvents = events.filter(toggleButtonViewEvents, e => e.type === 'click'); michael@0: let updateEvents = events.filter(toggleButtonViewEvents, e => e.type === 'update'); michael@0: michael@0: on(toggleButtonStateEvents, 'data', ({target, window, state}) => { michael@0: let id = toWidgetId(target.id); michael@0: michael@0: view.setIcon(id, window, state.icon); michael@0: view.setLabel(id, window, state.label); michael@0: view.setDisabled(id, window, state.disabled); michael@0: view.setChecked(id, window, state.checked); michael@0: }); michael@0: michael@0: on(clickEvents, 'data', ({target: id, window, checked }) => { michael@0: let button = buttons.get(id); michael@0: let windowState = getStateFor(button, window); michael@0: michael@0: let newWindowState = merge({}, windowState, { checked: checked }); michael@0: michael@0: setStateFor(button, window, newWindowState); michael@0: michael@0: let state = getDerivedStateFor(button, getActiveTab(window)); michael@0: michael@0: emit(button, 'click', state); michael@0: michael@0: emit(button, 'change', state); michael@0: }); michael@0: michael@0: on(updateEvents, 'data', ({target: id, window}) => { michael@0: render(buttons.get(id), window); michael@0: });