mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-22 08:27:31 +09:00
Move Add-on SDK source to toolkit/jetpack
This commit is contained in:
parent
0a03067ee3
commit
23b67db438
993 changed files with 484 additions and 100663 deletions
|
|
@ -1,151 +0,0 @@
|
|||
/* 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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "experimental",
|
||||
"engines": {
|
||||
"Firefox": "> 28"
|
||||
}
|
||||
};
|
||||
|
||||
const { Class } = require("../../core/heritage");
|
||||
const { EventTarget } = require("../../event/target");
|
||||
const { off, setListeners, emit } = require("../../event/core");
|
||||
const { Reactor, foldp, merges, send } = require("../../event/utils");
|
||||
const { Disposable } = require("../../core/disposable");
|
||||
const { InputPort } = require("../../input/system");
|
||||
const { OutputPort } = require("../../output/system");
|
||||
const { identify } = require("../id");
|
||||
const { pairs, object, map, each } = require("../../util/sequence");
|
||||
const { patch, diff } = require("diffpatcher/index");
|
||||
const { contract } = require("../../util/contract");
|
||||
const { id: addonID } = require("../../self");
|
||||
|
||||
// Input state is accumulated from the input received form the toolbar
|
||||
// view code & local output. Merging local output reflects local state
|
||||
// changes without complete roundloop.
|
||||
const input = foldp(patch, {}, new InputPort({ id: "toolbar-changed" }));
|
||||
const output = new OutputPort({ id: "toolbar-change" });
|
||||
|
||||
// Takes toolbar title and normalizes is to an
|
||||
// identifier, also prefixes with add-on id.
|
||||
const titleToId = title =>
|
||||
("toolbar-" + addonID + "-" + title).
|
||||
toLowerCase().
|
||||
replace(/\s/g, "-").
|
||||
replace(/[^A-Za-z0-9_\-]/g, "");
|
||||
|
||||
const validate = contract({
|
||||
title: {
|
||||
is: ["string"],
|
||||
ok: x => x.length > 0,
|
||||
msg: "The `option.title` string must be provided"
|
||||
},
|
||||
items: {
|
||||
is:["undefined", "object", "array"],
|
||||
msg: "The `options.items` must be iterable sequence of items"
|
||||
},
|
||||
hidden: {
|
||||
is: ["boolean", "undefined"],
|
||||
msg: "The `options.hidden` must be boolean"
|
||||
}
|
||||
});
|
||||
|
||||
// Toolbars is a mapping between `toolbar.id` & `toolbar` instances,
|
||||
// which is used to find intstance for dispatching events.
|
||||
var toolbars = new Map();
|
||||
|
||||
const Toolbar = Class({
|
||||
extends: EventTarget,
|
||||
implements: [Disposable],
|
||||
initialize: function(params={}) {
|
||||
const options = validate(params);
|
||||
const id = titleToId(options.title);
|
||||
|
||||
if (toolbars.has(id))
|
||||
throw Error("Toolbar with this id already exists: " + id);
|
||||
|
||||
// Set of the items in the toolbar isn't mutable, as a matter of fact
|
||||
// it just defines desired set of items, actual set is under users
|
||||
// control. Conver test to an array and freeze to make sure users won't
|
||||
// try mess with it.
|
||||
const items = Object.freeze(options.items ? [...options.items] : []);
|
||||
|
||||
const initial = {
|
||||
id: id,
|
||||
title: options.title,
|
||||
// By default toolbars are visible when add-on is installed, unless
|
||||
// add-on authors decides it should be hidden. From that point on
|
||||
// user is in control.
|
||||
collapsed: !!options.hidden,
|
||||
// In terms of state only identifiers of items matter.
|
||||
items: items.map(identify)
|
||||
};
|
||||
|
||||
this.id = id;
|
||||
this.items = items;
|
||||
|
||||
toolbars.set(id, this);
|
||||
setListeners(this, params);
|
||||
|
||||
// Send initial state to the host so it can reflect it
|
||||
// into a user interface.
|
||||
send(output, object([id, initial]));
|
||||
},
|
||||
|
||||
get title() {
|
||||
const state = reactor.value[this.id];
|
||||
return state && state.title;
|
||||
},
|
||||
get hidden() {
|
||||
const state = reactor.value[this.id];
|
||||
return state && state.collapsed;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
send(output, object([this.id, null]));
|
||||
},
|
||||
// `JSON.stringify` serializes objects based of the return
|
||||
// value of this method. For convinienc we provide this method
|
||||
// to serialize actual state data. Note: items will also be
|
||||
// serialized so they should probably implement `toJSON`.
|
||||
toJSON: function() {
|
||||
return {
|
||||
id: this.id,
|
||||
title: this.title,
|
||||
hidden: this.hidden,
|
||||
items: this.items
|
||||
};
|
||||
}
|
||||
});
|
||||
exports.Toolbar = Toolbar;
|
||||
identify.define(Toolbar, toolbar => toolbar.id);
|
||||
|
||||
const dispose = toolbar => {
|
||||
toolbars.delete(toolbar.id);
|
||||
emit(toolbar, "detach");
|
||||
off(toolbar);
|
||||
};
|
||||
|
||||
const reactor = new Reactor({
|
||||
onStep: (present, past) => {
|
||||
const delta = diff(past, present);
|
||||
|
||||
each(([id, update]) => {
|
||||
const toolbar = toolbars.get(id);
|
||||
|
||||
// Remove
|
||||
if (!update)
|
||||
dispose(toolbar);
|
||||
// Add
|
||||
else if (!past[id])
|
||||
emit(toolbar, "attach");
|
||||
// Update
|
||||
else
|
||||
emit(toolbar, update.collapsed ? "hide" : "show", toolbar);
|
||||
}, pairs(delta));
|
||||
}
|
||||
});
|
||||
reactor.run(input);
|
||||
Loading…
Add table
Add a link
Reference in a new issue