<@doc alias="model" hierarchy="GMLDOM"> Encapsulates the snapshot of the model currently held in memory. Instances of this class are created automatically by the system, and should not be created directly. To access the Model object that represents the currently open model use the $ENV.model property. (c) SAP AG 2003-2006. All rights reserved. /////////////////////////////////////////////////////////////////////// // CLASS HEADER Class Model inherit Object; <@doc scope="private"> Model constructor. The model information object The channel for reading/writing the model constructor(info, channel) this.name = info.modelName.replace(/0x([0-9][a-f])/gim,function(g1, g2) {return unescape('%'+g2);}); this.urn = info.modelUrn; this.status = info.modelStatus; this.channel = channel; this.isReadonly = true; this.isDTRModel = info.isDTRModel; end <@doc scope="private"> Initializes the model object method initialize() this.serializer = $ENV.createObject('core.gml:ModelSerializer', this); this.organizer = $ENV.createObject('core.gml:ModelOrganizer', this); this.recorder = $ENV.createObject('core.gml:SimpleModelRecorder', this); this.clipBoard = $ENV.createObject('core.gml:Clipboard'); var data = this.getModelData(); this.initFromProps(data.properties); this.readModelIndex(data.index); this.context = $ENV.createObject('core.gml:ModelContext', this); this.propagator = $ENV.createObject('core.gml:ChangeManager', this); this.transporter = $ENV.createObject('core.env:ExportImportMgr', this.channel); this.dynexp = $ENV.createObject('core.dev:DynExpParserPtn'); this.propsOrganizer = $ENV.createObject('core.dev:PropsOrganizer'); this.masterLanguage = GETVAR('masterLanguage'); end method getModelData() var data ={properties:null, index:null}; try { var response = this.channel.executeService("LibManager", "actionModelData", [this.urn], null, null); if(!response) //err get model details throw new Error(-1, this.channel.error + '(actionModelData service)'); data.properties = response.childNodes[0]; //model properties var node = response.childNodes[1]; node = node && node.selectSingleNode('FILE'); node = node && node.firstChild; data.index = node; //unit list } catch(e) { #LOG[4, 'Failed to retrieve model data: ' + this.urn + '. ' + e.description]; throw new Error(-1, '#TEXT[XMSG_BAD_MODEL_INDEX]'); } return data; end method initFromProps(props) var prop = props && props.getAttribute('read-only'); if(!prop) { $CTL.console.log("core.gml", "(Model:0)", 4, 'Failed to retrieve model properties. Model is set to Read Only mode.');; return; } this.isReadonly = BOOL(prop); this.isTranslationFreeze = #SYS[SUPPORT_TRANSLATION] && BOOL(props.getAttribute('translationFreeze')); this.isPersonalizationFreeze = BOOL(props.getAttribute('personalizationFreeze')); end <@doc scope="private"> Finalizes the model object method finalize() if (this.context) this.context.clear(); delete this.serializer; this.organizer.finalize(); delete this.organizer; delete this.context; delete this.recorder; delete this.propagator; delete this.transporter; delete this.dynexp; delete this.propsOrganizer; delete this.clipBoard; var C = this.units; for( var id in C){ var u = C[id] ; for( var prop in u){ delete u[prop]; } C[id].prototype = null; delete C[id]; } this.units = {}; this.root = null; this.reusables = null; this.infoshape_pool = null; end /////////////////////////////////////////////////////////////////////// // PROPERTIES <@doc>Gets the channel for reading/writing the model readonly property channel = ^env:GkbChannel; <@doc scope="private">Gets the collection of all cached units readonly property units = ^Unit[id]; <@doc>Indicates whether the model is dirty readonly property dirty = false; <@doc>Gets the model dynamic expressions parser readonly property dynexp = ^DynExpParser; <@doc scope="private">Gets the last allocated Id readonly property lastId = ''; <@doc scope="private">Gets the selected master language property masterLanguage = 'en'; <@doc>Gets the model's name readonly property name = ''; <@doc>Gets the model organizer readonly property organizer = ^ModelOrganizer; <@doc>Gets the model change propagator readonly property propagator = ^ChangeManager; <@doc>Gets the model recorder readonly property recorder = ^ModelRecorder; <@doc>Gets the model's root unit readonly property root = ^Module; <@doc>Gets the model's reusables unit readonly property reusables = ^Module; <@doc>Gets the model serializer readonly property serializer = ^ModelSerializer; <@doc>Gets the model transporter readonly property transporter = ^env:ExportImportMgr; <@doc>Gets the properties organizer readonly property propsOrganizer = ^dev:PropsOrganizer; <@doc>Gets the model's infoshape pool unit readonly property infoshape_pool = ^InfoshapePool; <@doc>Gets the model's urn readonly property urn = ''; <@doc>Gets the model's status readonly property status = ''; <@doc scope="private">Gets the unit classifier readonly property unitClassifier = 'core.gml:Unit'; <@doc scope="private">Gets the element classifier readonly property elementClassifier = 'core.gml:Element'; <@doc scope="private">Gets the field classifier readonly property fieldClassifier = 'core.gml:Field'; <@doc>True if the model can be enabled for modifications. For example, if model requires migration, isSafeToModify = false virtual property isSafeToModify = false; <@doc>True if the model is a DTR model, false otherwise (up to EhP1, otherwsie means it is a local model) virtual property isDTRModel = true; <@doc>List of "confirm once" messages valid only for this "model session" (until model is closed) virtual property blockedMessages = {}; <@doc>Clipboard for cut pasting elements virtual property clipBoard = ^gml:Clipboard; <@doc>whether the model is in translation freeze mode virtual property isTranslationFreeze = false; <@doc>whether the model is in personalization freeze mode virtual property isPersonalizationFreeze = false; /////////////////////////////////////////////////////////////////////// // PUBLIC METHODS <@method name="clearClipboard" > Clear the clipboard for the element types specified by the parameter key. If no key is sepcified, clean the whole clipboard (delete all types) The specific clipboard key method clearClipboard(key) this.clipBoard.clear(key); end <@method name="getClipboard" > The specific clipboard key The requested object or ~null if not found If no parameters, return null method getClipboard(key) if(!key) return null; return this.clipBoard.getProperty(key); end <@method name="setClipboard" > The specific clipboard key The content to keep method setClipboard(key, content) if(!key) return; this.clipBoard.setProperty(key, content); end <@doc> Creates a new model unit The unit class name A collection of property values for initializing the new unit The newly created unit, or ~null in case of any error method createUnit(className, values) var unitobj = this.createRawUnit(className, values); if (!unitobj) return null; unitobj = this.initRawUnit(unitobj); return unitobj; end <@doc scope="private"> Creates a raw model unit. This means that the newely object would be registered in the model, but not initialized (its 'onCreate' function will not be called) The unit class name A collection of property values for initializing the new unit The newly created raw unit, or ~null in case of any error method createRawUnit(className, values) try { if (!values) values = {}; values.model = this; values.id = this.newId(); var unitobj = $ENV.createRawObject(className, values); if (!unitobj) throw new Error(-1, '#TEXT[XMSG_FAILED_TO_CREATE]'+ ' '+className+' ' +'raw unit'); this.units[unitobj.id] = unitobj; unitobj.setDirty(); return unitobj; } catch(e) { WRITE(e.description); return null; } end <@doc scope="private"> Initializes a raw unit by calling its 'onCreate' function. The unit object sent to this function must be created using the 'createRawUnit' function. A unit object which was created using the 'createRawUnit' function The initialized unit, or ~null in case of any error method initRawUnit(unitobj) try { if (!unitobj) throw new Error(-1, '#TEXT[XMSG_FAILED_TO_INIT_RAW]'); if (unitobj.onCreate) unitobj.onCreate(); unitobj.setDirty(); return unitobj; } catch(e) { WRITE(e.description); return null; } end <@doc> Creates a new model element The element class name A collection of property values for initializing the new element The newly created element, or ~null in case of any error

