beforeEach(function () { foreignConstructor = jasmine.createSpy("Foreign") Vehicle = Montage.specialize.call(foreignConstructor, { constructor: { value: function Vehicle() { vehicleConstructorSpy(); this.super(); } } }); Car = Vehicle.specialize( { constructor: { value: function Car() { carConstructorSpy(); this.super(); } } }); Beetle = Car.specialize( { constructor: { value: function Beetle() { beetleConstructorSpy(); this.super(); } } }); });
beforeEach(function () { Vehicle = Montage.specialize.call(function Foreign () {}, { constructor: {value: function Vehicle() {}} }); Car = Vehicle.specialize( { constructor: {value: function Car() {}} }); Beetle = Car.specialize( { constructor: {value: function Beetle() {}} }); });
var MontageVisitor = Montage.specialize.call(Visitor, { _MONTAGE_ID_ATTRIBUTE: {value: "data-montage-id"}, _require: {value: null}, _units: {value: null}, _elements: {value: null}, constructor: { value: function MontageVisitor() {} }, initWithBuilderAndLabelerAndRequireAndUnits: { value: function(builder, labeler, require, units) { Visitor.call(this, builder, labeler); this._require = require; this._units = units; this._elements = []; return this; } }, getTypeOf: { value: function(object) { if ("getInfoForObject" in object || "getInfoForObject" in object.constructor) { return "MontageObject"; } else if (object.thisIsAReferenceCreatedByMontageSerializer) { return "MontageReference"; } else if (typeof Element !== "undefined" && Element.isElement(object)) { return "Element"; } } }, visitMontageReference: { value: function(malker, object, name) { this.builder.top.setProperty(name, object.reference); } }, visitElement: { value: function(malker, element, name) { var elementReference, id; id = element.getAttribute(this._MONTAGE_ID_ATTRIBUTE); if (id) { elementReference = this.builder.createElementReference(id); this.storeValue(elementReference, element, name); this._elements.push(element); } else { throw new Error("Not possible to serialize a DOM element with no " + this._MONTAGE_ID_ATTRIBUTE + " assigned: " + element.outerHTML); } } }, visitMontageObject: { value: function(malker, object, name) { if (this.isObjectSerialized(object)) { this.serializeReferenceToMontageObject(malker, object, name); } else { this.handleMontageObject(malker, object, name); } } }, handleMontageObject: { value: function(malker, object, name) { var builderObject = this.builder.createCustomObject(), substituteObject; this.setObjectSerialization(object, builderObject); substituteObject = this.serializeMontageObject(malker, object, builderObject); if (substituteObject) { this.serializeSubstituteObject(malker, object, name, builderObject, substituteObject); } else { builderObject.setLabel(this.labeler.getObjectLabel(object)); this.builder.top.setProperty(name, builderObject); } } }, serializeReferenceToMontageObject: { value: function(malker, object, name) { var label = this.labeler.getObjectLabel(object), reference = this.builder.createObjectReference(label); this.builder.top.setProperty(name, reference); } }, serializeSubstituteObject: { value: function(malker, object, name, builderObject, substituteObject) { var label, oldLabel, newLabel, substituteBuilderObject; label = this.labeler.getObjectLabel(object); // There are two label strategies to choose from when an object // is substituted for another one in a serialization: // // 1) The object that was substituted now starts being recognized // with the label of the substituted object, this means // relabeling all previously created references. // // 2) In the case that the object being substituted has a user // defined label then we want to honor this label and relabel // the substitute object with the user defined label instead. if (this.labeler.isUserDefinedLabel(label)) { oldLabel = this.labeler.getObjectLabel(substituteObject); // Make sure that the substitute object is now // known by the user defined label instead. this.labeler.setObjectLabel(substituteObject, label); // If there were references made to the substitute object we // need to change them to start pointing to the user defined // label. this.builder.relabelReferences(oldLabel, label); // Check if the substitute object was already serialized, if it // was we need to change the label of the serialization. substituteBuilderObject = this.getObjectSerialization(substituteObject); if (substituteBuilderObject) { substituteBuilderObject.setLabel(label); // If the substitute object also has a user defined label // then we create a reference from one label to the other if (this.labeler.isUserDefinedLabel(oldLabel)) { this.builder.createObjectReference(label).setLabel(oldLabel); } } malker.visit(substituteObject, name); } else { malker.visit(substituteObject, name); newLabel = this.labeler.getObjectLabel(substituteObject); // Make sure that the substituted object is now known by the // label of the substitute object. this.labeler.setObjectLabel(object, newLabel); // If there were references made to object we need to change // them to start pointing to the substitute object. this.builder.relabelReferences(label, newLabel); } } }, serializeMontageObject: { value: function(malker, object, builderObject) { var selfSerializer, substituteObject, propertiesBuilderObject = this.builder.createObjectLiteral(); this.setObjectType(object, builderObject); builderObject.setProperty("properties", propertiesBuilderObject); this.builder.push(builderObject); if (typeof object.serializeSelf === "function") { selfSerializer = new SelfSerializer(). initWithMalkerAndVisitorAndObject( malker, this, object, builderObject); substituteObject = object.serializeSelf(selfSerializer); } else { this.setObjectProperties(malker, object); this.setObjectCustomUnits(malker, object); } this.builder.pop(); // Remove the properties unit in case none was serialized, // we need to add it before any other units to make sure that // it's the first unit to show up in the serialization, since we // don't have a way to order the property names in a serialization. if (propertiesBuilderObject.getPropertyNames().length === 0) { builderObject.clearProperty("properties"); } return substituteObject; } }, setObjectType: { value: function(object, builderObject) { var isInstance = Montage.getInfoForObject(object).isInstance, locationId = this.getObjectLocationId(object), locationIdBuilderObject = this.builder.createString(locationId); if (isInstance) { builderObject.setProperty("prototype", locationIdBuilderObject); } else { builderObject.setProperty("object", locationIdBuilderObject); } } }, getObjectModuleId: { value: function(object) { var objectInfo = Montage.getInfoForObject(object); return this._require.identify(objectInfo.moduleId, objectInfo.require); } }, getObjectLocationId: { value: function(object) { var moduleId = this.getObjectModuleId(object), defaultObjectName, objectInfo = Montage.getInfoForObject(object), objectName = objectInfo.objectName; defaultObjectName = MontageSerializerModule.MontageSerializer.getDefaultObjectNameForModuleId(moduleId); if (defaultObjectName === objectName) { return moduleId; } else { return moduleId + "[" + objectName + "]"; } } }, /* * Expected object at the top of the stack: CustomObject */ setObjectProperties: { value: function(malker, object) { var propertiesSerializer, propertiesObject; propertiesObject = this.builder.top.getProperty("properties"); this.builder.push(propertiesObject); if (typeof object.serializeProperties === "function") { propertiesSerializer = new PropertiesSerializer() .initWithMalkerAndVisitorAndObject(malker, this, object); object.serializeProperties(propertiesSerializer); } else { this.setSerializableObjectProperties(malker, object); } this.builder.pop(); } }, /* * Expected object at the top of the stack: ObjectLiteral */ setSerializableObjectProperties: { value: function(malker, object) { var type, propertyName, propertyNames = Montage.getSerializablePropertyNames(object), propertyNamesCount = propertyNames.length; for (var i = 0; i < propertyNamesCount; i++) { propertyName = propertyNames[i]; type = Montage.getPropertyAttribute(object, propertyName, "serializable"); this.setProperty(malker, propertyName, object[propertyName], type); } } }, hackIsReferenceAllowedForValue: { value: function(value) { // Only serialize as a reference values that are non-null objects, // we don't support references to non-objects and elements. // There's nothing in the serialization that prevents us to store // a reference to an object but that would be an external reference // the problem here is that the serializable defaults to "reference" // for most cases when in reality we probably just want "value". return typeof value === "object" && value != null && !(typeof Element !== "undefined" && Element.isElement(value)); } }, /* * Expected object at the top of the stack: ObjectLiteral */ setProperty: { value: function(malker, propertyName, value, type) { var label; if (type === "reference" && this.hackIsReferenceAllowedForValue(value)) { label = this.labeler.getObjectLabel(value); var reference = this.builder.createObjectReference(label); this.builder.top.setProperty(propertyName, reference); } else { malker.visit(value, propertyName); } } }, setObjectCustomUnits: { value: function(malker, object) { for (var unitName in this._units) { this.setObjectCustomUnit(malker, object, unitName); } } }, setObjectCustomUnit: { value: function(malker, object, unitName) { var unit = this._units[unitName], value, unitSerializer; if (!unit) { return; } unitSerializer = new UnitSerializer() .initWithMalkerAndVisitorAndObject(malker, this, object); value = unit(unitSerializer, object); if (value != null) { malker.visit(value, unitName); } } }, getExternalObjects: { value: function() { var externalObjects = {}, labels = this.builder.getExternalReferences(), label; for (var i = 0; label = labels[i]; i++) { externalObjects[label] = this.labeler.getObjectByLabel(label); } return externalObjects; } }, getExternalElements: { value: function() { return this._elements; } } });
var Labeler = require("mousse/serialization/labeler").Labeler; exports.MontageLabeler = Montage.specialize.call(Labeler, { _labelRegexp: {value: /^[a-zA-Z_$][0-9a-zA-Z_$]*$/}, constructor: { value: function MontageLabeler() { Labeler.call(this); } }, getObjectName: { value: function(object) { var identifier = object.identifier, objectName; if (identifier && this._labelRegexp.test(identifier)) { objectName = object.identifier; } else if ("getInfoForObject" in object || "getInfoForObject" in object.constructor ) { objectName = Montage.getInfoForObject(object).objectName; objectName = objectName.toLowerCase(); } else { objectName = Labeler.prototype.getObjectName.call( this, object); } return objectName; } } });
var Value = require("mousse/serialization/ast").Value; /** * @class ElementReference * @extends Value */ var ElementReference = Montage.specialize.call(Value, /** @lends ElementReference# */ { constructor: { value: function ElementReference() {} }, initWithRootAndId: { value: function(root, id) { Value.call(this, root, id); return this; } }, _getSerializationValue: { value: function() { return {"#": this.value}; } } }); /** * @class ModuleReference * @extends Value */ var ModuleReference = Montage.specialize.call(Value, /** @lends ModuleReference# */ {
var Montage = require("montage").Montage; var Value = require("mousse/serialization/ast").Value; /** * ElementReference * * @extends Value */ var ElementReference = Montage.specialize.call(Value, { constructor: { value: function ElementReference() {} }, initWithRootAndId: { value: function(root, id) { Value.call(this, root, id); return this; } }, _getSerializationValue: { value: function() { return {"#": this.value}; } } }); exports.ElementReference = ElementReference;