233 lines
5.4 KiB
JavaScript
233 lines
5.4 KiB
JavaScript
|
|
import browser from "../utils/browser.js";
|
||
|
|
import clone from "../utils/clone.js";
|
||
|
|
import domObjects from "../utils/domObjects.js";
|
||
|
|
import extend from "../utils/extend.js";
|
||
|
|
import raf from "../utils/raf.js";
|
||
|
|
import * as win from "../utils/window.js";
|
||
|
|
import { Eventable } from "./Eventable.js";
|
||
|
|
import { InteractEvent } from "./InteractEvent.js";
|
||
|
|
import { Interactable as InteractableBase } from "./Interactable.js";
|
||
|
|
import { InteractableSet } from "./InteractableSet.js";
|
||
|
|
import { defaults } from "./defaultOptions.js";
|
||
|
|
import events from "./events.js";
|
||
|
|
import { createInteractStatic } from "./interactStatic.js";
|
||
|
|
import interactions from "./interactions.js";
|
||
|
|
export class Scope {
|
||
|
|
// main window
|
||
|
|
// main document
|
||
|
|
// main window
|
||
|
|
// all documents being listened to
|
||
|
|
constructor() {
|
||
|
|
this.id = `__interact_scope_${Math.floor(Math.random() * 100)}`;
|
||
|
|
this.isInitialized = false;
|
||
|
|
this.listenerMaps = [];
|
||
|
|
this.browser = browser;
|
||
|
|
this.defaults = clone(defaults);
|
||
|
|
this.Eventable = Eventable;
|
||
|
|
this.actions = {
|
||
|
|
map: {},
|
||
|
|
phases: {
|
||
|
|
start: true,
|
||
|
|
move: true,
|
||
|
|
end: true
|
||
|
|
},
|
||
|
|
methodDict: {},
|
||
|
|
phaselessTypes: {}
|
||
|
|
};
|
||
|
|
this.interactStatic = createInteractStatic(this);
|
||
|
|
this.InteractEvent = InteractEvent;
|
||
|
|
this.Interactable = void 0;
|
||
|
|
this.interactables = new InteractableSet(this);
|
||
|
|
this._win = void 0;
|
||
|
|
this.document = void 0;
|
||
|
|
this.window = void 0;
|
||
|
|
this.documents = [];
|
||
|
|
this._plugins = {
|
||
|
|
list: [],
|
||
|
|
map: {}
|
||
|
|
};
|
||
|
|
|
||
|
|
this.onWindowUnload = event => this.removeDocument(event.target);
|
||
|
|
|
||
|
|
const scope = this;
|
||
|
|
this.Interactable = class extends InteractableBase {
|
||
|
|
get _defaults() {
|
||
|
|
return scope.defaults;
|
||
|
|
}
|
||
|
|
|
||
|
|
set(options) {
|
||
|
|
super.set(options);
|
||
|
|
scope.fire('interactable:set', {
|
||
|
|
options,
|
||
|
|
interactable: this
|
||
|
|
});
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
|
||
|
|
unset() {
|
||
|
|
super.unset();
|
||
|
|
scope.interactables.list.splice(scope.interactables.list.indexOf(this), 1);
|
||
|
|
scope.fire('interactable:unset', {
|
||
|
|
interactable: this
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
addListeners(map, id) {
|
||
|
|
this.listenerMaps.push({
|
||
|
|
id,
|
||
|
|
map
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
fire(name, arg) {
|
||
|
|
for (const {
|
||
|
|
map: {
|
||
|
|
[name]: listener
|
||
|
|
}
|
||
|
|
} of this.listenerMaps) {
|
||
|
|
if (!!listener && listener(arg, this, name) === false) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
init(window) {
|
||
|
|
return this.isInitialized ? this : initScope(this, window);
|
||
|
|
}
|
||
|
|
|
||
|
|
pluginIsInstalled(plugin) {
|
||
|
|
return this._plugins.map[plugin.id] || this._plugins.list.indexOf(plugin) !== -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
usePlugin(plugin, options) {
|
||
|
|
if (!this.isInitialized) {
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.pluginIsInstalled(plugin)) {
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (plugin.id) {
|
||
|
|
this._plugins.map[plugin.id] = plugin;
|
||
|
|
}
|
||
|
|
|
||
|
|
this._plugins.list.push(plugin);
|
||
|
|
|
||
|
|
if (plugin.install) {
|
||
|
|
plugin.install(this, options);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (plugin.listeners && plugin.before) {
|
||
|
|
let index = 0;
|
||
|
|
const len = this.listenerMaps.length;
|
||
|
|
const before = plugin.before.reduce((acc, id) => {
|
||
|
|
acc[id] = true;
|
||
|
|
acc[pluginIdRoot(id)] = true;
|
||
|
|
return acc;
|
||
|
|
}, {});
|
||
|
|
|
||
|
|
for (; index < len; index++) {
|
||
|
|
const otherId = this.listenerMaps[index].id;
|
||
|
|
|
||
|
|
if (before[otherId] || before[pluginIdRoot(otherId)]) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
this.listenerMaps.splice(index, 0, {
|
||
|
|
id: plugin.id,
|
||
|
|
map: plugin.listeners
|
||
|
|
});
|
||
|
|
} else if (plugin.listeners) {
|
||
|
|
this.listenerMaps.push({
|
||
|
|
id: plugin.id,
|
||
|
|
map: plugin.listeners
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
|
||
|
|
addDocument(doc, options) {
|
||
|
|
// do nothing if document is already known
|
||
|
|
if (this.getDocIndex(doc) !== -1) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
const window = win.getWindow(doc);
|
||
|
|
options = options ? extend({}, options) : {};
|
||
|
|
this.documents.push({
|
||
|
|
doc,
|
||
|
|
options
|
||
|
|
});
|
||
|
|
this.events.documents.push(doc); // don't add an unload event for the main document
|
||
|
|
// so that the page may be cached in browser history
|
||
|
|
|
||
|
|
if (doc !== this.document) {
|
||
|
|
this.events.add(window, 'unload', this.onWindowUnload);
|
||
|
|
}
|
||
|
|
|
||
|
|
this.fire('scope:add-document', {
|
||
|
|
doc,
|
||
|
|
window,
|
||
|
|
scope: this,
|
||
|
|
options
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
removeDocument(doc) {
|
||
|
|
const index = this.getDocIndex(doc);
|
||
|
|
const window = win.getWindow(doc);
|
||
|
|
const options = this.documents[index].options;
|
||
|
|
this.events.remove(window, 'unload', this.onWindowUnload);
|
||
|
|
this.documents.splice(index, 1);
|
||
|
|
this.events.documents.splice(index, 1);
|
||
|
|
this.fire('scope:remove-document', {
|
||
|
|
doc,
|
||
|
|
window,
|
||
|
|
scope: this,
|
||
|
|
options
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
getDocIndex(doc) {
|
||
|
|
for (let i = 0; i < this.documents.length; i++) {
|
||
|
|
if (this.documents[i].doc === doc) {
|
||
|
|
return i;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
getDocOptions(doc) {
|
||
|
|
const docIndex = this.getDocIndex(doc);
|
||
|
|
return docIndex === -1 ? null : this.documents[docIndex].options;
|
||
|
|
}
|
||
|
|
|
||
|
|
now() {
|
||
|
|
return (this.window.Date || Date).now();
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
export function initScope(scope, window) {
|
||
|
|
scope.isInitialized = true;
|
||
|
|
win.init(window);
|
||
|
|
domObjects.init(window);
|
||
|
|
browser.init(window);
|
||
|
|
raf.init(window);
|
||
|
|
scope.window = window;
|
||
|
|
scope.document = window.document;
|
||
|
|
scope.usePlugin(interactions);
|
||
|
|
scope.usePlugin(events);
|
||
|
|
return scope;
|
||
|
|
}
|
||
|
|
|
||
|
|
function pluginIdRoot(id) {
|
||
|
|
return id && id.replace(/\/.*$/, '');
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=scope.js.map
|