The element created by this method is not inserted into the model. Use the GmlObject's @gml:TransObject!insertElement method to insert the new element into a parent object. Or, use the GmlObject's @gml:TransObject!createElement method to create an element and insert it into a parent object in a single step.

TIP: Prefer using this method to create composite elements. When creating a composite element, it is more efficient to create the element and its sub-elements off the model, before inserting the composite element to the model.

method createElement(className, values) if (!values) values = {}; values.id = this.newId(); return $ENV.createObject(className, values); end <@doc> Clones a given collection of elements The collection of elements to clone A collection that maps the 'old unit's children ID to the new unit's children ID Returns the collection of cloned elements Any inter-references that exist among the given collection of elements are reflected in the the cloned elements. All other object references are removed in the cloned elements. method cloneElements(elements,force,elementsMap) var os = $ENV.createObject("core.gml:ObjectSet", elements); return os.clone(force,elementsMap); end <@doc> Gets a specified unit, loading it if needed The id of the unit to get The requested unit, or ~null if not found Use the @env:BaseChannel!error property of the model's channel object to check for any errors method getUnit(id) if (!id) return null; if (typeof id != 'string') return (ISA(id, 'core.gml:Unit') ? id : null); if (!(id in this.units)) { var gml = null; var unitobj = null; if ( this.channel.isJ2EE() ) { // Preprocessing units in the server. // The following is commented. uncomment when you want to activate preprocessing in the server. // and remove the 2 lines after these lines. gml = this.readFile(id+'.gml'); unitobj = gml && $ENV.serializer.deserializeJSUnit(gml,id) || null; // The following 2 lines should be removed when activating preprocessing in the server. //gml = this.readXMLFile(id+'.gml'); //unitobj = gml && $ENV.serializer.deserializeUnit(gml) || null; }else{// Preprocessing of units in the server is not implemented in IIS. // Reading gml in xml format gml = this.readXMLFile(id+'.gml'); unitobj = gml && $ENV.serializer.deserializeUnit(gml) || null; } if (!unitobj) return null; this.units[id] = unitobj; $ENV.fireUnitRead(id, unitobj !== null); } return this.units[id]; end <@doc> Gets units, loading them if needed Array of ids of the units to get Array of requested units, or ~null if not found Use the @env:BaseChannel!error property of the model's channel object to check for any errors method getRootUnits(root_ids) if (!root_ids) return null; var ret_arr= new Array(); var temp_id_arr = new Array(); for (var id in root_ids) temp_id_arr[id]=root_ids[id]+'.gml'; var gml_arr = this.readFiles(temp_id_arr); var unitobj = null; for (var gml in gml_arr) { unitobj = gml && $ENV.serializer.deserializeJSUnit(gml_arr[gml],root_ids[gml]) || null; this.units[root_ids[gml]] = unitobj; $ENV.fireUnitRead(root_ids[gml], unitobj !== null); ret_arr[root_ids[gml]]=unitobj; } return ret_arr; end <@doc scope="private"> Checks if a unit is loaded The unit Id Retrurns true ~iff the unit is loaded method isUnitLoaded(id) if (id in this.units) return true; return false; end <@doc scope="private"> Checks if a a unit has been deleted. The unit object Retrurns true ~iff the unit has been deleted. IMPORTENT NOTE: Use this method only after $ENV.organizer.updateIndex() has been executed. method isUnitDeleted(unit) var model=this, organizer=model.organizer; var index=organizer.getIndex(); var unitNode = index[unit.id] ; var unit = null; if(!unitNode) return false; if( unitNode.unused || unit.deleted) return true; return false; end <@doc scope="private"> Checks if a element has been deleted. The element object Retrurns true ~iff the unit has been deleted. IMPORTENT NOTE: Use this method only after $ENV.organizer.updateIndex() has been executed. method isElementDeleted(element) var model=this, organizer=model.organizer; var index = organizer.getIndex(); var parent = element.parent; var child = element; while( parent){ child = parent; parent = parent.parent; } if(ISA(child, 'core.gml:Unit') ) { if (!child || !child.id || !index[child.id]) debugger; if(index[child.id].unused || child.deleted) return true; return false; } return true; end <@doc> Gets a specified element The unit Id The element Id (within the unit) The concatenated unit and element Ids, in the form: ~unitId.elemId The requested element, or ~null if not found Use the @env:BaseChannel!error property of the model's channel object to check for any errors method getElement(unitId, elemId) if (!elemId) { if (typeof unitId != 'string') return (ISA(unitId, 'core.gml:Element') ? unitId : null); var k=(unitId+'').indexOf('.'); if (k <= 0) return null; elemId = unitId.substring(k+1); unitId = unitId.substring(0,k); } var unitobj = this.getUnit(unitId); return unitobj && unitobj.getElement(elemId, true) || null; // call GMLObject.getElement for deep search end <@doc> Saves all changes to the model to a new location Indicates whether to wait for the save operation to complete before returning Indicates whether to perform a silent save. this is true today only in ModelOrganizer.gs->parseIndex The new location to save to, in case the model is being "Saved As..." Returns ~true iff the model was saved successfully; otherwise, returns ~false method saveAs(wait, silent, location) // copy model file to newurn if (!this.channel.online) return; var newurn = location; if (!newurn) return; var oldurn = this.urn; var aDTRModel = this.isDTRModel || (this.status == 'Added To DTR'); //"Added to DTR" is currently not considered a 'DTR model', but requires a copyToLocal if (aDTRModel) { success = this.channel.copyToLocal(oldurn, newurn, true);//if (success) ... } else { success = this.channel.copyFolder(oldurn, newurn);//if (success) ... } // change model.urn to newurn this.urn = newurn; // update model parameters to reflect new model status. //TODO: is there a cleaner way to to this? this.isReadonly=false; this.status="Local"; this.save(true,false); // reload the model to remove any dirty references var mhandle = this.channel.toModelHandle(this.urn, this.status, false); $ENV.openModel(mhandle); end <@doc> Saves all changes to the model Indicates whether to wait for the save operation to complete before returning Indicates whether to perform a silent save. this is true today only in ModelOrganizer.gs->parseIndex Returns ~true iff the model was saved successfully; otherwise, returns ~false method save(wait, silent) var files = []; var progHandle = silent ? null : PROGRESS_START('#TEXT[XMSG_SAVING_MODEL]'); if (progHandle) PROGRESS_UPDATE(progHandle, this.name, 0); var model=this, channel=model.channel, organizer=$ENV.organizer; var bReusablesModel = false; if (!this.dirty || !this.isEditable()) { if (!silent && progHandle) PROGRESS_FINISH(progHandle, '#TEXT[XMSG_DONE_SAVING]'); return true; } this.clearClipboard(); // update and print index organizer.updateIndex(); files['_index.xml'] = {isUnit:false, text:organizer.printIndex()}; // remove unused units and get remaining dirty units var list=[], index=organizer.index; for (var id in index) { if (index[id].unused) { var unitobj=model.getUnit(id); // make sure the unit is read before deleting it, so it can later be undeleted if (!unitobj) { var a='#TEXT[XMSG_FAILED_TO_DELETE]'; #LOG[4, a+ ' ' +id+': '+channel.error]; if (!silent && progHandle) PROGRESS_FINISH(progHandle, '#TEXT[XMSG_FAILED_SAVING]'); return false; } files[id+'.gml'] = {isUnit:true, text:''}; } else { var unitobj=model.units[id]; if (unitobj && (unitobj.dirty || unitobj.deleted)) list.push(unitobj); try { //if the model contains a GlobalReusables unit, then mark it as a containing reusables model if(!bReusablesModel) { //if the unit is of type GlobalReusables - set the flag if(ISA(unitobj, 'gml:GlobalReusables') ) bReusablesModel = true; //if the unit is null (then the previous ISA didn't work) - create class obj for checking if it's of type GlobalReusables else if(!unitobj) { var obj = {}; obj.Class = $ENV.getClass(index[id].type); if(ISA(obj, 'gml:GlobalReusables')) bReusablesModel = true; } } } catch(e){ //if fails to set bReusablesModel - continue } } } // save dirty units synchronously or asynchronously var len=list.length, pos=0; if (wait || len < 5) { var unit = null; while (pos < len){ unit = list[pos++]; saveUnit(unit); } try { saveModelFiles(); } catch(e) { saveFailed(e.description); return false; } saveSucceeded(); return true; } else { // PROMPT('#TEXT[XMSG_SAVING]'); setTimeout(saveNextUnit, 0); } function saveUnit(unitobj) { var id = unitobj.id; var gml = $ENV.serializer.serializeUnit(unitobj, false); files[id+'.gml'] = {isUnit:true, text:gml}; } function saveNextUnit() { if (pos >= len) { try { setTimeout(saveModelFiles); } catch(e) { saveFailed(e.description); } setTimeout(saveSucceeded); return; } var unit = list[pos++]; saveUnit(unit); if (!silent && progHandle) PROGRESS_UPDATE(progHandle, this.name, '+'+90/len); setTimeout(saveNextUnit); } function saveSucceeded() { if (!silent && progHandle) PROGRESS_FINISH(progHandle, '#TEXT[XMSG_DONE_SAVING]'); // save of the model is done in a transaction so either all // of the model is saved or nothing is saved fireUnitSaveEvents(); model.clearDirty(); $ENV.fireModelSave(true); } function saveFailed(msg) { if (!silent && progHandle) PROGRESS_FINISH(progHandle, '#TEXT[XMSG_FAILED_SAVING]'); PROMPT('#TEXT[XMSG_FAILED_SAVING]', {icon:'ERROR'}); #LOG[4, msg]; $ENV.fireModelSave(false, msg); } function fireUnitSaveEvents() { for (var name in files) { if (files[name].isUnit) { var id = name.substring(0, name.lastIndexOf('.gml')); var text = files[name].text; var unitobj=model.getUnit(id); if ('' == text) { // unused unit unitobj.deleted = true; $ENV.fireUnitErase(id, true); } else { unitobj.deleted = false; // dirty unit $ENV.fireUnitWrite(id, true); } } } } function saveModelFiles() { if (!silent && progHandle) PROGRESS_UPDATE(progHandle, this.name, 90); var res = model.writeXMLFiles(files, bReusablesModel); if (!res) throw new Error (-1, channel.error); } end <@doc> Gets full object Id as a string, that can be used as a parameter of getElement method. GmlObject instance. Returns full object Id method getFullId(obj) return (obj) ? (obj.unit.id+'.'+obj.id) : ''; end <@doc> Gets an element Id The element Id The concatenated unit and element Ids, in the form: ~unitId.elemId The element to get the id from method getElementId(element) if(typeof element != 'string') return element.id; var A = this.parseFullId(element); return A ? A[1] : element; // return only the element id. end <@doc> Parse the given full ID Full id of an element return an Array in which the first item is the unit ID and the second is the element ID method parseFullId(id) var A = SPLIT(id,'.'); if( !A || A.length !=2) return null; return A; end <@doc> Checks whether the given ID is a full id, i.e. it has the format ~unit-id.element-id id of an object. ~true if the id is a full id method isFullId(id) var k=(id+'').indexOf('.'); //TODO: additional verifications, that unit exists ?? return (k > 0); end <@doc>Gets whether the model is in readonly mode readonly property isReadonly = true; <@doc> Indicates whether the model is editable Returns ~true when the model is editable, and ~false when it is readonly method isEditable() //if (this.isReadonly == null) { // var ch=this.channel; // if (!ch.online) return true; // this.isReadonly = ($ENV.kitsConfig.readonly== "true") ? true : false; //} return !this.isReadonly; end <@doc> Indicates whether the model is enabled - so the editing UI can be enabled. This for the new mode of checked in model that can be edited. Returns ~true when the model is enabled, and ~false otherwise method isEnabled() return(this.isEditable() || this.isSafeToModify); end <@doc> Sets the model dirty flag and raises a notification event method setIsSafeToModify(alert) this.isSafeToModify = !this.requiresMigration(); if (alert && !this.isEnabled()) WRITE("#TEXT[XMSG_MODEL_UNSAFE_DSABLD]"); end /////////////////////////////////////////////////////////////////////// // PRIVATE METHODS <@doc scope="privte"> copied from Patterns - "com.sap.tc.uip.dt.fw:VclModelToolsHelper.requiresMigration()" Checks whether the current model requires migration in order to be compliant with the current ESI kit. The check works as follows: a model is compliant iff (the systemName property is set on the infoshape pool, or there is no bo infoshape in the model) A model requires migration iff (the model is not compliant (cf. above) and the model is editable) method requiresMigration() var pool = this.infoshape_pool; if (COUNT(pool.elements)>0 && !pool.systemName) { for (var i in pool.elements) { var element = pool.elements[i]; if (element.isa('core.gml:BOInfoshapeUsage')) { // stop at first infoshape usage. because this means that there is at least one bo infoshape in the model return(true); break; } } } return(false); end <@doc scope="private"> Reads the model index, if index file doesn't exist then initialize the model. method readModelIndex(index) try { if (index) { $ENV.organizer.parseIndex(index); if($ENV.organizer.hasChildren(this.reusables)){ SETVAR('ReusableMode',true); } return true; } // if model index was not found but the model has units, then something is wrong // ==> this check is done in server 21/8/07 //var list = this.channel.listUnits(this.urn); //if (!list || list.length != 0) throw -1; // create index for a new model this.root = $ENV.createObject('core.gml:Module', {id:'A', name:CAPITAL(this.name)+' Model', model:this}); this.reusables = $ENV.createObject('core.gml:Reusables', {id:'B', name:'Component Library', model:this}); this.infoshape_pool = $ENV.createObject('core.gml:InfoshapePool', {id:'C', name:'Infoshape Pool', model:this}); this.lastId = 'C'; this.units['A'] = this.root; this.units['B'] = this.reusables; this.units['C'] = this.infoshape_pool; this.root.setDirty(); this.reusables.setDirty(); this.infoshape_pool.setDirty(); $ENV.organizer.addIndexNode(this.root.id,this.root.name,this.root.Class.fullname); $ENV.organizer.addIndexNode(this.reusables.id,this.reusables.name, this.reusables.Class.fullname); $ENV.organizer.addIndexNode(this.infoshape_pool.id,this.infoshape_pool.name, this.infoshape_pool.Class.fullname); $ENV.organizer.addIndexNode('B'); $ENV.organizer.addIndexNode('C'); this.save(); return true; } catch(e) { #TRACE[1, "readModelIndex: " + e.description]; throw new Error(-1, '#TEXT[XMSG_BAD_MODEL_INDEX]'); } end <@doc scope="private"> Reads a specified XML file under this model The name of the file to read Returns the XML file method readXMLFile(name) var ch=this.channel, urn=this.urn; return ch.readFile(ch.toModelFile(urn, name), true); end <@doc scope="private"> Reads a specified file under this model The name of the file to read Returns the string file method readFile(name) var ch=this.channel, urn=this.urn; return ch.readFile(ch.toModelFile(urn, name), false); end <@doc scope="private"> Reads files under this model The name of the files to read Returns the string files method readFiles(names) var ch=this.channel, urn=this.urn; var names_arr=new Array(); for (var name in names) names_arr[name]= ch.toModelFile(urn, names[name]); return ch.readFiles(names_arr, false)|| '' end <@doc scope="private"> Writes a specified XML file under this model The name of the file to write The XML object to write into the file Returns ~true if the file was written successfully; otherwise, returns ~false method writeXMLFile(name, xml) var ch=this.channel, urn=this.urn; return ch.writeFile(ch.toModelFile(urn, name), xml); end <@doc scope="private"> Writes a specified list of XML files under this model The list of files to write does the model contains reusable units Returns ~true if all of the files were written successfully; otherwise, returns ~false method writeXMLFiles(files, bReusablesModel) var ch=this.channel, modelUrn=this.urn; var list=[]; for (var name in files) { var modelFile = ch.toModelFile(modelUrn, name); list[modelFile] = files[name].text; } return ch.writeFiles(list, bReusablesModel); end <@doc scope="private"> Sets the model dirty flag and raises a notification event ~true iff the "dirty" was due to a setProperty of an svg property method setDirty(isSVGChange) if (!isSVGChange) this.clearClipboard(); // do not clear clipboard for inadvertent element movements if (this.dirty) return; var showMsg; var doSetDirty=true; //by default do the set dirty - see logic bellow for cases where setDirty is NOT done if (!this.isEditable()) { //model is probably DTR, and not checked-out by current user if (this.isEnabled()){ //model is safe for modicifcations (in EhP1 it means it was already migrated) showMsg = GETVAR('PromptReadonlyEnabledModel'); if (showMsg) msg = '#TEXT[XMSG_MDL_RDONLY_ENBLD_DIRTY]'; doSetDirty=true; //enabled model allows to set the dirty flag } else {//model is NOT safe for modicifcations (in EhP1 it means it was not yet migrated) showMsg = GETVAR('PromptReadonlyDisabledModel'); if (showMsg) msg = '#TEXT[XMSG_MDL_RDONLY_DSABLD_DIRTY]'; doSetDirty=false; //never set dirty flag for disabed model } if (!this.blockedMessages[msg]) { this.blockedMessages[msg] = true; var buttons = { '#TEXT[XBUT_OK]':2 } CONFIRM(msg, buttons); } } if (doSetDirty) { this.dirty = true; $ENV.fireModelDirty(true); } end <@doc scope="private"> Clears the model dirty flag and raises a notification event method clearDirty() if (!this.dirty) return; this.dirty = false; for (var k in this.units) { var d=this.units[k]; if (d) d.dirty = false; } $ENV.fireModelDirty(false); end <@doc scope="private"> Allocates a new identifier, unique within the scope of this model The new identifier method newId() this.lastId = LEX_NEXT(this.lastId); return this.lastId; end <@doc> Extends a rule by adding a new constrained behavior The rule Id The constraint definition method extendRule(id,def) return $ENV.extendRule(id,def); end <@doc name="defineRuleHelper"> Defines a rule helper function The rule Id The helper function name (should be unique within the scope of the rule). The function object Indicates whether the helper was successfully defined method defineRuleHelper(id, funcName, func) return $ENV.defineRuleHelper(id, funcName, func); end /////////////////////////////////////////////////////////////////////// // CONFIGURATION configure ENVIRONMENT $ENV.registerModelClass('core.gml:Model'); end