Last active
November 3, 2015 03:03
-
-
Save mcgwiz/fc55e4f62655453634fa to your computer and use it in GitHub Desktop.
Pattern for JavaScript prototypical inheritance with private and protected state
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This could be improved to reuse the same objects for
Type.prototype
andinternal
across calls tocreateType()
.