Skip to content

Instantly share code, notes, and snippets.

@mcgwiz
Last active November 3, 2015 03:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mcgwiz/fc55e4f62655453634fa to your computer and use it in GitHub Desktop.
Save mcgwiz/fc55e4f62655453634fa to your computer and use it in GitHub Desktop.
Pattern for JavaScript prototypical inheritance with private and protected state
var Modal = (function() {
function createType(subTypeBox) {
var subTypeBox = subTypeBox || { };
var internal = { }; // "protected" state bag
var stack = [ ]; // "private" field
// Name the constructor function strictly for debug purposes, in a way that doesn't spill throughout this scope.
var Type = (function() { return function Modal(id) { this.id = id; }; })();
Type.prototype = {
id: undefined,
getEl: function() { return internal[this.id].el; },
getPromise: function() { return internal[this.id].deferred.promise(); },
isOpen: function() { return !!internal[this.id]; },
isTop: function() {
var top = stack[stack.length - 1];
return top === this.id;
},
close: function(data) {
if (!this.isTop()) { return false; }
$(this.getEl()).parent().remove();
var state = internal[this.id];
delete internal[this.id];
stack.pop();
state.deferred.resolveWith(this, [data]);
return true;
},
constructor: Type
};
Type.create = function() {
var deferred = $.Deferred(),
id = Date.now(), // hacky
modal = new (subTypeBox.Type || Type)(id);
var $el = $('<div class="modal"><div class="modal-content">').
appendTo('body').
click(function(e) {
if (e.target === $(modal.getEl()).parent()[0]) { modal.close({ canceled: true }); }
}).
find('.modal-content');
internal[id] = { el: $el[0], deferred: deferred, api: modal };
stack.push(id);
return modal;
};
Type.find = function(el) {
var id, state, $el = $(el);
for (id in internal) {
if (!internal.hasOwnProperty(id)) { continue; }
state = internal[id];
if ($el.closest(state.el)) { return state.api; }
}
};
return {
Type: Type,
friend: internal
};
};
var SealedType = createType();
SealedType.createType = createType;
return SealedType;
})();
var SuperModal = (function() {
var box = { };
var typeData = Modal.createType(box);
var UnsealedModal = typeData.Type;
var friend = typeData.friend;
var Type = (function() { return function SuperModal() { UnsealedModal.prototype.constructor.apply(this, arguments); }; })();
box.Type = Type;
Type.prototype = Object.create(UnsealedModal.prototype);
Type.prototype.isClosed = function() { return !friend[this.id]; };
Type.prototype.constructor = Type;
Type.create = UnsealedModal.create;
Type.find = UnsealedModal.find;
return Type;
})();
@mcgwiz
Copy link
Author

mcgwiz commented Nov 3, 2015

This could be improved to reuse the same objects for Type.prototype and internal across calls to createType().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment