Files
openlayers/float-no-zero/closure-library/closure/goog/labs/classdef/classdef.js
2014-03-07 10:55:12 +01:00

163 lines
5.3 KiB
JavaScript

// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Restricted class definitions for Closure.
*
* @author johnlenz@google.com (John Lenz)
*/
goog.provide('goog.labs.classdef');
/** @typedef {
{constructor:!Function}|
{constructor:!Function, statics:(Object|function(Function):void)}} */
goog.labs.classdef.ClassDescriptor;
/**
* Creates a restricted form of a Closure "class":
* - from the compiler's perspective, the instance returned from the
* constructor is sealed (no new properties may be added). This enables
* better type checking.
* - the compiler will rewrite this definition to a form that is optimal
* for type checking and optimization (initially this will be a more
* traditional form).
*
* @param {Function} superClass The superclass or null.
* @param {goog.labs.classdef.ClassDescriptor} def
* An object literal describing the
* the class. It may have the following properties:
* "constructor": the constructor function
* "statics": an object literal containing methods to add to the constructor
* as "static" methods or a function that will recieve the constructor
* function as its only parameter to which static properties can
* be added.
* all other properties are added to the prototype.
* @return {!Function} The class constructor.
*/
goog.labs.classdef.defineClass = function(superClass, def) {
// TODO(johnlenz): consider making the superClass an optional parameter.
var constructor = def.constructor;
var statics = def.statics;
// Wrap the constructor prior to setting up the prototype and static methods.
if (!constructor || constructor == Object.prototype.constructor) {
throw Error('constructor property is required.');
}
var cls = goog.labs.classdef.createSealingConstructor_(constructor);
if (superClass) {
goog.inherits(cls, superClass);
}
// Remove all the properties that should not be copied to the prototype.
delete def.constructor;
delete def.statics;
goog.labs.classdef.applyProperties_(cls.prototype, def);
if (statics != null) {
if (statics instanceof Function) {
statics(cls);
} else {
goog.labs.classdef.applyProperties_(cls, statics);
}
}
return cls;
};
/**
* @define {boolean} Whether the instances returned by
* goog.labs.classdef.defineClass should be sealed when possible.
*/
goog.define('goog.labs.classdef.SEAL_CLASS_INSTANCES', goog.DEBUG);
/**
* If goog.labs.classdef.SEAL_CLASS_INSTANCES is enabled and Object.seal is
* defined, this function will wrap the constructor in a function that seals the
* results of the provided constructor function.
*
* @param {!Function} ctr The constructor whose results maybe be sealed.
* @return {!Function} The replacement constructor.
* @private
*/
goog.labs.classdef.createSealingConstructor_ = function(ctr) {
if (goog.labs.classdef.SEAL_CLASS_INSTANCES &&
Object.seal instanceof Function) {
/** @this {*} */
var wrappedCtr = function() {
// Don't seal an instance of a subclass when it calls the constructor of
// its super class as there is most likely still setup to do.
var instance = ctr.apply(this, arguments) || this;
if (this.constructor === wrappedCtr) {
Object.seal(instance);
}
return instance;
};
return wrappedCtr;
}
return ctr;
};
// TODO(johnlenz): share these values with the goog.object
/**
* The names of the fields that are defined on Object.prototype.
* @type {!Array.<string>}
* @private
* @const
*/
goog.labs.classdef.OBJECT_PROTOTYPE_FIELDS_ = [
'constructor',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toLocaleString',
'toString',
'valueOf'
];
// TODO(johnlenz): share this function with the goog.object
/**
* @param {!Object} target The object to add properties to.
* @param {!Object} source The object to copy properites from.
* @private
*/
goog.labs.classdef.applyProperties_ = function(target, source) {
// TODO(johnlenz): update this to support ES5 getters/setters
var key;
for (key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
// For IE the for-in-loop does not contain any properties that are not
// enumerable on the prototype object (for example isPrototypeOf from
// Object.prototype) and it will also not include 'replace' on objects that
// extend String and change 'replace' (not that it is common for anyone to
// extend anything except Object).
for (var i = 0; i < goog.labs.classdef.OBJECT_PROTOTYPE_FIELDS_.length; i++) {
key = goog.labs.classdef.OBJECT_PROTOTYPE_FIELDS_[i];
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
};