aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.12/lib/sdk/core/heritage.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.12/lib/sdk/core/heritage.js')
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/core/heritage.js146
1 files changed, 146 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.12/lib/sdk/core/heritage.js b/tools/addon-sdk-1.12/lib/sdk/core/heritage.js
new file mode 100644
index 0000000..1c62e6c
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/core/heritage.js
@@ -0,0 +1,146 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/* 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": "unstable"
+};
+
+var getPrototypeOf = Object.getPrototypeOf;
+var getNames = Object.getOwnPropertyNames;
+var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+var create = Object.create;
+var freeze = Object.freeze;
+var unbind = Function.call.bind(Function.bind, Function.call);
+
+// This shortcut makes sure that we do perform desired operations, even if
+// associated methods have being overridden on the used object.
+var owns = unbind(Object.prototype.hasOwnProperty);
+var apply = unbind(Function.prototype.apply);
+var slice = Array.slice || unbind(Array.prototype.slice);
+var reduce = Array.reduce || unbind(Array.prototype.reduce);
+var map = Array.map || unbind(Array.prototype.map);
+var concat = Array.concat || unbind(Array.prototype.concat);
+
+// Utility function to get own properties descriptor map.
+function getOwnPropertyDescriptors(object) {
+ return reduce(getNames(object), function(descriptor, name) {
+ descriptor[name] = getOwnPropertyDescriptor(object, name);
+ return descriptor;
+ }, {});
+}
+
+/**
+ * Takes `source` object as an argument and returns identical object
+ * with the difference that all own properties will be non-enumerable
+ */
+function obscure(source) {
+ var descriptor = reduce(getNames(source), function(descriptor, name) {
+ var property = getOwnPropertyDescriptor(source, name);
+ property.enumerable = false;
+ descriptor[name] = property;
+ return descriptor;
+ }, {});
+ return create(getPrototypeOf(source), descriptor);
+}
+exports.obscure = obscure;
+
+/**
+ * Takes arbitrary number of source objects and returns fresh one, that
+ * inherits from the same prototype as a first argument and implements all
+ * own properties of all argument objects. If two or more argument objects
+ * have own properties with the same name, the property is overridden, with
+ * precedence from right to left, implying, that properties of the object on
+ * the left are overridden by a same named property of the object on the right.
+ */
+var mix = function(source) {
+ var descriptor = reduce(slice(arguments), function(descriptor, source) {
+ return reduce(getNames(source), function(descriptor, name) {
+ descriptor[name] = getOwnPropertyDescriptor(source, name);
+ return descriptor;
+ }, descriptor);
+ }, {});
+
+ return create(getPrototypeOf(source), descriptor);
+};
+exports.mix = mix;
+
+/**
+ * Returns a frozen object with that inherits from the given `prototype` and
+ * implements all own properties of the given `properties` object.
+ */
+function extend(prototype, properties) {
+ return freeze(create(prototype, getOwnPropertyDescriptors(properties)));
+}
+exports.extend = extend;
+
+/**
+ * Returns a constructor function with a proper `prototype` setup. Returned
+ * constructor's `prototype` inherits from a given `options.extends` or
+ * `Class.prototype` if omitted and implements all the properties of the
+ * given `option`. If `options.implemens` array is passed, it's elements
+ * will be mixed into prototype as well. Also, `options.extends` can be
+ * a function or a prototype. If function than it's prototype is used as
+ * an ancestor of the prototype, if it's an object that it's used directly.
+ * Also `options.implements` may contain functions or objects, in case of
+ * functions their prototypes are used for mixing.
+ */
+var Class = new function() {
+ function prototypeOf(input) {
+ return typeof(input) === 'function' ? input.prototype : input;
+ }
+ var none = freeze([]);
+
+ return function Class(options) {
+ // Create descriptor with normalized `options.extends` and
+ // `options.implements`.
+ var descriptor = {
+ // Normalize extends property of `options.extends` to a prototype object
+ // in case it's constructor. If property is missing that fallback to
+ // `Type.prototype`.
+ extends: owns(options, 'extends') ?
+ prototypeOf(options.extends) : Class.prototype,
+ // Normalize `options.implements` to make sure that it's array of
+ // prototype objects instead of constructor functions.
+ implements: owns(options, 'implements') ?
+ freeze(map(options.implements, prototypeOf)) : none
+ };
+
+ // Create array of property descriptors who's properties will be defined
+ // on the resulting prototype. Note: Using reflection `concat` instead of
+ // method as it may be overridden.
+ var descriptors = concat(descriptor.implements, options, descriptor);
+ // Create `prototype` that inherits from given ancestor passed as
+ // `options.extends`, falling back to `Type.prototype`, implementing all
+ // properties of given `options.implements` and `options` itself.
+ var prototype = extend(descriptor.extends, mix.apply(mix, descriptors));
+
+ // Note: we use reflection `apply` in the constructor instead of method
+ // call since later may be overridden.
+ function constructor() {
+ return apply(prototype.constructor, create(prototype), arguments);
+ }
+ constructor.prototype = prototype;
+ return freeze(constructor);
+ };
+}
+Class.prototype = extend(null, obscure({
+ constructor: function constructor() {
+ this.initialize.apply(this, arguments);
+ return this;
+ },
+ initialize: function initialize() {
+ // Do your initialization logic here
+ },
+ // Copy useful properties from `Object.prototype`.
+ toString: Object.prototype.toString,
+ toLocaleString: Object.prototype.toLocaleString,
+ toSource: Object.prototype.toSource,
+ valueOf: Object.prototype.valueOf,
+ isPrototypeOf: Object.prototype.isPrototypeOf
+}));
+exports.Class = freeze(Class);