Files
2025-11-30 08:35:03 +02:00

234 lines
6.2 KiB
JavaScript

import Collection from './collection';
import Super from './super';
import * as u from './utils';
///////////////////////
/// MANAGER ///
///////////////////////
function Manager (options) {
var self = this;
self.ids = {};
self.index = 0;
self.collections = [];
self.scroll = u.getScroll();
self.config(options);
self.prepareCollections();
// Listen for resize, to reposition every joysticks
var resizeHandler = function () {
var pos;
self.collections.forEach(function (collection) {
collection.forEach(function (nipple) {
pos = nipple.el.getBoundingClientRect();
nipple.position = {
x: self.scroll.x + pos.left,
y: self.scroll.y + pos.top
};
});
});
};
u.bindEvt(window, 'resize', function () {
u.throttle(resizeHandler);
});
// Listen for scrolls, so we have a global scroll value
// without having to request it all the time.
var scrollHandler = function () {
self.scroll = u.getScroll();
};
u.bindEvt(window, 'scroll', function () {
u.throttle(scrollHandler);
});
return self.collections;
}
Manager.prototype = new Super();
Manager.constructor = Manager;
Manager.prototype.prepareCollections = function () {
var self = this;
// Public API Preparation.
self.collections.create = self.create.bind(self);
// Listen to anything
self.collections.on = self.on.bind(self);
// Unbind general events
self.collections.off = self.off.bind(self);
// Destroy everything
self.collections.destroy = self.destroy.bind(self);
// Get any nipple
self.collections.get = function (id) {
var nipple;
// Use .every() to break the loop as soon as found.
self.collections.every(function (collection) {
nipple = collection.get(id);
return nipple ? false : true;
});
return nipple;
};
};
Manager.prototype.create = function (options) {
return this.createCollection(options);
};
// Collection Factory
Manager.prototype.createCollection = function (options) {
var self = this;
var collection = new Collection(self, options);
self.bindCollection(collection);
self.collections.push(collection);
return collection;
};
Manager.prototype.bindCollection = function (collection) {
var self = this;
var type;
// Bubble up identified events.
var handler = function (evt, data) {
// Identify the event type with the nipple's identifier.
type = evt.type + ' ' + data.id + ':' + evt.type;
self.trigger(type, data);
};
// When it gets destroyed we clean.
collection.on('destroyed', self.onDestroyed.bind(self));
// Other events that will get bubbled up.
collection.on('shown hidden rested dir plain', handler);
collection.on('dir:up dir:right dir:down dir:left', handler);
collection.on('plain:up plain:right plain:down plain:left', handler);
};
Manager.prototype.bindDocument = function () {
var self = this;
// Bind only if not already binded
if (!self.binded) {
self.bindEvt(document, 'move')
.bindEvt(document, 'end');
self.binded = true;
}
};
Manager.prototype.unbindDocument = function (force) {
var self = this;
// If there are no touch left
// unbind the document.
if (!Object.keys(self.ids).length || force === true) {
self.unbindEvt(document, 'move')
.unbindEvt(document, 'end');
self.binded = false;
}
};
Manager.prototype.getIdentifier = function (evt) {
var id;
// If no event, simple increment
if (!evt) {
id = this.index;
} else {
// Extract identifier from event object.
// Unavailable in mouse events so replaced by latest increment.
id = evt.identifier === undefined ? evt.pointerId : evt.identifier;
if (id === undefined) {
id = this.latest || 0;
}
}
if (this.ids[id] === undefined) {
this.ids[id] = this.index;
this.index += 1;
}
// Keep the latest id used in case we're using an unidentified mouseEvent
this.latest = id;
return this.ids[id];
};
Manager.prototype.removeIdentifier = function (identifier) {
var removed = {};
for (var id in this.ids) {
if (this.ids[id] === identifier) {
removed.id = id;
removed.identifier = this.ids[id];
delete this.ids[id];
break;
}
}
return removed;
};
Manager.prototype.onmove = function (evt) {
var self = this;
self.onAny('move', evt);
return false;
};
Manager.prototype.onend = function (evt) {
var self = this;
self.onAny('end', evt);
return false;
};
Manager.prototype.oncancel = function (evt) {
var self = this;
self.onAny('end', evt);
return false;
};
Manager.prototype.onAny = function (which, evt) {
var self = this;
var id;
var processFn = 'processOn' + which.charAt(0).toUpperCase() +
which.slice(1);
evt = u.prepareEvent(evt);
var processColl = function (e, id, coll) {
if (coll.ids.indexOf(id) >= 0) {
coll[processFn](e);
// Mark the event to avoid cleaning it later.
e._found_ = true;
}
};
var processEvt = function (e) {
id = self.getIdentifier(e);
u.map(self.collections, processColl.bind(null, e, id));
// If the event isn't handled by any collection,
// we need to clean its identifier.
if (!e._found_) {
self.removeIdentifier(id);
}
};
u.map(evt, processEvt);
return false;
};
// Cleanly destroy the manager
Manager.prototype.destroy = function () {
var self = this;
self.unbindDocument(true);
self.ids = {};
self.index = 0;
self.collections.forEach(function(collection) {
collection.destroy();
});
self.off();
};
// When a collection gets destroyed
// we clean behind.
Manager.prototype.onDestroyed = function (evt, coll) {
var self = this;
if (self.collections.indexOf(coll) < 0) {
return false;
}
self.collections.splice(self.collections.indexOf(coll), 1);
};
export default Manager;