<@script name="ClassFactory" hide="y"> A collection of macros for creating and managing namespaces, classes, prototypes, and aspect declarations This file must be loaded exactly once, into the top-level %STUDIO% framework (c) SAP AG 2003-2006. All rights reserved. // GLOBALS var $DOM = {}; // The global GML DOM $DOM.namespaces = {}; // The global namespaces collection $DOM.allClasses = {}; // The global classes collection $DOM.allEvents = {}; // The global events collection $DOM.allRoles = {}; // The global aspect roles collection $DOM.allAssociations = {}; // The global associations collection $DOM.currNS = null; // The namespace currently in scope $DOM.currClass = null; // The class currently in scope $DOM.currFile = null; // The source file urn currently in scope $DOM.prefixes = {}; // The global namespaces prefixes collection $DOM._partialClassOrder = {}; // The classes partial ordering relation $DOM._metadataCounter = 0; // Determines the priority of the 'metadata for' statements $DOM.perfCheck = #[PERF_MSR]; //true; var _reinstalling = false; $DOM.sysAliases = { // The builtin system aliases dev: 'core.dev', env: 'core.env', gkb: 'core.gkb', gml: 'core.gml', gml2: 'core.gml2', lib: 'core.lib', lyt: 'core.lyt', svg: 'core.svg' } $DOM.sysEvents = { // The builtin system events oninit: '', onexit: '', onshow: '', onhide: '', onresize: '', onoptionsmenu: '', onchannelconnect: '$ENV', oncontextswitch: '$ENV', onSyncState: '$ENV', onkitchange: '$ENV', onmodelclose: '$ENV', onmodeldirty: '$ENV', onmodelevent: '$ENV', onmodelorganize: '$ENV', onmodelopen: '$ENV', onmodelsave: '$ENV', onsettingschange: '$CTL', onSyncState: '$ENV', onstudioexit: '$CTL', onLogEvent: '$ENV' } //////////////////////////////////////////////////////////////////////// // PRIMARY DECLARATIONS <@func name="declareClass"> Starts a class declaration The class name The direct superclass name, if any $DOM.declareClass = function(className, superClassName, isAbstract) { try { var newClass = $DOM.startDeclaration('Class', className, superClassName, isAbstract); var superClass = newClass.superclass; if (superClass) { for (var k in superClass.dependents) newClass.dependents[k] = superClass.dependents[k]; } if (!superClassName) { $DOM._addNativeMethod(newClass, 'isa', $DOM._isaInstance, {public:true}); $DOM._addNativeMethod(newClass, 'getProperty', $DOM._getInstanceProp, {public:true}); $DOM._addNativeMethod(newClass, 'setProperty', $DOM._setInstanceProp, {public:true}); } $DOM._addNativeMethod(newClass, 'toString', $DOM._toString, {public:true}); return newClass; } catch (e) { $DOM.raiseDOMError(e.description); return null; } } <@func name="declarePrototype"> Starts a prototype declaration The prototype name The direct super-prototype name, if any $DOM.declarePrototype = function(prototypeName, superPrototypeName, isAbstract) { try { return $DOM.startDeclaration('Prototype', prototypeName, superPrototypeName, isAbstract); } catch (e) { $DOM.raiseDOMError(e.description); return null; } } <@func name="declareAspect"> Starts an aspect declaration The aspect name The direct super-aspect name, if any The aspect role $DOM.declareAspect = function(aspectName, superAspectName, aspectRole, isAbstract) { try { var roleClass = $DOM._getClassifier(aspectRole), superAspect=roleClass; if (!roleClass || !roleClass.dollar) throw new Error(-1, 'Unknown aspect role "'+aspectRole+'"'); if (superAspectName) { var superAspect = $DOM._getClassifier(superAspectName); if (!superAspect) throw new Error(-1, 'Unknown super aspect "'+superAspectName+'"'); if (!(roleClass.fullname in superAspect.precedents)) throw new Error(-1, 'Super aspect "'+superAspect.name+'" must belong to "'+roleClass.name+'" Aspect role'); } var aspectClass = $DOM.startDeclaration('Aspect', aspectName, superAspect.fullname, isAbstract); aspectClass.dollar = roleClass.dollar; aspectClass.rolename = roleClass.rolename; return aspectClass; } catch (e) { $DOM.raiseDOMError(e.description); return null; } } <@func name="declareAspectRole"> Declares a new aspect role The aspect role's qualified name The aspect role's dollar qualifier $DOM.declareAspectRole = function(aspectRole, dollar) { try { var roleClass = $DOM.startDeclaration('Aspect', aspectRole); var roleName = roleClass.fullname; if (roleName in $DOM.allRoles) throw new Error(-1, 'Aspect role "'+roleName+'" is already defined.'); try { var mft=$ENV.channel1.getKitsManifest(); dollar = mft.aspects[roleName].prefix; // use the correct dollar qualifier for the currently connected server } catch(e) { throw new Error(-1, 'Unrecognized aspect role "'+roleName+'"'); } roleClass.dollar = dollar; roleClass.rolename = roleName; roleClass.isAbstract = true; $DOM.allRoles[dollar] = roleClass; $DOM.allRoles[roleClass.rolename] = roleClass; $DOM.endDeclaration(); return roleClass; } catch (e) { $DOM.raiseDOMError(e.description); return null; } } <@func name="attachPrototype"> Attaches a prototype to an class/aspect The name of the prototype to add The name of the class/aspect that implements the prototype; if omitted, the class/aspect currently in scope is assumed $DOM.attachPrototype = function(prototypeName, implementorName) { try { // validate prototype and class var protoclass = $DOM._getClassifier(prototypeName); var implementor = $DOM._getClassifier(implementorName); if (!protoclass) throw new Error(-1, 'Prototype "'+prototypeName+'" is undefined'); if (!implementor) throw new Error(-1, 'Implementor "'+implementorName+'" is undefined'); if (!protoclass.isPrototype || !(implementor.isClass || implementor.isAspect)) throw new Error(-1, implementorName+' cannot implement '+prototypeName); if (protoclass.fullname in implementor.precedents) return; // attach prototype implementation implementor.ownRelations[protoclass.fullname] = protoclass; protoclass.ownRelations[implementor.fullname] = implementor; for (var k in protoclass.precedents) implementor.precedents[k] = protoclass.precedents[k]; protoclass.dependents[implementor.fullname] = implementor; $DOM._mergeMembers(implementor, protoclass); } catch (e) { $DOM.raiseDOMError(e.description); } } <@func name="attachAspect"> Attaches an aspect to a class The name of the aspect to attach The name of the class to attach A table of property values to override on the attached object $DOM.attachAspect = function(aspectName, className, values) { // This method is used for attaching aspects to classes based on GmlScript attach statements. // The aspect can later be tailored dynamically using the tailorAspect method in AspectFactory. try { // validate aspect and class var aspect = $DOM._getClassifier(aspectName); var baseclass = $DOM._getClassifier(className); if (!aspect) throw new Error(-1, 'Aspect "'+aspectName+'" is undefined'); if (!baseclass) throw new Error(-1, 'Base class "'+className+'" is undefined'); if (!aspect.isAspect || !baseclass.isClass) throw new Error(-1, className+' cannot be attached to '+aspectName); var originalAspect=baseclass.dependents[aspect.rolename] || null; var V={}; if (values) { for (var k in values) V[aspect.dollar+'$'+k] = values[k]; } // recursively attach aspect to class and all its sub-classes, if any are defined at this point baseclass.ownRelations[aspect.fullname] = aspect; aspect.ownRelations[baseclass.fullname] = baseclass; attachClass(baseclass); function attachClass(cstor) { var oldAspect = cstor.dependents[aspect.rolename] || null; if (oldAspect && oldAspect !== originalAspect) return; cstor.dependents[aspect.rolename] = aspect; aspect.dependents[cstor.fullname] = cstor; // add non-virtual members (virtual members are owned by the aspect instances) var P = aspect.properties; var Q = (cstor.superclass ? cstor.superclass.properties : null); var M = aspect.prototype; for (var k in M) { var p=P[k], m=M[k], q=(Q ? Q[k] : ''); if (q && q.isProperty && !q.isVirtual) { cstor.properties[k] = q; cstor.prototype[k] = ((k in V) ? V[k] : cstor.superclass.prototype[k]); } else if (p && p.isProperty && !p.isVirtual) { cstor.properties[k] = p; cstor.prototype[k] = ((k in V) ? V[k] : aspect.prototype[k]); } else if (m && m.isMethod && !m.isVirtual) { cstor.prototype[k] = m; } } for (var k in cstor.subclasses) { attachClass(cstor.subclasses[k]); } } } catch (e) { $DOM.raiseDOMError(e.description); } } //////////////////////////////////////////////////////////////////////// // MEMBER DECLARATIONS <@func name="addConstructor"> Adds a constructor to the current declaration unit The constructor function $DOM.addConstructor = function(cstor) { try { if (!$DOM.currClass) throw new Error(-1, 'Constructor definition out of scope'); var modifiers = {override:true}; if ($DOM.currClass.isAspect) modifiers.virtual = true; $DOM.addMethod('Constructor', cstor, modifiers); } catch (e) { $DOM.raiseDOMError(e.description); } } <@func name="addDestructor"> Adds a destructor to the current declaration unit The destructor function $DOM.addDestructor = function(dstor) { try { if (!$DOM.currClass) throw new Error(-1, 'Destructor definition out of scope'); var modifiers = {override:true}; if ($DOM.currClass.isAspect) modifiers.virtual = true; $DOM.addMethod('Destructor', dstor, modifiers); } catch (e) { $DOM.raiseDOMError(e.description); } } <@func name="addMethod"> Adds a method to the currrent declaration unit The method name The method function The property's modifiers {override, native, sealed, static, and virtual} $DOM.perfCheckFn = function(func){ return function (p1,p2, p3, p4 , p5, p6, p7,p8, p9){ var dis = $ENV.perfMng.disabled; dis || $ENV.perfMng.begin('Checking method:' + func.Class.fullname+ '.' +func.name); var res = func.call(this ,p1,p2, p3, p4 , p5, p6, p7,p8, p9); dis || $ENV.perfMng.end(); return res; } } $DOM.addMethodPerfCheck = function(name, func, modifiers) { try { if (name.search(/\b(constructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid method name'); var M=$DOM._splitName(name); if (!M) throw new Error(-1, '"'+name+'" is not a valid method name'); var newFn = $DOM.perfCheckFn(func); newFn.member = func.member = 'method'; newFn.name = func.name = (M.Class.isAspect ? M.Class.dollar+'$' : '') + M.Name; newFn.Class = func.Class = M.Class; newFn.isMethod = func.isMethod = true; if(!modifiers) modifiers = {}; // Methods by default are public if (!modifiers.public && !modifiers.private && !modifiers.protected) modifiers.public = true; for (var k in modifiers) newFn['is'+PREF(k)+k.substring(1)] = func['is'+PREF(k)+k.substring(1)] = true; newFn.member = func.member = (modifiers.public ? 'public ' : '') + (modifiers.private ? 'private ' : '') + (modifiers.protected ? 'protected ' : '') + (modifiers.static ? 'static ' : '') + (modifiers.virtual ? 'virtual ' : '') + (modifiers.abstract ? 'abstract ' : '') + func.member; if (func.isSealed) newFn.isOverride = func.isOverride = true; newFn.metadata = func.metadata = {}; if( M.Class.fullname == 'core.env:PerformanceMng' || func.name == 'supercall' || func.name == 'protocall' ) { func.Class.ownMethods[func.name] = func ; $DOM._addMember(func); }else{ func.Class.ownMethods[func.name] = newFn ; $DOM._addMember(newFn); } } catch (e) { $DOM.raiseDOMError(e.description); } } $DOM.addMethodRegular = function(name, func, modifiers) { try { if (name.search(/\b(constructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid method name'); var M=$DOM._splitName(name); if (!M) throw new Error(-1, '"'+name+'" is not a valid method name'); func.member = 'method'; func.name = (M.Class.isAspect ? M.Class.dollar+'$' : '') + M.Name; func.Class = M.Class; func.isMethod = true; if(!modifiers) modifiers = {}; // Methods by default are public if (!modifiers.public && !modifiers.private && !modifiers.protected) modifiers.public = true; for (var k in modifiers) func['is'+PREF(k)+k.substring(1)] = true; func.member = (modifiers.public ? 'public ' : '') + (modifiers.private ? 'private ' : '') + (modifiers.protected ? 'protected ' : '') + (modifiers.static ? 'static ' : '') + (modifiers.virtual ? 'virtual ' : '') + (modifiers.abstract ? 'abstract ' : '') + func.member; if (func.isSealed) func.isOverride = true; func.metadata = {}; func.Class.ownMethods[func.name] = func; $DOM._addMember(func); } catch (e) { $DOM.raiseDOMError(e.description); } } $DOM.addMethod = $DOM.perfCheck ? $DOM.addMethodPerfCheck : $DOM.addMethodRegular; ////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// <@func name="addProperty"> Adds a property definition to the current declaration unit The property's name The property's data type (boolean|number|string|object|array|pointer|collection|vector). The property's default value. The property's modifiers {override, native, sealed, static, virtual, readonly} The property's expression type $DOM.addProperty = function(name, type, value, modifiers, exprType) { try { if (name.search(/\b(constructor|Constructor|Destructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid property name'); var N=$DOM._splitName(name); if (!N) throw new Error(-1, '"'+name+'" is not a valid property name'); var prop={}; prop.member = 'property'; prop.name = (N.Class.isAspect ? N.Class.dollar+'$' : '') + N.Name; prop.type = type; if(('undefined' != exprType) && (null != exprType)) { prop.exprType = exprType; } if ('p|v|c'.indexOf(type.charAt(0)) >= 0 ) { if (N.Class.isAspect) { if (!modifiers || !modifiers.virtual) { throw new Error(-1, '"'+name+'": non-virtual aspect pointer|vector|collection properties are not supported'); } } prop.typecheck = value; //this is a class name that we should make type checks against prop.value = null; } else { prop.value = value; } /* NadavL - every property has a 'Class' attribute, to indicate the class where the property was originated. This attribute should therefore point to the class that first defined this proeprty. What do we do then, when a class overrides its predecessor's property? This depends on the nature of the change - is it 'minor' or 'radical'? If it's minor, the property should still point to the corresponding ancestor. If it's radical, the property should point to the new class (the class we are currently creating). The only 'radical' change we identified so far, is if the property's 'type' is modified. There is one exception - meaning an override with no 'type' change, yet we still set the 'Class' to point to the new class. This exception is the 'Class' property - this is because the 'Class' property has a special semantic meaning. Note: All this is valid for properties coming from the 'inherit' parent. Properties coming from the 'implement' parents, always point to the new class. */ prop.Class = N.Class; // By default, the property points to the new class // Now, check if this is the one exception - property is not 'Class', it's overriden, and its type is the same as in the ancestor if (name != 'Class' && modifiers && modifiers['override']) { // use name because prop.name may contain 'dollar' var olddef = N.Class.properties[prop.name]; // olddef is this property's def in the 'inherit' parent (or undefined if overriding 'implement' parent) if (olddef && olddef.type == prop.type)// is it a minor change? (meaning same 'type') prop.Class = olddef.Class; // take ancestor's Class } prop.isProperty = true; if(!modifiers) modifiers = {}; // If no scope specified for the property, if it is virtual, set scope to // private, otherwise set to public if (!modifiers.public && !modifiers.private && !modifiers.protected) { if (modifiers.virtual) modifiers.private = true; else modifiers.public = true; } for (var k in modifiers) prop['is'+PREF(k)+k.substring(1)] = true; prop.member = (modifiers.public ? 'public ' : '') + (modifiers.private ? 'private ' : '') + (modifiers.protected ? 'protected ' : '') + (modifiers.static ? 'static ' : '') + (modifiers.virtual ? 'virtual ' : '') + (modifiers.readonly ? 'readonly ' : '') + (modifiers.strong ? 'strong ' : '') + prop.member; if (prop.isSealed) prop.isOverride = true; prop.metadata = {}; // prop.Class.ownProperties[prop.name] = prop; // NadavL - prop.Class can now point to an ancestor class ==> use the following instead: N.Class.ownProperties[prop.name] = prop; $DOM._addMember(prop); } catch (e) { $DOM.raiseDOMError(e.description); } } <@func name="addMetadata"> Adds a metadata definition to the current declaration unit The metadata name The metadata value $DOM.addMetadata = function(name, value) { try { if (name.search(/\b(constructor|Constructor|Destructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid metadata name'); var N=$DOM._splitName(name); if (!N) throw new Error(-1, '"'+name+'" is not a valid property name'); N.Class.metadata[N.Name] = value; } catch (e) { $DOM.raiseDOMError(e.description); } } <@func name="addMetadataFor"> Adds a member metadata definition to the current declaration unit The member name The metadata value The metadata modifiers {override} $DOM.addMetadataFor = function(name, value, modifiers) { try { if (name.search(/\b(constructor|Constructor|Destructor|prototype|class|aspect)\b/) == 0) { throw new Error(-1, '"'+name+'" is not a valid member name'); } var N=$DOM._splitName(name); if (!N) throw new Error(-1, '"'+name+'" is not a valid member name'); var classObj = N.Class; var name = (N.Class.isAspect ? N.Class.dollar+'$' : '') + N.Name; if ('override' in modifiers && !classObj.ownProperties[name]) { // If the metadata was overriden, but the property was not, override the propetry var prop = classObj.properties[name]; $DOM._addOverrideProperty(N.Name, prop); } var member = classObj.ownProperties[name]; // check if the member is a property if (!member) { member = classObj.prototype[name]; // check if the member is a method if (!member) { member = classObj.ownEvents[name]; // check if the member is an event if (!member) throw new Error(-1, '"'+name+'" is not a valid member name'); } } if (ISARRAY(value)) { for (var k=0; k Adds an event to the currrent declaration unit The event name The event parameters list $DOM.addEvent = function(name, params,modifiers) { try { if (name.search(/\b(constructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid event name'); var E=$DOM._splitName(name); if (!E) throw new Error(-1, '"'+name+'" is not a valid event name'); if (!$ENV.isValidId(name)) { return $ENV.raiseKitError('Invalid namespace specification of event name: "'+ name + '" in kit :' + E.NS.$name ); } var evt=[], type=LOWER(name); for (var i=0, len=params.length; i all attached aspects; Aspect -> all base classes; Prototype -> all implementors (classes/aspects} thisClass.metadata = {}; thisClass.ownEvents = {}; thisClass.ownMethods = {}; thisClass.ownRelations = {}; thisClass.isValid = true; thisClass.isa = $DOM._isaClass; thisClass.getProperty = $DOM._getClassProp Namespace[bareName] = thisClass; $DOM.currClass = thisClass; thisClass['is'+classType] = true; for (var k in thisClass.prototype) { delete thisClass.prototype[k]; } // Setup the classifier inheritance chain thisClass.precedents[className] = thisClass; if (typeof superClass == 'function') { if (!(thisClass.fullname in superClass.subclasses)) superClass.subcount++; superClass.subclasses[thisClass.fullname] = thisClass; for (var k in superClass.precedents) thisClass.precedents[k] = superClass.precedents[k]; $DOM._mergeMembers(thisClass, superClass); } // Complete the classifier and return thisClass.prototype.constructor = thisClass; $DOM.addProperty('Class', 'object', thisClass, {static:true, virtual:true, readonly:true, override:true}); return thisClass; } // INTERNAL: Ends a declaration unit $DOM.endDeclaration = function() { var classObj=$DOM.currClass; if (classObj && classObj.inheritCstors) { var A=[], C=classObj.inheritCstors; for (var k in C) { var insert = (C[k].isPrototype ? 'unshift' : 'push'); A[insert]('$DOM.allClasses["'+k+'"].prototype.Constructor.apply(this, arguments);'); } var func = new Function(JOIN(A,'\n')); func.member = 'method'; func.name = 'Constructor'; func.Class = classObj; func.isMethod = true; func.isOverride = true; classObj.prototype.Constructor = func; delete classObj.inheritCstors; } //check abstract. loop on all methods, if any is abstract - mark class as abstract for (var name in classObj.prototype) { var def = classObj.prototype[name]; if (!def) continue; if (def.isMethod && def.isAbstract) { classObj.isAbstract = true; //$DOM.raiseDOMError("Class " + classObj.fullname + ' is abstract due to unimplemented method ' + name ); break; } } //end this declaration unit $DOM.currClass = null; } // INTERNAL: appends a new member (method or property) to a declaration unit $DOM._addMember = function(newdef) { // var classObj = newdef.Class; // NadavL - newdef can now point to an ancestor class ==> use the following instead: var classObj = $DOM.currClass; var newname = newdef.name; var olddef = classObj.properties[newname] || classObj.prototype[newname] || null; if (olddef && olddef.isMethod && olddef.isAbstract) { if (newdef.isAbstract) { var errmsg = 'Abstract method ' + newname + ' definition in class ' + olddef.Class.name + ' was redefined in class ' + classObj.name; throw new Error(-1, errmsg); } if (!newdef.isOverride) { var errmsg = 'Abstract method ' + newname + ' inherited from class ' + olddef.Class.name + " must be overriden using the 'override' keyword in class " + classObj.name; throw new Error(-1, errmsg); } } if (olddef && ((olddef.isMethod && !olddef.isAbstract) || olddef.isProperty || olddef.isEvent)) { // check for duplicate members if (olddef.Class === classObj && !olddef.isNative) { throw new Error(-1, 'Encountered a duplicate definition for member "'+newname+'".'); } // check for incompatible or missing members override if (olddef.member != newdef.member || !newdef.isOverride) { var errmsg = 'A '+olddef.member+' "'+newname+'" has already been inherited from "'+olddef.Class.name+'". '; if (olddef.member != newdef.member) { errmsg += 'This definition cannot be overridden into a '+newdef.member; } else { errmsg += 'You need to use the override keyword, or to supply a different name.' } throw new Error(-1, errmsg); } // check for sealed members override if (olddef.isSealed) { throw new Error(-1, 'Cannot override member "'+newname+'" since it has been sealed by "'+olddef.Class.name+'".'); } } if (newdef.isProperty){ classObj.properties[newname] = newdef; classObj.prototype[newname] = newdef.value; // Check the metadata of overidden properties: if the metadata statement was // not overriden, inherit the metadata if (newdef.isOverride && ISEMPTY(newdef.metadada) && olddef) { if (!ISEMPTY(olddef.metadata) && !newdef.exprType) { // don't inherit the metadata if the property is an expression newdef.metadata = CLONE(olddef.metadata, true) } } } else { classObj.prototype[newname] = newdef; } } // INTERNAL: merges the members of a source declaration unit into a target declaration unit $DOM._mergeMembers = function(targetObj, sourceObj) { for (var name in sourceObj.prototype) { var newdef = sourceObj.properties[name] || sourceObj.prototype[name] || null; var olddef = targetObj.properties[name] || targetObj.prototype[name] || null; var newcstor = newdef && newdef.Class || null; var oldcstor = olddef && olddef.Class || null; if (olddef) { if (newdef && newdef.isAbstract) { if (olddef.isAbstract) { var errmsg = 'Abstract method ' + name + ' definition in class ' + newdef.Class.name + ' was redefined in class ' + olddef.Class.name; throw new Error(-1, errmsg); } if (!olddef.isOverride) { var errmsg = 'Abstract method ' + name + ' inherited from class ' + newdef.Class.name+ " must be overriden using the 'override' keyword in class " + olddef.Class.name; throw new Error(-1, errmsg); } } else { if (oldcstor === targetObj) { // check for incompatible or missing members override if (olddef.member != newdef.member || !olddef.isOverride) { var errmsg = 'A '+newdef.member+' "'+name+'" has already been inherited from '+LOWER(newcstor.type)+' "'+newcstor.name+'". '; if (olddef.member != newdef.member) { errmsg += 'This definition cannot be overridden into a '+olddef.member+'.'; } else { errmsg += 'You need to use the override keyword, or to supply a different name.' } throw new Error(-1, errmsg); } // check for sealed members override if (newdef.isSealed) { throw new Error(-1, 'Cannot override member "'+name+'" since it has been sealed by "'+newcstor.name+'"'); } } else { // check for ambigous inheritance if (name == 'Constructor') { if (!targetObj.inheritCstors) targetObj.inheritCstors={}; targetObj.inheritCstors[newcstor.fullname] = newcstor; targetObj.inheritCstors[oldcstor.fullname] = oldcstor; } else if (name != 'toString') { // IE8 includes 'toString' in each object's prototype, while IE6/7 - doesn't. The IF above elliminates throwing in IE8 throw new Error(-1, 'Member "'+name+'" is ambiguously inherited from '+LOWER(newcstor.type)+' "'+newcstor.name+'" and '+LOWER(oldcstor.type)+' "'+oldcstor.name+'". Override the member definition to resolve the ambiguity. '); } } } } else { if (newdef.isProperty){ targetObj.properties[name] = newdef; targetObj.prototype[name] = sourceObj.prototype[name] || newdef.value; } else { targetObj.prototype[name] = newdef; } } } for (var name in sourceObj.metadata) { if (targetObj.metadata[name] == null) targetObj.metadata[name] = sourceObj.metadata[name]; } } // INTERNAL: Declares the start of a source file $DOM.fileStart = function(package, fileName) { try { var ns = $DOM.namespaces; var pkgName = (typeof package == 'object' ? package.name : package); if (fileName && fileName.indexOf(':') < 0) fileName = pkgName+':'+fileName; $DOM.currNS = ns[pkgName]; $DOM.currFile = fileName || null; $DOM.currClass = null; if ($DOM.currNS) return; // this is the first time the namespace is encountered, so add it to the namespaces table var pref=''; if (pkgName == 'core.env') { // use the builtin prefix (the kits manifest is not available at this stage) pref = 'env'; } else { // use the kits manifest to obtain the correct namespace prefix for the current server. try { var mft=$ENV.channel1.getKitsManifest(); pref = mft.packages[pkgName].prefix; } catch(e) { throw new Error(-1, 'Unrecognized package "'+pkgName+'"'); } } $DOM.currNS = {$name:pkgName, $ver:package.version, $pref:pref}; $DOM.namespaces[pkgName] = $DOM.currNS; $DOM.prefixes[pref] = pkgName; } catch (e) { var err = e.description; try { $ENV.raiseKitError(err); } catch(e) { alert(err); } } } // INTERNAL: Declares the end of a source file $DOM.fileEnd = function() { $DOM.currNS = null; $DOM.currFile = null; $DOM.currClass = null; } //////////////////////////////////////////////////////////////////////// // UTILITIES <@func name="getAspectRole"> Gets a specified aspect role Gets the aspect role by name The qualified aspect role name Gets the aspect role by its properties prefix The aspect dollar qualifier The requested aspect role $DOM.getAspectRole = function(roleName) { return $DOM.allRoles[roleName] || null; } <@func name="getAspect$Name"> Gets the dollar-qualified member name for a specified aspect role The qualified aspect role name The bare member name The prefixed member name $DOM.getAspect$Name = function(roleName, propName) { var R=$DOM.allRoles[roleName]; return (R ? R.dollar+'$' : '') + propName; } <@func name="toQName"> Converts a given name to a qualified classifier name The classifier name to qualify The qualified classifier name $DOM.toQName = function(qname) { if (qname in $DOM.allClasses) return qname; qname = qname+''; if (qname.indexOf(':') < 0) { qname = 'core.gml:'+qname; } else if (qname.indexOf('.') < 0) { qname = 'core.'+qname; } return (qname in $DOM.allClasses) ? qname : ''; } // INTERNAL: gets a specified classifier (class, prototype, or aspect) $DOM._getClassifier = function(className) { if (!className) return $DOM.currClass || null; if (typeof className == 'object') { if (className.isClass || className.isAspect) return className; return className.Class; } if (className in $DOM.allClasses) return $DOM.allClasses[className]; var k=className.indexOf(':'), ns=className.substring(0,k), cls=className.substring(k+1); var nsobj = ns && ($DOM.namespaces[ns] || $DOM.namespaces[$DOM.sysAliases[ns]]) || $DOM.currNS || $DOM.namespaces[$DOM.prefixes[ns]] || null; if (cls.indexOf('.') >= 0) cls=cls.substring(0,cls.indexOf('.')); return nsobj && nsobj[cls] || null; } // INTERNAL: parses a given member name and returns its composite parts $DOM._splitName = function(name) { if (!name) return null; var k=name.indexOf(':'), ns=name.substring(0,k), mbr=name.substring(k+1); var k=mbr.indexOf('.'), cls=mbr.substring(0,k), mbr=mbr.substring(k+1)||''; var nsobj = ns && ($DOM.namespaces[ns] || $DOM.namespaces[$DOM.sysAliases[ns]]) || $DOM.currNS || $DOM.namespaces[$DOM.prefixes[ns]] || null; if (!nsobj) return null; var cstor = cls && nsobj[cls] || $DOM.currClass || null; if (!cstor || !(cstor.name in nsobj)) return null; return {NS:nsobj, Class:cstor, Name:mbr}; } // INTERNAL: raises a DOM parsing error $DOM.raiseDOMError = function(error) { var file=$DOM.currFile || ''; if (file) file = ' [' + file.replace(/^.+?(\w+\.\w+)$/, '$1') + ']'; error = error.replace(/[\s\.]*$/,'.'); try { $ENV.raiseKitError(error+file); } catch(e) { alert(error+file); } } // INTERNAL: records a parsing error in the kit errors log $DOM.raiseKitError = function(error, where) { $ENV.raiseKitError(error, where); } // INTERNAL: registers a kit configuration function $DOM.registerConfigFunc = function(namespace, sections, func, priority) { $ENV.registerConfigFunc(func, sections, namespace, $ENV.getCurrentKit(), $DOM.currFile, priority); } //////////////////////////////////////////////////////////////////////// // NATIVE OBJECT METHODS // INTERNAL: adds a native method $DOM._addNativeMethod = function(classObj, name, func , modifiers) { func.member = 'method'; if(modifiers && modifiers.public) func.member = 'public ' + func.member; else func.member = 'private ' + func.member; func.name = name; func.isMethod = true; func.isNative = true; func.Class = classObj; classObj.prototype[name] = func; } // INTERNAL: native class constructor $DOM._cstorClass = function(values) { var cstor=this.constructor, I=cstor.instantiators; if (I) { for (var k in I) { var val = cstor.properties[k].value; if( ISEMPTY(val) ){ this[k] = new I[k](); }else{ this[k] = CLONE(val,true); } } } if (this.Constructor) { this.Constructor.apply(this, arguments); } else if (values) { for (var k in values) this[k] = values[k]; } } // INTERNAL: native GML class constructor $DOM._cstorGmlClass = function(values) { var cstor=this.constructor, I=cstor.instantiators; if (I) { for (var k in I) { var val = cstor.properties[k].value; if( ISEMPTY(val) ){ this[k] = new I[k](); }else{ this[k] = CLONE(val,true); } } } if (!values) return; for (var k in values) this[k] = values[k]; // actual constructor will be invoked only at the completeDiagram stage } // INTERNAL: native aspect constructor $DOM._cstorAspect = function() { throw new Error(-1, this.constructor.name + ' cannot be instantiated'); } // INTERNAL: native prototype constructor $DOM._cstorPrototype = function() { throw new Error(-1, this.constructor.name + ' cannot be instantiated'); } // INTERNAL: native class property getter $DOM._getClassProp = function(name) { var P=this.properties[name]; return (P && P.isProperty && P.isStatic) ? P.value : null; } // INTERNAL: native instance property getter $DOM._getInstanceProp = function(name) { if (this.hasOwnProperty(name)) return this[name]; var P=this.Class.properties[name]; return (P && P.isProperty) ? P.value : null; } // INTERNAL: native instance property setter $DOM._setInstanceProp = function(name, value) { var C=this.Class, P=C.properties[name]; if (!P || P.isReadonly) return this; if (P.isStatic) { C.prototype[name] = value; return this; } if (value === undefined) delete this[name]; else this[name] = value; return this; } // INTERNAL: native subclassof operator $DOM._isaClass = function() { var P = this.precedents; if (arguments[0] in P) return true; // fast check the most common case for (var i=0, len=arguments.length; i