package app { import app.vo.SessionInfo; import app.vo.Bike; import app.vo.DataSeries; import app.vo.Point; import app.vo.Ride; import app.vo.Route; import app.vo.Stop; import com.adobe.crypto.MD5; import flash.display.DisplayObject; import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.core.Application; import mx.managers.PopUpManager; import mx.rpc.AsyncToken; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.rpc.remoting.RemoteObject; import views.LoginDialog; import flash.utils.describeType; import app.vo.User; import forms.UserForm; import flash.utils.setInterval; import flash.utils.clearInterval; import mx.utils.ObjectUtil; import flash.utils.setTimeout; import app.vo.UserGroup; import mx.collections.Sort; import mx.collections.SortField; public class Controller { private static var _instance:Controller; public static function getInstance(model:Model, service:RemoteObject):Controller { if (_instance != null) { return _instance; } else { return new Controller(model, service); } } private var model:Model; private var service:RemoteObject; public function Controller(model:Model, service:RemoteObject) { if (_instance != null) { throw new Error("You can't create multiple controller instances"); } _instance = this; this.model = model; this.service = service; // get session info periodically to keep session alive setInterval(function():void { getSessionInfo(); }, 1000 * 60 * 10); } public function createBike(b:Bike):void { var token:AsyncToken = service.createBike({bike:b}); token.resultHandler = function(event:ResultEvent):void { reloadBikes(); } } public function createPoint(p:Point):void { var token:AsyncToken = service.createPoint({point:p}); token.resultHandler = function(event:ResultEvent):void { reloadPoints(); } } public function createRide(r:Ride):void { var token:AsyncToken = service.createRide({ride:serializeRide(r)}); token.resultHandler = function(event:ResultEvent):void { reloadRides(); reloadSameReport(); } } public function createRoute(r:Route):void { var token:AsyncToken = service.createRoute({route:serializeRoute(r)}); token.resultHandler = function(event:ResultEvent):void { reloadRoutes(); } } public function createUser(u:User, form:UserForm):void { var token:AsyncToken = service.createUser({user:u}); token.resultHandler = function(event:ResultEvent):void { reloadUsers(); getSessionInfo(); form.saveCallback(); } token.faultHandler = function(event:FaultEvent):void { getSessionInfo(); try { var type:String = event.message["extendedData"].type; if (type.indexOf("UsernameAlreadyTakenException") == 0) { form.usernameAlreadyTaken(); return; } } catch (e:Error) { } Alert.show(event.fault.message); } } public function createUserGroup(ug:UserGroup):void { var token:AsyncToken = service.createUserGroup({group:ug}); token.resultHandler = function(event:ResultEvent):void { reloadUserGroups(); } } public function doLogin():void { var d:LoginDialog = LoginDialog(PopUpManager.createPopUp(DisplayObject(Application.application), LoginDialog, true)); d.controller = this; PopUpManager.centerPopUp(d); } public function getSessionInfo(forceDataInitialization:Boolean=false):void { var oldViewUserId:int = model.sessionInfo == null ? -1 : model.sessionInfo.viewUserId; var token:AsyncToken = service.getSessionInfo(); token.resultHandler = function(event:ResultEvent):void { model.sessionInfo = new SessionInfo(event.result); if (forceDataInitialization || model.sessionInfo.viewUserId != oldViewUserId) { initializeUserData(); } } } public function initializeUserData():void { reloadPoints(); reloadRoutes(); reloadBikes(); reloadRides(); reloadSameReport(); } public function login(username:String, password:String, loginDialog:LoginDialog):void { var token:AsyncToken = service.login(username, MD5.hash(password)); token.resultHandler = function(event:ResultEvent):void { getSessionInfo(true); reloadUsers(); reloadUserGroups(); PopUpManager.removePopUp(loginDialog); } token.faultHandler = function(event:FaultEvent):void { getSessionInfo(); try { var type:String = event.message["extendedData"].type; if (type.indexOf("CredentialsDeniedException") == 0) { loginDialog.credentialsDenied(); return; } } catch (e:Error) { } Alert.show(event.fault.message); } } public function logout():void { var token:AsyncToken = service.getOperation("logout").send(); token.resultHandler = function(event:ResultEvent):void { getSessionInfo(true); reloadUsers(); reloadUserGroups(); initializeUserData(); } token.faultHandler = function(event:FaultEvent):void { getSessionInfo(); } } public function reloadBikes():void { var token:AsyncToken = service.getBikeList(); token.resultHandler = function(event:ResultEvent):void { RemotingUtils.repopulate( model.bikeList, RemotingUtils.inflate(ArrayCollection(event.result).source, Bike) ); } } public function reloadPoints():void { var token:AsyncToken = service.getPointList(); token.resultHandler = function(event:ResultEvent):void { RemotingUtils.repopulate( model.pointList, RemotingUtils.inflate(ArrayCollection(event.result).source, Point) ); } } private function limitedLoop(isReady:Function, doAction:Function, period:int, maxCount:int):void { var f:Function = function():void { if (isReady()) { doAction(); } else { maxCount--; if (maxCount >= 0) { setTimeout(f, period); } } } setTimeout(f, period); } public function reloadReport(granularity:String, breakdown:String):void { model.lastReportArgs = arguments; var token:AsyncToken = service.getReport(granularity, []); token.resultHandler = function(event:ResultEvent):void { var data:ArrayCollection = ArrayCollection(event.result); var ac:ArrayCollection; var interval:uint; model.reportSeries.removeAll(); RemotingUtils.repopulate( model.reportData, data, false ); switch (breakdown) { case "route": limitedLoop( function():Boolean { return model.routeList.length > 0; }, function():void { for each (var r:Route in model.routeList) { doReloadReport(r.name, granularity, [{field:"route", id:r.id}], model.reportSeries); } }, 250, 5); break; case "bike": limitedLoop( function():Boolean { return model.bikeList.length > 0; }, function():void { for each (var b:Bike in model.bikeList) { doReloadReport(b.name, granularity, [{field:"bike", id:b.id}], model.reportSeries); } }, 250, 5); break; case "dayOfWeek": for (var i:int = 0; i < model.dayOfWeekList.length; i++) { doReloadReport(model.dayOfWeekList[i], granularity, [{field:"dayOfWeek", num:i}], model.reportSeries); } break; default: model.reportSeries.addItem(new DataSeries("All", data)); } } } private function doReloadReport(label:String, granularity:String, filters:Array, seriesContainer:ArrayCollection):AsyncToken { var token:AsyncToken = service.getReport(granularity, filters); token.resultHandler = function(event:ResultEvent):void { var ac:ArrayCollection = ArrayCollection(event.result); if (ac.length > 0) { seriesContainer.addItem(new DataSeries(label, ac)); } } return token; } public function reloadRides():void { var token:AsyncToken = service.getRideList(); token.resultHandler = function(event:ResultEvent):void { RemotingUtils.repopulate( model.rideList, RemotingUtils.inflate(ArrayCollection(event.result).source, Ride, function(state:Object, type:Class):Ride { var ride:Ride = new Ride(state); var i:int; for (i = 0; i < model.bikeList.length; i++) { var b:Bike = model.bikeList[i]; if (b.id == ride.bikeId) { ride.bike = b; } } for (i = 0; i < model.routeList.length; i++) { var r:Route = model.routeList[i]; if (r.id == ride.routeId) { ride.route = r; } } return ride; } ) ); var s:Sort = new Sort(); s.fields = [new SortField("startTimestamp", false, true, false)]; model.rideList.sort = s; model.rideList.refresh(); } } public function reloadRoutes():void { var token:AsyncToken = service.getRouteList(); token.resultHandler = function(event:ResultEvent):void { RemotingUtils.repopulate( model.routeList, RemotingUtils.inflate(ArrayCollection(event.result).source, Route, function(state:Object, type:Class):Route { var r:Route = new Route(state); for (var i:int = 0; i < model.pointList.length; i++) { var p:Point = model.pointList[i]; if (p.id == r.startPointId) { r.startPoint = p; } if (p.id == r.endPointId) { r.endPoint = p; } } return r; } ) ); } } public function reloadSameReport():void { if (model.lastReportArgs != null) { try { reloadReport.apply(this, model.lastReportArgs); } catch (e:Error) { trace(e); } } } public function reloadStops(ride:Ride):void { var token:AsyncToken = service.getStopList(ride.id); token.resultHandler = function(event:ResultEvent):void { RemotingUtils.repopulate( ride.stopList, RemotingUtils.inflate(ArrayCollection(event.result).source, Stop, function(state:Object, type:Class):Stop { state.rideId = ride.id; var s:Stop = new Stop(state); s.ride = ride; for (var i:int = 0; i < model.pointList.length; i++) { var p:Point = model.pointList[i]; if (p.id == s.pointId) { s.point = p; } } return s; } ) ); ride.stopCount = ride.stopList.length; } } public function reloadUsers():void { var token:AsyncToken = service.getUserList(); token.resultHandler = function(event:ResultEvent):void { RemotingUtils.repopulate( model.userList, RemotingUtils.inflate(ArrayCollection(event.result).source, User ) ); } } public function reloadUserGroups():void { var token:AsyncToken = service.getUserGroupList(); token.resultHandler = function(event:ResultEvent):void { RemotingUtils.repopulate( model.userGroupList, RemotingUtils.inflate(ArrayCollection(event.result).source, UserGroup ) ); } } public function switchViewUser(userId:int):void { var token:AsyncToken = service.switchViewUser({userId: userId}); token.resultHandler = function(event:ResultEvent):void { getSessionInfo(); } } public function testAuth():void { var token:AsyncToken = service.testAuth(); token.resultHandler = function(event:ResultEvent):void { Alert.show("A authenticated session exists!"); } token.faultHandler = function(event:FaultEvent):void { Alert.show("GONK: no authenticated session."); } } public function updateBike(b:Bike):void { var token:AsyncToken = service.updateBike({bike:b}); token.resultHandler = function(event:ResultEvent):void { reloadBikes(); reloadSameReport(); } } public function updatePoint(p:Point):void { var token:AsyncToken = service.updatePoint({point:p}); token.resultHandler = function(event:ResultEvent):void { reloadPoints(); } } public function updateRide(r:Ride):void { var ride:Object = serializeRide(r); var token:AsyncToken = service.updateRide({ride:ride}); token.resultHandler = function(event:ResultEvent):void { reloadRides(); reloadSameReport(); } } public function updateRoute(r:Route):void { var token:AsyncToken = service.updateRoute({route:serializeRoute(r)}); token.resultHandler = function(event:ResultEvent):void { reloadRoutes(); reloadSameReport(); } } public function updateUser(u:User, form:UserForm):void { var token:AsyncToken = service.updateUser({user:u}); token.resultHandler = function(event:ResultEvent):void { reloadUsers(); getSessionInfo(); form.saveCallback(); } token.faultHandler = function(event:FaultEvent):void { getSessionInfo(); try { var type:String = event.message["extendedData"].type; if (type.indexOf("CredentialsDeniedException") == 0) { form.credentialsDenied(); return; } } catch (e:Error) { } Alert.show(event.fault.message); } } public function updateUserGroup(ug:UserGroup):void { var token:AsyncToken = service.updateUserGroup({group:ug}); token.resultHandler = function(event:ResultEvent):void { reloadUserGroups(); } } private function serializeRide(r:Ride):Object { var o:Object = genericize(r, ["bike", "route", "stopList"]); o.stopList = new ArrayCollection(); for (var i:int = 0; i < r.stopList.length; i++) { o.stopList.addItem(genericize(r.stopList[i], ["ride", "point"])); } return o; } private function serializeRoute(r:Route):Object { return genericize(r, ["startPoint", "endPoint"]) } private function genericize(o:Object, toIgnore:Array):Object { var desc:XML = describeType(o); var g:Object = {}; var keys:XMLList = desc.accessor.(@access == "readwrite" || @access == "readonly"); for (var i:int = 0; i < keys.length(); i++) { var n:String = keys[i].@name; var ignore:Boolean = false; for (var j:int = 0; j < toIgnore.length; j++) { if (n == toIgnore[j]) { ignore = true; break; } } if (ignore) { trace("ignoring " + n); continue; } g[n] = o[n]; } return g; } } }