/*global console, Proxy */
var Montage = require("../../core").Montage,
ValuesDeserializer = require("./values-deserializer").ValuesDeserializer,
SelfDeserializer = require("./self-deserializer").SelfDeserializer,
UnitDeserializer = require("./unit-deserializer").UnitDeserializer,
ModuleReference = require("../../module-reference").ModuleReference,
Alias = require("../alias").Alias, Bindings = require("../bindings"),
Promise = require("../../promise").Promise,
deprecate = require("../../deprecate"),
camelCaseConverter = require('../../converter/camel-case-converter').singleton,
kebabCaseConverter = require('../../converter/kebab-case-converter').singleton,
ONE_ASSIGNMENT = "=",
ONE_WAY = "<-",
TWO_WAY = "<->";
require("../../shim/string");
var PROXY_ELEMENT_MAP = new WeakMap();
var DATA_ATTRIBUTES_MAP = new Map();
var ModuleLoader = Montage.specialize({
_require: {
value: null
},
_objectRequires: {
value: null
},
init: {
value: function (_require, objectRequires) {
if (typeof _require !== "function") {
throw new Error("Function 'require' missing.");
}
if (typeof _require.location !== "string") {
throw new Error("Function 'require' location is missing");
}
if (typeof objectRequires !== "object" &&
typeof objectRequires !== "undefined") {
throw new Error("Parameter 'objectRequires' should be an object.");
}
this._require = _require;
this._objectRequires = objectRequires;
return this;
}
},
getExports: {
value: function (_require, moduleId) {
var module = _require.getModuleDescriptor(_require.resolve(moduleId));
while (module.redirect !== void 0) {
module = _require.getModuleDescriptor(module.redirect);
}
if (module.mappingRedirect !== void 0) {
return this.getExports(module.mappingRequire, module.mappingRedirect);
}
return module.exports;
}
},
getModule: {
value: function (moduleId, label) {
var objectRequires = this._objectRequires,
_require,
module;
if (objectRequires && label in objectRequires) {
_require = objectRequires[label];
} else {
_require = this._require;
}
module = this.getExports(_require, moduleId);
if (!module) {
module = _require.async(moduleId);
}
return module;
}
}
});
/**
* @class MontageReviver
*/
var MontageReviver = exports.MontageReviver = Montage.specialize(/** @lends MontageReviver# */ {
moduleLoader: {
value: null
},
/**
* @param {Require} _require The require object to load modules
* @param {Object} objectRequires A dictionary indexed by object label with
* the require object to use for a specific object of the
* serialization.
* @param {Function} deserializerConstructor Function to create a new
* deserializer. Useful for linking to an external module
* that also needs to be deserialized.
*/
init: {
value: function (_require, objectRequires, deserializerConstructor) {
this.moduleLoader = new ModuleLoader().init(_require, objectRequires);
this._require = _require;
this._deserializerConstructor = deserializerConstructor;
return this;
}
},
getTypeOf: {
value: function (value) {
var typeOf = typeof value;
if (value === null) {
return "null";
} else if (Array.isArray(value)) {
return "array";
} else if (typeOf === "object" && Object.keys(value).length === 1) {
if ("@" in value) {
return "reference";
} else if ("/" in value) {
return "regexp";
} else if ("#" in value) {
return "Element";
} else if ("%" in value) {
return "Module";
} else if (ONE_WAY in value || TWO_WAY in value || ONE_ASSIGNMENT in value) {
return "binding";
} // else return typeOf -> object
}
return typeOf;
}
},
_checkLabel: {
value: function (label, isTemplateProperty) {
if (isTemplateProperty && label[0] !== ":") {
return new Error("Aliases can only be defined in template values (start with a colon (:)), \"" + label + "\".");
} else if (!isTemplateProperty && label[0] === ":") {
return new Error("Only aliases are allowed as template values (start with a colon (:), \"" + label + "\".");
}
}
},
setProxyForDatasetOnElement: {
value: function (element, montageObjectDesc) {
var originalDataset = element.dataset;
if (Object.getPrototypeOf(originalDataset) !== null) {
var datasetAttributes = Object.keys(originalDataset),
targetObject = Object.create(null), self = this,
datasetAttribute, propertyNames;
if (Proxy.prototype) { // The native Proxy has no prototype property.
// Workaround for Proxy polyfill https://github.com/GoogleChrome/proxy-polyfill
// the properties of a proxy must be known at creation time.
// TODO: remove when we drop the support of IE11.
if (montageObjectDesc.values) {
propertyNames = Object.keys(montageObjectDesc.values);
} else { // deprecated
propertyNames = Object.keys(montageObjectDesc.properties)
.concat(Object.keys(montageObjectDesc.bindings));
}
datasetAttributes = datasetAttributes.concat(
propertyNames.filter(function (propertyName) {
return propertyName.startsWith("dataset.");
})
);
for (var i = 0, length = datasetAttributes.length; i < length; i++) {
datasetAttribute = datasetAttributes[i];
if (originalDataset[datasetAttribute]) {
targetObject[datasetAttribute] =
originalDataset[datasetAttribute];
} else {
targetObject[datasetAttribute.replace(/^dataset\./, '')] = void 0;
}
}
}
Object.defineProperty(element, "dataset", {
value: new Proxy(targetObject, {
set: function (target, propertyName, value) {
target[propertyName] = value;
originalDataset[propertyName] = value;
element.nativeSetAttribute(
DATA_ATTRIBUTES_MAP.get(propertyName) ||
(DATA_ATTRIBUTES_MAP.set(
propertyName,
'data-' +
kebabCaseConverter.convert(propertyName)
)).get(propertyName),
value
);
return true;
},
get: function (target, propertyName) {
return target[propertyName];
}
})
});
}
}
},
setProxyOnElement: {
value: function (element, montageObjectDesc) {
if (!PROXY_ELEMENT_MAP.has(element)) {
var targetObject = Object.create(null);
if (Proxy.prototype) { // The native Proxy has no prototype property.
// Workaround for Proxy polyfill https://github.com/GoogleChrome/proxy-polyfill
// the properties of a proxy must be known at creation time.
// TODO: remove when we drop the support of IE11.
var propertyNames, propertyName;
for (propertyName in element) {
if (element.hasOwnProperty(propertyName)) {
targetObject[propertyName] = void 0;
}
}
if (montageObjectDesc.values) {
propertyNames = Object.keys(montageObjectDesc.values);
} else { // deprecated
propertyNames = Object.keys(montageObjectDesc.properties)
.concat(Object.keys(montageObjectDesc.bindings));
}
for (var i = 0, length = propertyNames.length; i < length; i++) {
propertyName = propertyNames[i];
if (!(propertyName in element) && propertyName.indexOf('.') === -1) {
targetObject[propertyName] = void 0;
}
}
}
PROXY_ELEMENT_MAP.set(element, new Proxy(targetObject, {
set: function (target, propertyName, value) {
if (!(propertyName in Object.getPrototypeOf(element))) {
if (Object.getOwnPropertyDescriptor(element, propertyName) === void 0) {
Object.defineProperty(element, propertyName, {
set: function (value) {
target[propertyName] = value;
if (value === null || value === void 0) {
element.removeAttribute(propertyName);
} else {
element.nativeSetAttribute(propertyName, value);
}
},
get: function () {
return target[propertyName];
}
});
}
}
if (target[propertyName] !== value) {
element[propertyName] = value;
}
return true;
},
get: function (target, propertyName) {
return target[propertyName] || element[propertyName];
}
}));
}
return PROXY_ELEMENT_MAP.get(element);
}
},
wrapSetAttributeForElement: {
value: function (element) {
if (element.setAttribute === element.nativeSetAttribute) {
var proxyElement = PROXY_ELEMENT_MAP.get(element),
self = this;
element.setAttribute = function (key, value) {
var propertyName;
if (key.startsWith('data-')) {
propertyName = camelCaseConverter.convert(key.replace('data-', ''));
proxyElement.dataset[propertyName] = value;
} else {
propertyName = camelCaseConverter.convert(key);
proxyElement[propertyName] = value;
}
element.nativeSetAttribute(key, value);
};
}
}
},
reviveRootObject: {
value: function (value, context, label) {
var error,
object,
isAlias = "alias" in value;
// Only aliases are allowed as template values, everything else
// should be rejected as an error.
error = this._checkLabel(label, isAlias);
if (error) {
return Promise.reject(error);
}
// Check if the optional "debugger" unit is set for this object
// and stop the execution. This is intended to provide a certain
// level of debugging in the serialization.
if (value.debugger) {
debugger; // jshint ignore:line
}
if ("value" in value) {
// it's overriden by a user object
if (context.hasUserObject(label)) {
object = context.getUserObject(label);
context.setObjectLabel(object, label);
return object;
}
var revivedValue = this.reviveValue(value.value, context, label),
valueType = this.getTypeOf(value.value);
if (valueType === "Element") {
if (!Promise.is(revivedValue)) {
var proxyElement = this.setProxyOnElement(revivedValue, value);
this.setProxyForDatasetOnElement(revivedValue, value);
this.wrapSetAttributeForElement(revivedValue);
context.setBindingsToDeserialize(proxyElement, value);
this.deserializeMontageObjectValues(
proxyElement,
value.values || value.properties, //deprecated
context
);
}
} else if (valueType === "object") {
context.setBindingsToDeserialize(revivedValue, value);
this.deserializeMontageObjectValues(
revivedValue,
value.values || value.properties, //deprecated
context
);
context.setUnitsToDeserialize(revivedValue, value, MontageReviver._unitNames);
}
return revivedValue;
} else if (Object.keys(value).length === 0) {
// it's an external object
if (context.hasUserObject(label)) {
object = context.getUserObject(label);
context.setObjectLabel(object, label);
return object;
}
return this.reviveExternalObject(value, context, label);
} else if ("alias" in value) {
return this.reviveAlias(value, context, label);
} else {
return this.reviveMontageObject(value, context, label);
}
}
},
reviveElement: {
value: function (value, context, label) {
var elementId = value["#"],
element = context.getElementById(elementId);
if (element) {
if (label) {
context.setObjectLabel(element, label);
}
return element;
} else {
return Promise.reject(new Error("Element with id '" + elementId + "' was not found."));
}
}
},
reviveModule: {
value: function (value, context, label) {
var moduleId = value["%"],
_require = context.getRequire();
moduleId = _require.resolve(moduleId);
var module = _require.getModuleDescriptor(moduleId);
return new ModuleReference().initWithIdAndRequire(module.id, module.require);
}
},
reviveAlias: {
value: function (value, context, label) {
var alias = new Alias();
alias.value = value.alias;
context.setObjectLabel(alias, label);
return alias;
}
},
reviveMontageObject: {
value: function (value, context, label) {
var self = this,
module,
locationDesc,
locationId = value.prototype || value.object,
objectName;
if (locationId) {
locationDesc = MontageReviver.parseObjectLocationId(locationId);
module = this.moduleLoader.getModule(locationDesc.moduleId,
label);
objectName = locationDesc.objectName;
}
if (Promise.is(module)) {
return module.then(function (exports) {
return self.instantiateObject(exports, locationDesc, value, objectName, context, label);
}, function (error) {
if (error.stack) {
console.error(error.stack);
}
throw new Error('Error deserializing "' + label +
'" when loading module "' + locationDesc.moduleId +
"' from '" + value.prototype + "' cause: " + error.message);
});
} else {
return this.instantiateObject(module, locationDesc, value, objectName, context, label);
}
}
},
instantiateObject: {
value: function (module, locationDesc, value, objectName, context, label) {
var self = this,
moduleId = value.prototype || value.object,
object;
if (moduleId && (moduleId.endsWith(".mjson") || moduleId.endsWith(".meta"))) {
return this.getMjsonObject(value, module, moduleId, context)
.then(function (object) {
context.setObjectLabel(object, label);
return self.instantiateMjsonObject(value, object, objectName, context, label);
});
} else {
object = this.getMontageObject(value, module, objectName, context, label);
context.setObjectLabel(object, label);
return this.instantiateMontageObject(value, object, objectName, context, label);
}
}
},
getMjsonObject: {
value: function (serialization, json, moduleId, context) {
var self = this,
deserializer = new this._deserializerConstructor().init(
json,
this._deserializerConstructor.getModuleRequire(this._require, moduleId),
void 0,
moduleId
);
return Promise.resolve(deserializer)
.then(function (deserializer) {
return deserializer.deserializeObject();
})
.then(function (object) {
if ("prototype" in serialization) {
return Object.create(object);
} else {
return object;
}
});
}
},
getMontageObject: {
value: function (value, module, objectName, context, label) {
var object;
if (context.hasUserObject(label)) {
return context.getUserObject(label);
} else if ("prototype" in value) {
if (!(objectName in module)) {
throw new Error('Error deserializing "' + label +
'": object named "' + objectName + '"' +
' was not found in "' + value.prototype + '".' +
" Available objects are: " + Object.keys(module) + ".");
}
// TODO: For now we need this because we need to set
// isDeserilizing before calling didCreate.
object = module[objectName];
object = (typeof object === "function") ? new object() : Object.create(object);
object.isDeserializing = true;
return object;
} else if ("object" in value) {
if (value.object.endsWith(".json")) {
return module;
}
if (!(objectName in module)) {
throw new Error('Error deserializing "' + label +
'": object named "' + object +
"' was not found given '" + value.object + "'");
}
return module[objectName];
} else {
throw new Error("Error deserializing " + JSON.stringify(value) + ", might need \"prototype\" or \"object\" on label " + JSON.stringify(label));
}
}
},
instantiateMjsonObject: {
value: function (serialization, object, objectName, context, label) {
var self = this,
montageObjectDesc;
if (object !== null && object !== void 0) {
object.isDeserializing = true;
}
context.setBindingsToDeserialize(object, serialization);
montageObjectDesc = this.reviveObjectLiteral(serialization, context);
if (Promise.is(montageObjectDesc)) {
return montageObjectDesc.then(function(montageObjectDesc) {
return self.deserializeMontageObject(montageObjectDesc, object, context, label);
});
} else {
return this.deserializeMontageObject(montageObjectDesc, object, context, label);
}
}
},
instantiateMontageObject: {
value: function (serialization, object, objectName, context, label) {
var self = this,
montageObjectDesc;
if (object !== null && object !== void 0) {
object.isDeserializing = true;
}
if (serialization.bindings) {
deprecate.deprecationWarningOnce(
"'bindings' block is deprecated, use 'values' instead"
);
}
if (serialization.properties) {
deprecate.deprecationWarningOnce(
"'properties' block is deprecated, use 'values' instead"
);
}
context.setBindingsToDeserialize(object, serialization);
montageObjectDesc = this.reviveObjectLiteral(serialization, context);
if (Promise.is(montageObjectDesc)) {
return montageObjectDesc.then(function(montageObjectDesc) {
if (typeof object.deserializeSelf === "function") {
return self.deserializeCustomMontageObject(object, montageObjectDesc, context, label);
} else {
return self.deserializeMontageObject(montageObjectDesc, object, context, label);
}
});
} else {
if (typeof object.deserializeSelf === "function") {
return this.deserializeCustomMontageObject(object, montageObjectDesc, context, label);
} else {
return this.deserializeMontageObject(montageObjectDesc, object, context, label);
}
}
}
},
deserializeMontageObject: {
value: function (montageObjectDesc, object, context, label) {
var values;
// Units are deserialized after all objects have been revived.
// This happens at didReviveObjects.
context.setUnitsToDeserialize(object, montageObjectDesc, MontageReviver._unitNames);
values = this.deserializeMontageObjectValues(
object,
montageObjectDesc.values || montageObjectDesc.properties, //deprecated
context
);
return object;
}
},
deserializeMontageObjectProperties: {
value: deprecate.deprecateMethod(void 0, function (object, values, context) {
return this.deserializeMontageObjectValues(object, values, context);
}, "deserializeMontageObjectProperties", "deserializeMontageObjectValues")
},
deserializeMontageObjectValues: {
value: function (object, values, context) {
var value;
if (typeof object.deserializeProperties === "function" || typeof object.deserializeValues === "function") {
var valuesDeserializer = new ValuesDeserializer()
.initWithReviverAndObjects(this, context);
if (object.deserializeValues) {
value = object.deserializeValues(valuesDeserializer);
} else { // deprecated
value = object.deserializeProperties(valuesDeserializer);
}
} else {
/* jshint forin: true */
for (var key in values) {
/* jshint forin: false */
object[key] = values[key];
}
}
return value;
}
},
deserializeCustomMontageObject: {
value: function (object, objectDesc, context, label) {
var substituteObject;
var selfDeserializer = new SelfDeserializer()
.initWithObjectAndObjectDescriptorAndContextAndUnitNames(object, objectDesc, context, MontageReviver._unitNames);
substituteObject = object.deserializeSelf(selfDeserializer);
if (Promise.is(substituteObject)) {
return substituteObject.then(function(substituteObject) {
context.setObjectLabel(substituteObject, label);
return substituteObject;
});
} else if (typeof substituteObject !== "undefined") {
context.setObjectLabel(substituteObject, label);
return substituteObject;
} else {
return object;
}
}
},
didReviveObjects: {
value: function (objects, context) {
var self = this;
return Promise.all([
this._deserializeBindings(context),
this._deserializeUnits(context)
]).then(function () {
self._invokeDeserializedFromSerialization(objects, context);
});
}
},
// TODO: can deserializeSelf make deserializedFromSerialization irrelevant?
_invokeDeserializedFromSerialization: {
value: function (objects, context) {
var object;
/* jshint forin: true */
for (var label in objects) {
/* jshint forin: false */
object = objects[label];
if (object !== null && object !== void 0) {
delete object.isDeserializing;
}
if (!context.hasUserObject(label)) {
// TODO: merge deserializedFromSerialization with
// deserializedFromTemplate?
if (object && typeof object.deserializedFromSerialization === "function") {
object.deserializedFromSerialization(label);
}
}
}
}
},
_deserializeBindings: {
value: function (context) {
var bindingsToDeserialize = context.getBindingsToDeserialize(),
unitDeserializer = new UnitDeserializer(),
bindingsToDeserializeDesc;
if (bindingsToDeserialize) {
try {
for (var i = 0, length = bindingsToDeserialize.length; i < length; i++) {
bindingsToDeserializeDesc = bindingsToDeserialize[i];
Bindings.deserializeObjectBindings(
unitDeserializer.initWithContext(context),
bindingsToDeserializeDesc.object,
bindingsToDeserializeDesc.bindings
);
}
} catch (ex) {
return Promise.reject(ex);
}
}
}
},
_deserializeUnits: {
value: function (context) {
var unitsToDeserialize = context.getUnitsToDeserialize(),
unitDeserializer = new UnitDeserializer(),
unitNames;
try {
for (var i = 0, unitsDesc; (unitsDesc = unitsToDeserialize[i]); i++) {
unitNames = unitsDesc.unitNames;
for (var j = 0, unitName; (unitName = unitNames[j]); j++) {
if (unitName in unitsDesc.objectDesc) {
unitDeserializer.initWithContext(context);
MontageReviver._unitRevivers.get(unitName)(unitDeserializer, unitsDesc.object, unitsDesc.objectDesc[unitName]);
}
}
}
} catch (ex) {
return Promise.reject(ex);
}
}
},
_createAssignValueFunction: {
value: function(object, propertyName) {
return function(value) {
object[propertyName] = value;
};
}
},
getCustomObjectTypeOf: {
writable: true,
value: function() {}
},
reviveValue: {
value: function(value, context, label) {
var type = this.getTypeOf(value);
if (type === "string" || type === "number" || type === "boolean" || type === "null" || type === "undefined") {
return this.reviveNativeValue(value, context, label);
} else if (type === "regexp") {
return this.reviveRegExp(value, context, label);
} else if (type === "reference") {
return this.reviveObjectReference(value, context, label);
} else if (type === "array") {
return this.reviveArray(value, context, label);
} else if (type === "object") {
return this.reviveObjectLiteral(value, context, label);
} else if (type === "Element") {
return this.reviveElement(value, context, label);
} else if (type === "binding") {
return value;
} else {
return this._callReviveMethod("revive" + type, value, context, label);
}
}
},
reviveNativeValue: {
value: function(value, context, label) {
if (label) {
context.setObjectLabel(value, label);
}
return value;
}
},
reviveObjectLiteral: {
value: function(value, context, label) {
var item,
promises = [];
if (label) {
context.setObjectLabel(value, label);
}
for (var propertyName in value) {
if (value.hasOwnProperty(propertyName)) {
if (value[propertyName] === value) {
// catch object property that point to its parent
return value;
}
item = this.reviveValue(value[propertyName], context);
if (Promise.is(item)) {
promises.push(
item.then(this._createAssignValueFunction(
value, propertyName)
)
);
} else {
value[propertyName] = item;
}
}
}
if (promises.length === 0) {
return value;
} else {
return Promise.all(promises).then(function() {
return value;
});
}
}
},
reviveRegExp: {
value: function(value, context, label) {
var valuePath = value["/"],
regexp = new RegExp(valuePath.source, valuePath.flags);
if (label) {
context.setObjectLabel(regexp, label);
}
return regexp;
}
},
reviveObjectReference: {
value: function(value, context, label) {
var valuePath = value["@"],
object = context.getObject(valuePath);
return object;
}
},
reviveArray: {
value: function(value, context, label) {
var item,
promises = [];
if (label) {
context.setObjectLabel(value, label);
}
for (var i = 0, ii = value.length; i < ii; i++) {
item = this.reviveValue(value[i], context);
if (Promise.is(item)) {
promises.push(
item.then(this._createAssignValueFunction(value, i))
);
} else {
value[i] = item;
}
}
if (promises.length === 0) {
return value;
} else {
return Promise.all(promises).then(function() {
return value;
});
}
}
},
reviveExternalObject: {
value: function(value, context, label) {
return Promise.reject(
new Error("External object '" + label + "' not found in user objects.")
);
}
},
_callReviveMethod: {
value: function(methodName, value, context, label) {
return this[methodName](value, context, label);
}
}
}, /** @lends MontageReviver. */ {
_unitRevivers: {value: new Map()},
_unitNames: {value: []},
_findObjectNameRegExp: {
value: /([^\/]+?)(\.reel)?$/
},
_toCamelCaseRegExp: {
value: /(?:^|-)([^-])/g
},
_replaceToCamelCase: {
value: function (_, g1) { return g1.toUpperCase(); }
},
// Cache of location descriptors indexed by locationId
_locationDescCache: {value: new Map()},
customObjectRevivers: {value: new Map()},
/**
* Location Id is in the form of <moduleId>[<objectName>] where
* [<objectName>] is optional. When objectName is missing it is derived
* from the last path component of moduleId transformed into CamelCase.
*
* @example "event/event-manager" has a default objectName of EventManager.
*
* When the last path component ends with ".reel" it is removed before
* creating the default objectName.
*
* @example "matte/ui/input-range.reel" has a default objectName of
* InputRange.
*
* @returns {moduleId, objectName}
*/
parseObjectLocationId: {
value: function (locationId) {
return this._locationDescCache.get(locationId) || this.createObjectLocationDesc(locationId);
}
},
createObjectLocationDesc: {
value: function (locationId) {
var moduleId,
objectName,
bracketIndex = locationId.indexOf("[");
if (bracketIndex > 0) {
moduleId = locationId.substr(0, bracketIndex);
objectName = locationId.slice(bracketIndex + 1, -1);
} else {
moduleId = locationId;
this._findObjectNameRegExp.test(locationId);
objectName = RegExp.$1.replace(
this._toCamelCaseRegExp,
this._replaceToCamelCase
);
}
var locationDesc = {
moduleId: moduleId,
objectName: objectName
};
this._locationDescCache.set(locationId, locationDesc);
return locationDesc;
}
},
defineUnitReviver: {
value: function (name, funktion) {
this._unitRevivers.set(name, funktion);
this._unitNames.push(name);
}
},
getTypeOf: {
value: function (value) {
return this.prototype.getTypeOf.call(this, value);
}
},
addCustomObjectReviver: {
value: function(reviver) {
var customObjectRevivers = this.customObjectRevivers;
/* jshint forin: true */
for (var methodName in reviver) {
/* jshint forin: false */
if (methodName === "getTypeOf") {
continue;
}
if (
typeof reviver[methodName] === "function" &&
methodName.substr(0, 5) === "revive"
) {
if (typeof (customObjectRevivers.get(methodName)) === "undefined") {
customObjectRevivers.set(methodName, reviver[methodName].bind(reviver));
} else {
return new Error("Reviver '" + methodName + "' is already registered.");
}
}
}
this.prototype.getCustomObjectTypeOf = this.makeGetCustomObjectTypeOf(reviver.getTypeOf);
}
},
resetCustomObjectRevivers: {
value: function() {
this.customObjectRevivers.clear();
this.prototype.getCustomObjectTypeOf = function() {};
}
},
makeGetCustomObjectTypeOf:{
value: function (getCustomObjectTypeOf) {
var previousGetCustomObjectTypeOf = this.prototype.getCustomObjectTypeOf;
return function(value) {
return getCustomObjectTypeOf(value) || previousGetCustomObjectTypeOf(value);
};
}
}
});
MontageReviver.findProxyForElement = function (element) {
return PROXY_ELEMENT_MAP.get(element);
};
if (typeof exports !== "undefined") {
exports.MontageReviver = MontageReviver;
}