00001 import mx.utils.Delegate;
00002
00003 import gugga.application.ISectionsController;
00004 import gugga.application.Section;
00005 import gugga.application.SectionsTransition;
00006 import gugga.application.TransitionInfo;
00007 import gugga.collections.HashTable;
00008 import gugga.common.EventDescriptor;
00009 import gugga.components.ISectionLoader;
00010 import gugga.components.SectionAttacher;
00011 import gugga.components.SectionLoader;
00012 import gugga.debug.Assertion;
00013 import gugga.sequence.ExecuteAsyncMethodTask;
00014 import gugga.sequence.FictiveTask;
00015 import gugga.sequence.TaskManager;
00016 import gugga.utils.Listener;
00017 import gugga.utils.Locker;
00018
00019 [Event("currentSectionPathChanged")]
00020
00024 class gugga.application.SectionsController extends Section implements ISectionsController
00025 {
00026 private var mSectionsInitializationStartTask : FictiveTask;
00027 private var mSectionsInitializationEndTask : FictiveTask;
00028 private var mSectionsControllerInitializingSequence : TaskManager;
00029
00030
00031 private var mSections : HashTable;
00032
00033 private var mMouseEventsLocker : Locker;
00034
00035 private var mLazySectionLoaders : HashTable;
00036 private var mDetachAfterCloseSections : HashTable;
00037
00038 private var mTransition : SectionsTransition;
00039
00040 private var mTargetSectionID : String;
00041
00042 private var mCurrentSection : Section;
00043 public function get CurrentSection() : Section { return mCurrentSection; }
00044
00045 private var mCurrentSectionID : String;
00046 public function get CurrentSectionID() : String { return mCurrentSectionID; }
00047
00048 private var mCurrentSectionPath : String;
00049 public function get CurrentSectionPath() : String { return mCurrentSectionPath; }
00050
00051 private var mIsTransitingSections : Boolean = false;
00052 public function get IsTransitingSections() : Boolean { return mIsTransitingSections; }
00053 public function set IsTransitingSections(aValue:Boolean) : Void { mIsTransitingSections = aValue; }
00054
00059 private var mDisableNavigationWhenTransiting : Boolean = false;
00060 public function get DisableNavigationWhenTransiting() : Boolean { return mDisableNavigationWhenTransiting; }
00061 public function set DisableNavigationWhenTransiting(aValue:Boolean) : Void { mDisableNavigationWhenTransiting = aValue; }
00062
00063 private var mSectionSwfsLoadingPart : Number = 70;
00064 public function get SectionSwfsLoadingPart() : Number { return mSectionSwfsLoadingPart; }
00065 public function set SectionSwfsLoadingPart(aValue:Number) : Void { mSectionSwfsLoadingPart = aValue; }
00066
00067 function SectionsController()
00068 {
00069 mSections = new HashTable();
00070 mMouseEventsLocker = new Locker();
00071
00072 mLazySectionLoaders = new HashTable();
00073 mDetachAfterCloseSections = new HashTable();
00074 }
00075
00076 private function initUI() : Void
00077 {
00078 super.initUI();
00079
00080 createSectionInitializationSequence();
00081 }
00082
00083 private function getSectionID(aSection:Section) : String
00084 {
00085 for(var key:String in mSections)
00086 {
00087 if(mSections[key] == aSection)
00088 {
00089 return key;
00090 }
00091 }
00092
00093 return null;
00094
00095 }
00096
00097 public function getCurrentSectionPath() : String
00098 {
00099 return CurrentSectionPath;
00100 }
00101
00102 private function harvestCurrentSectionPath() : Void
00103 {
00104 var sectionPath : String = CurrentSectionID;
00105
00106 if(mCurrentSection instanceof ISectionsController)
00107 {
00108 sectionPath += "." + ISectionsController(mCurrentSection).getCurrentSectionPath();
00109 }
00110
00111 mCurrentSectionPath = sectionPath;
00112 dispatchEvent({type: "currentSectionPathChanged", target: this});
00113 }
00114
00115 private function createSectionInitializationSequence() : Void
00116 {
00117 var initializingSequence : TaskManager = new TaskManager();
00118 mSectionsInitializationStartTask = new FictiveTask();
00119 mSectionsInitializationEndTask = new FictiveTask();
00120
00121 initializingSequence.addStartingTasks([mSectionsInitializationStartTask]);
00122
00123 initializingSequence.addTaskWithPredecessor(mSectionsInitializationEndTask, mSectionsInitializationStartTask);
00124 initializingSequence.setFinalTask(mSectionsInitializationEndTask);
00125
00126
00127
00128
00129
00130
00131
00132
00133 mSectionsControllerInitializingSequence = initializingSequence;
00134 registerInitializingTask(initializingSequence);
00135 }
00136
00137 public function registerSection(aSectionInstance:Section, aID:String)
00138 {
00139 Assertion.failIfEmpty(aID, "aID is empty", this, arguments);
00140 Assertion.failIfNotNull(mSections[aID], "mSections[" + aID + "] already exists", this, arguments);
00141 Assertion.failIfNull(aSectionInstance, "aSectionInstance is null", this, arguments);
00142
00143 mSections[aID] = aSectionInstance;
00144
00145 if(aSectionInstance instanceof ISectionsController)
00146 {
00147 ISectionsController(aSectionInstance).addEventListener(
00148 "currentSectionPathChanged", Delegate.create(this, onSubControllerSectionPathChanged));
00149 }
00150
00151 var initializeSectionTask : ExecuteAsyncMethodTask = ExecuteAsyncMethodTask.createBasic(
00152 "initialized", aSectionInstance, aSectionInstance.initialize);
00153
00154 mSectionsControllerInitializingSequence.addTaskWithPredecessor(initializeSectionTask, mSectionsInitializationStartTask);
00155 mSectionsControllerInitializingSequence.addTaskPredecessor(mSectionsInitializationEndTask, initializeSectionTask);
00156 }
00157
00158 public function registerChildSection(aSectionInstanceName:String, aID:String)
00159 {
00160 registerSection(this[aSectionInstanceName], aID);
00161 return this[aSectionInstanceName];
00162 }
00163
00164 public function registerLazyLoadSection(aSectionLoader:SectionLoader, aSectionSwfPath:String, aID:String, aDetachAfterClose:Boolean)
00165 {
00166 Assertion.failIfEmpty(aSectionSwfPath, "aSectionSwfPath is empty", this, arguments);
00167
00168 aSectionLoader.ContentPath = aSectionSwfPath;
00169 registerLazySection(aSectionLoader, aID, aDetachAfterClose);
00170 }
00171
00172 public function registerLazyAttachSection(aSectionAttacher:SectionAttacher, aSectionLinkage:String, aID:String, aDetachAfterClose:Boolean)
00173 {
00174 Assertion.failIfEmpty(aSectionLinkage, "aSectionLinkage is empty", this, arguments);
00175
00176 aSectionAttacher.SectionLinkage = aSectionLinkage;
00177 registerLazySection(aSectionAttacher, aID, aDetachAfterClose);
00178 }
00179
00180 public function registerLazySection(aSectionLoader:ISectionLoader, aID:String, aDetachAfterClose:Boolean)
00181 {
00182 Assertion.failIfEmpty(aID, "aID is empty", this, arguments);
00183 Assertion.failIfNotNull(mSections[aID], "mSections[" + aID + "] is already exists", this, arguments);
00184
00185 aSectionLoader.addEventListener("sectionAvailable", Delegate.create(this, onLazySubSectionAvailable));
00186
00187 mLazySectionLoaders[aID] = aSectionLoader;
00188 mDetachAfterCloseSections[aID] = aDetachAfterClose;
00189 }
00190
00191
00192 private function onLazySubSectionAvailable(ev) : Void
00193 {
00194 var section : Section = ev["section"];
00195
00196 if(section instanceof ISectionsController)
00197 {
00198 ISectionsController(section).addEventListener(
00199 "currentSectionPathChanged", Delegate.create(this, onSubControllerSectionPathChanged));
00200 }
00201 }
00202
00203 private function isSectionIDRegistered(aSectionID:String) : Boolean
00204 {
00205 if(mSections.containsKey(aSectionID))
00206 {
00207 return true;
00208 }
00209 else if(mLazySectionLoaders.containsKey(aSectionID))
00210 {
00211 return true;
00212 }
00213 else
00214 {
00215 return false;
00216 }
00217 }
00218
00219 private function isLazySection(aSectionID:String) : Boolean
00220 {
00221 return mLazySectionLoaders.containsKey(aSectionID);
00222 }
00223
00224 private function isDetachAfterCloseSection(aSectionID:String) : Boolean
00225 {
00226 if(mDetachAfterCloseSections.containsKey(aSectionID))
00227 {
00228 return mDetachAfterCloseSections[aSectionID];
00229 }
00230 else
00231 {
00232 return false;
00233 }
00234 }
00235
00236 private function needLazyLoad(aSectionID:String) : Boolean
00237 {
00238 if(mSections.containsKey(aSectionID))
00239 {
00240 return false;
00241 }
00242 else if(mLazySectionLoaders.containsKey(aSectionID))
00243 {
00244 return true;
00245 }
00246 else
00247 {
00248 Assertion.fail("Invalid section ID", this, arguments);
00249 }
00250 }
00251
00252
00253 private function getTransitionInfo(aSectionID:String, aSectionPathRest:String) : TransitionInfo
00254 {
00255 var transitionInfo : TransitionInfo = new TransitionInfo();
00256
00257 transitionInfo.SectionsController = this;
00258
00259 transitionInfo.CurrentSection = mCurrentSection;
00260 transitionInfo.TargetSection = mSections[aSectionID];
00261
00262 if(isLazySection(aSectionID))
00263 {
00264 transitionInfo.TargetSectionLoader = mLazySectionLoaders[aSectionID];
00265 transitionInfo.DoLoadTargetSection = needLazyLoad(aSectionID);
00266 }
00267
00268 transitionInfo.DetachCurrentSectionAfterClose = isDetachAfterCloseSection(aSectionID);
00269 transitionInfo.SectionPathRest = aSectionPathRest;
00270
00271 return transitionInfo;
00272 }
00273
00274
00275 private function getTransition(aSectionID:String, aSectionPathRest:String) : SectionsTransition
00276 {
00277 var transitionInfo : TransitionInfo = getTransitionInfo(aSectionID, aSectionPathRest);
00278
00279 var transition : SectionsTransition = new SectionsTransition(transitionInfo);
00280 return transition;
00281 }
00282
00283 private function startTransition(aTransition:SectionsTransition) : Void
00284 {
00285
00286 Listener.createSingleTimeListener(
00287 new EventDescriptor(aTransition, "lazyTargetSectionInitialized"),
00288 Delegate.create(this, onLazyTargetSectionInitialized)
00289 );
00290
00291 Listener.createSingleTimeListener(
00292 new EventDescriptor(aTransition, "sectionsSwapped"),
00293 Delegate.create(this, onSectionsSwapped)
00294 );
00295
00296 Listener.createSingleTimeListener(
00297 new EventDescriptor(aTransition, "currentSectionDestroyed"),
00298 Delegate.create(this, onCurrentSectionDestroyed)
00299 );
00300
00301 Listener.createSingleTimeListener(
00302 new EventDescriptor(aTransition, "completed"),
00303 Delegate.create(this, onTransitionCompleted)
00304 );
00305
00306
00307 Listener.createSingleTimeListener(
00308 new EventDescriptor(aTransition, "interrupted"),
00309 Delegate.create(this, onTransitionInterrupted)
00310 );
00311
00312 Listener.createSingleTimeListener(
00313 new EventDescriptor(aTransition, "disposed"),
00314 Delegate.create(this, onTransitionDisposed)
00315 );
00316
00317 aTransition.start();
00318 }
00319
00320 public function openSection(aSectionPath:String) : SectionsTransition
00321 {
00322 Assertion.failIfEmpty(aSectionPath, "Argument aPath is empty", this, arguments);
00323 Assertion.failIfNotNull(mTransition, "Old transition is still alive", this, arguments);
00324 Assertion.failIfTrue(mTransition.isRunning(), "Old transition is still running", this, arguments);
00325
00326 var pathArray:Array = aSectionPath.split(".");
00327 var sectionID:String = pathArray[0];
00328 var sectionPathRest:String;
00329
00330 mTargetSectionID = sectionID;
00331
00332 Assertion.failIfReturnsFalse(
00333 this, isSectionIDRegistered, [sectionID],
00334 "Section ID '" + sectionID + "' is not registered", this, arguments);
00335
00336 if(pathArray.length > 1)
00337 {
00338 var sectionPathRestArray:Array = pathArray.slice(1);
00339 sectionPathRest = sectionPathRestArray.join(".");
00340 }
00341
00342 if(mCurrentSectionID == sectionID)
00343 {
00344 var currentSection : Section = Section(mSections[mCurrentSectionID]);
00345
00346 if(currentSection)
00347 {
00348 if((currentSection instanceof ISectionsController) && sectionPathRest)
00349 {
00350 var openChildSectionsTransition : SectionsTransition =
00351 ISectionsController(currentSection).openSection(sectionPathRest);
00352
00353 return openChildSectionsTransition;
00354 }
00355 else
00356 {
00357 return null;
00358 }
00359 }
00360 }
00361
00362
00363
00364 mIsTransitingSections = true;
00365
00366
00367
00368
00369
00370
00371
00372
00373 mTransition = getTransition(sectionID, sectionPathRest);
00374
00375 if(mTransition)
00376 {
00377 startTransition(mTransition);
00378 }
00379
00380 return mTransition;
00381 }
00382
00383 private function isSectionClosing(aSectionPath:String) : Boolean
00384 {
00385 var pathArray:Array = aSectionPath.split(".");
00386 var sectionID:String = pathArray[0];
00387 var section:Section = mSections[sectionID];
00388
00389 if(section instanceof SectionsController)
00390 {
00391 var sectionPathRest:String = "";
00392
00393 if(pathArray.length > 1)
00394 {
00395 var sectionPathRestArray:Array = pathArray.slice(1);
00396 sectionPathRest = sectionPathRestArray.join(".");
00397 }
00398
00399 return SectionsController(section).isSectionClosing(sectionPathRest);
00400 }
00401 else
00402 {
00403 return section.IsClosing;
00404 }
00405 }
00406
00407 private function onLazyTargetSectionInitialized(ev) : Void
00408 {
00409 var section : Section = ev.section;
00410 mSections[mTargetSectionID] = section;
00411 }
00412
00413 private function onCurrentSectionDestroyed(ev) : Void
00414 {
00415 var sectionID : String = getSectionID(ev.section);
00416 if(mLazySectionLoaders.containsKey(sectionID))
00417 {
00418 var sectionLoader : ISectionLoader = mLazySectionLoaders[sectionID];
00419 sectionLoader.unloadData();
00420 }
00421
00422 mSections[sectionID] = null;
00423 }
00424
00425 private function onSectionsSwapped(ev) : Void
00426 {
00427 mCurrentSectionID = mTargetSectionID;
00428 mCurrentSection = mSections[mCurrentSectionID];
00429
00430 harvestCurrentSectionPath();
00431 }
00432
00433 private function onSubControllerSectionPathChanged(ev) : Void
00434 {
00435 harvestCurrentSectionPath();
00436 }
00437
00438 private function onTransitionCompleted(ev) : Void
00439 {
00440 mTargetSectionID = null;
00441 mIsTransitingSections = false;
00442
00443
00444 }
00445
00446 private function onTransitionInterrupted(ev) : Void
00447 {
00448 mTargetSectionID = null;
00449 mIsTransitingSections = false;
00450
00451 mCurrentSection = ev.newCurrentSection;
00452 mCurrentSectionID = getSectionID(mCurrentSection);
00453
00454
00455 }
00456
00457 private function onTransitionDisposed(ev) : Void
00458 {
00459 delete mTransition;
00460 }
00461
00462 public function enableBoundingBoxMouseEvents() : Void
00463 {
00464 mMouseEventsLocker.clearAllLocks();
00465 super.enableBoundingBoxMouseEvents();
00466 }
00467
00468 public function setMouseEventsLock(aLockID:String) : String
00469 {
00470 disableBoundingBoxMouseEvents();
00471 return mMouseEventsLocker.setLock(aLockID);
00472 }
00473
00474 public function clearMouseEventsLock(aLockID:String) : Void
00475 {
00476 mMouseEventsLocker.clearLock(aLockID);
00477
00478 if(!mMouseEventsLocker.isLocked())
00479 {
00480 super.enableBoundingBoxMouseEvents();
00481 }
00482 }
00483 }