xb.core.stateful = { }; xb.core.stateful.prototype = { stir: function( ) { var object = this; var proxyMethod = function() { return this.state.run( arguments.callee, arguments ); } object.stateObject = { __isStateObject: true } var hasProtoStates = this.buildPrototypes( object.__proto__, object ); var methods = []; this.getAllStateMethods( methods, object, false ); if ( !hasProtoStates && methods.length === 0 ) { //console.log( "Class does not have any states; removing stateObject", methods, object ) delete object.stateObject; return false; } // hier worden de originele functies gesaved for ( var i = 0, il = methods.length; i < il; i++ ) { eval( "var proxyMethodCopy = " + proxyMethod.toString() ); var methodName = methods[ i ]; proxyMethodCopy.methodName = methodName; if ( object.hasOwnProperty( methodName ) ) { if ( typeof( object[ methodName ] ) !== "undefined" ) { object.stateObject[ methodName ] = object[ methodName ]; } } if ( typeof( object[ methodName ] ) === "undefined" || typeof( object[ methodName ].methodName ) === "undefined" ) { object[ methodName ] = proxyMethodCopy; } } return true; }, buildPrototypes: function( proto, object ) { var result = false; for ( var name in proto ) { if ( object.hasOwnProperty( name ) && typeof( object[ name ] ) === "object" ) { if ( name === "stateObject" || name.substr( 0, 9 ) === "__state__" ) { var newObject = { __proto__: proto[ name ] } if ( typeof( object[ name ] ) !== "undefined" ) { for ( var prop in object[ name ] ) { if ( object[ name ].hasOwnProperty( prop ) ) { newObject[ prop ] = object[ name ][ prop ]; } } } object[ name ] = newObject; if ( name !== "stateObject" ) { result = true; this.buildPrototypes( proto[ name ], object[ name ] ); } } } } return result; }, getAllStateMethods: function( result, object, inState ) { if ( inState ) { for ( var name in object ) { if ( object.hasOwnProperty( name ) ) { if ( typeof( object[ name ] ) === "function" ) { if ( name !== "__onLeave" && name !== "__onEnter" ) { if ( result.indexOf( name ) === -1 ) { result.push( name ); } } } } } } for ( var name in object ) { if ( object.hasOwnProperty( name ) ) { if ( typeof( object[ name ] ) === "object" && name.substr( 0, 9 ) === "__state__" ) { this.getAllStateMethods( result, object[ name ], true ); } } } }, copyMixin: function( original ) { if ( typeof( original.stateObject ) !== "undefined" ) { // console.warn( "stateful.copy(): FIXME: Dit moet echt een copy worden!" ); this.state = new xb.core.stateful.controller( this ); } }, ctor: function( ) { if ( typeof( this.stateObject ) !== "undefined" ) { this.state = new xb.core.stateful.controller( this ); } } } xb.core.stateful.controller = function( object ) { this.object = object; this.stack = [ this.object.stateObject ]; this.stackNames = [ "object" ]; this.currentStateString = ""; this.callStack = []; } xb.core.stateful.controller.prototype = { copy: function( object ) { console.log( "statecontroller:copy") var c = { object: object, currentStateString: "" + this.currentStateString }; c.__proto__ = object.__proto__; var properties = Object.getOwnPropertyNames( this ); for ( var j = 0, jl = properties.length; j < jl; j++ ) { var name = properties[ j ]; var prop = this[ name ]; if ( prop instanceof Array ) { console.log( "statecontroller:copy", name ) var r = []; for ( var i = 0, il = prop.length; i < il; i++ ) { r[ i ] = prop[ i ]; } c[ name ] = r; } } return c; }, getState: function( current, to ) { if ( to.charAt( 0 ) !== '/' ) { to = current + '/' + to; } var pStack = []; var pathicles = to.split( '/' ); for ( var i = 0, il = pathicles.length; i < il; i++ ) { var name = pathicles[ i ]; if ( name === "." || name === "" ) { continue; } else if ( name === ".." ) { pStack.pop(); } else { pStack.push( name ) } } var stack = [ this.object.stateObject ]; var current = this.object; var currentString = "/"; for ( var i = 0, il = pStack.length; i < il; i++ ) { var name = "__state__" + pStack[ i ]; currentString += pStack[ i ] + "/"; current = current[ name ]; if ( typeof( current ) !== "object" ) { throw( "No followup state named", currentString ) } else { stack.push( current ); } } //console.log( "goto", currentString, stack ) return { stack: stack, string: currentString }; }, goto: function( stateString ) { var state = this.getState( this.currentStateString, stateString ) if ( state.stack[ state.stack.length - 1 ] === this.stack[ this.stack.length - 1 ] ) { return false; // no change } var currentState = this.stack[ this.stack.length - 1 ]; if ( typeof( currentState.__onLeave ) === 'function' ) { currentState.__onLeave.call( this.object, state.string ); } var previousStateString = this.currentStateString; var currentState = state.stack[ state.stack.length - 1 ]; this.stack = state.stack; this.currentStateString = state.string; if ( typeof( currentState.__onEnter ) === 'function' ) { currentState.__onEnter.call( this.object, previousStateString ); } }, run: function( method, arguments, level ) { if ( typeof( level ) === "undefined" ) { level = this.stack.length - 1; } if ( level > -1 ) { for ( var i = level; i >= 0; i-- ) { if ( typeof( this.stack[ i ][ method.methodName ] ) === "function" ) { this.callStack.push( { state: this.stack[ i ], method: method, arguments: arguments, level: i } ); result = this.stack[ i ][ method.methodName ].apply( this.object, arguments ); this.callStack.pop(); return result; } } } return undefined; }, parent: function() { var callParams = this.callStack[ this.callStack.length - 1 ]; if ( callParams.level > -1 ) { return this.run( callParams.method, callParams.arguments, callParams.level - 1 ); } return undefined; }, super: function() { var callParams = this.callStack[ this.callStack.length - 1 ]; if ( typeof( callParams.state.__proto__[ callParams.method.methodName ] ) === "function" ) { callParams.state = callParams.state.__proto__; return callParams.state[ callParams.method.methodName ].apply( this.object, callParams.arguments ) } return undefined; } }