/**
* @license
* Copyright (c) 2019 Gaurang Lade
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import EventDispatcher from "../../createjs/EventDispatcher";
import ElementUtils from '../utils/ElementUtils';
import ViewStack from './ViewStack';
import View from './View';
import EventRouter from './EventRouter';
/**
*
* ViewNavigator
* @extends {EventDispatcher}
*/
class ViewNavigator extends EventDispatcher {
/**
* @description
* UI navigation manages by ViewNavigator.
*
* ViewNavigator" is base class which have simple view navigation functionality.
*
* ViewNavigator manages set of view using stack-based history mechanism which also called as ViewStack.
*
* Each Viewstack represent its own view history stack. So View4Js supports multiple history stack too.
*
* ViewNavigator also Manages LifeCycle of View.
*
* @param {String} _id - ViewNavigator ID
* @param {String} [_parentId=null] - Parent ID View or "root" DOM Element
* @todo {Boolean} navigationHistory - Enable or Disable Navigation History.
* If true ViewNavigator will keep View History.
* @memberof ViewNavigator
*/
constructor(_id, _parentId = null) {
super();
this.id = _id;
// HTML ID attribute only
this.parentId = _parentId;
this.activeViewId = null;
this.activeViewStackId = null;
this.activeRoute = null;
this.views = {};
this.viewstacks = {};
this.eventRoute = new EventRouter();
this.isRendered = false;
this.navigationHistory = true;
this.initNavigator();
}
/**
* @description Implemented by Subclass
* Call by ViewNavigator Constructor
* @override
* @memberof ViewNavigator
*/
initNavigator() {
}
/**
* Sets Navigation History
* Boolean Property , returns True if Component is Enabled
*/
get history() {
return this.navigationHistory;
}
set history(_navigationHistory = true){
this.navigationHistory = _navigationHistory;
}
/**
* @description Set EventRouter for ViewNavigator, EventRouter is useful for EventBased Navigation and for navigation of single view or multiple views.
* @param {Object} _router - EventRouter
* @memberof ViewNavigator
*/
set eventRouter(_router) {
if (!_router instanceof EventRouter) {
throw new ClassError("ViewNavigator", "Wrong Type of Router");
}
this.eventRoute = _router;
}
/**
* @description Returns EventRouter instance of ViewNavigator
* @returns {object} - EventRouter Instance
* @readonly
* @memberof ViewNavigator
*/
get eventRouter() {
return this.eventRoute;
}
/**
*
* @description
* By Default View class will be created
* When Overrides by Subclass , custom Views will be created
* @override
* @param {String} _viewId - View ID
* @param {String} _route - Navigation Route / Path
* @param {String} _navparams - Navigation Parameters pass to View
* @param {String} _viewStackId - Parent Viewstack ID of View
* @returns {Object} - View Instance
* @memberof ViewNavigator
*/
createView(_viewId, _route, _navparams, _viewStackId) {
return new View(_viewId, _route, _navparams, _viewStackId);
}
/**
* @description
* By Default ViewStack class will be created
* When Overrides by Subclass , custom Viewstack will be created
* @override
* @param {String} _viewStackId - ViewStack ID
* @param {String} _route - Navigation Route / Path
* @param {String} _parentId - Parent ViewNavigator ID
* @returns {Object} - ViewStack Instance
* @memberof ViewNavigator
*/
createViewStack(_viewStackId, _route, _parentId) {
return new ViewStack(_viewStackId, _route, _parentId);
}
/**
* @description
* Call by ViewManager
* Navigation Route and Navigation EventRoute Combination must be unique
* @param {String} _route - Navigation Route
* @param {String} _navevent - Navigation Event Route
* @param {String} _navparams - Parameters pass to View
* @memberof ViewNavigator
*/
navigate(_route, _navevent, _navparams) {
let tmpviewStackId = null;
if (this.navigationHistory == false) {
this.navigateBack(_route);
}
tmpviewStackId = this.eventRoute.findViewStackId(_navevent, _route);
let tmpViewStack = this.getViewStack(tmpviewStackId);
if (tmpViewStack == null)
tmpViewStack = this.createViewStack(tmpviewStackId, _route, this.id);
// Viewstack have _parentId
tmpViewStack.render(); // will construct Element and add it to DOM parent
this.activeViewStackId = tmpviewStackId;
this.viewstacks[tmpviewStackId] = tmpViewStack;
let tmpviewId = this.eventRoute.findViewId(_navevent, _route);
let tmpView = this.getView(tmpviewId);
if (tmpView == null)
tmpView = this.createView(tmpviewId, _route, _navevent, _navparams, tmpviewStackId);
this.removeActiveMenuElement();
let tmpViewStackEl = tmpViewStack.getViewStackElement();
tmpView.attachView(tmpViewStackEl); // will construct Element and add it to DOM parent
tmpViewStack.pushViewElement(tmpviewId, this.views);
tmpView.activateView();
this.activeViewId = tmpviewId;
this.views[tmpviewId] = tmpView;
this.activeRoute = _route;
this.setActiveMenuElement(_navevent);
}
/**
* @description
* Call by ViewManager or ViewNavigator internally
* Navigate back to previous View if history set to true
* @param {String} _route
* @memberof ViewNavigator
*/
navigateBack(_route) {
if (_route == this.activeRoute) {
let tmpViewStack = this.getViewStack(this.activeViewStackId);
tmpViewStack.popViewElement();
let tmpView = this.views[this.activeViewId];
tmpView.deActivateView();
tmpView.detachView();
tmpView.destroy();
this.removeActiveMenuElement();
tmpView = null; // make garbage collected
this.views[this.activeViewId] = null;
delete this.views[this.activeViewId];
if (this.navigationHistory == true) {
this.activeViewId = tmpViewStack.getActiveViewId();
let tmpViewBack = this.views[this.activeViewId];
tmpViewBack.activateView();
let tmpNavEvent = this.views[this.activeViewId].navEvent;
this.setActiveMenuElement(tmpNavEvent);
this.activeRoute = this.views[this.activeViewId].route;
}
}
}
/**
* @description Navigate Back to Specific View
* @param {String} _viewId - View ID
* @todo To be Implemented
* @memberof ViewNavigator
*/
navigateBackToView(_viewId) {
}
/**
* @description Navigate to Specific View
* @param {String} _viewId - View ID
* @todo To be Implemented
* @memberof ViewNavigator
*/
navigateToView(_viewId) {
}
/**
*
* @description This method will be implemented by Subclass
* @memberof ViewNavigator
*/
removeActiveMenuElement(){
}
/**
*
* @description This method will be implemented by Subclass
* @param {string} _navEvent - Navigation Event Name
* @memberof ViewNavigator
*/
setActiveMenuElement(_navEvent){
}
/**
* @description Get ViewStack Object by ViewStackId
* @param {String} _viewStackId - ViewStackID
* @returns {Object} - ViewStack Instance
* @memberof ViewNavigator
*/
getViewStack(_viewStackId) {
let tmpVstack = null;
tmpVstack = this.viewstacks[_viewStackId];
return tmpVstack;
}
/**
* @description Get ViewObject by ViewId
* @param {string} _viewId - ViewID
* @returns {Object} - View Instance
* @memberof ViewNavigator
*/
getView(_viewId) {
let tmpV = null;
tmpV = this.views[_viewId];
return tmpV;
}
/**
* @description
* ViewNavigator Lifecycle Method,
* Call by ViewManager,
* Render ViewNavigator DOM Content
* @public
* @memberof ViewNavigator
*/
render() {
if (!this.isRendered) {
this.renderNavigator();
this.renderNavigatorContent();
}
}
/**
*
* @description Renders, ViewNavigator DOM Element , it call by Render Method
* @private
* @memberof ViewNavigator
*/
renderNavigator() {
let tmpParentId = this.parentId;
let tmpParentElement = null;
if (this.parentId != "root") {
tmpParentElement = ElementUtils.view(this.parentId);
} else {
tmpParentElement = document.getElementById(tmpParentId);
}
if (tmpParentElement != null) {
let tmpNavigatorEl = ElementUtils.constructNavigatorBaseElement(this.id);
tmpParentElement.insertAdjacentHTML('beforeend', tmpNavigatorEl);
this.isRendered = true;
} else {
throw new ClassError("ViewNavigator", "No Parent Element found for Navigator");
}
}
/**
* @description Render Navigator Subclass DOM Element content
* @private
* @override
* @memberof ViewNavigator
*/
renderNavigatorContent() {}
/**
*
* @description
* ViewNavigator Lifecycle Method,
* Call by ViewManager to destroy ViewNavigator,
* Remove Event Handlers, Make Properties null,
* Remove Views and Viewstack objects.
* Remove ViewNavigator DOM Element and Its Contents
* Remove EventRoute Object
* @public
* @memberof ViewNavigator
*/
destroy() {
for (let viewObj in this.views) {
let tmpview = this.views[viewObj];
tmpview.destroy();
}
for (let viewStkObj in this.viewstacks) {
let tmpviewstk = this.viewstacks[viewStkObj];
tmpviewstk.destroy();
}
let tmpNavigatorEl = ElementUtils.viewNavigator(this.id);
tmpNavigatorEl.parentNode.removeChild(tmpNavigatorEl);
this.views = null;
this.viewstacks = null;
this.parentId = null;
this.id = null;
this.parentId = null;
this.activeViewId = null;
this.activeViewStackId = null;
this.activeRoute = null;
this.eventRoute = null;
this.isRendered = false;
}
}
export default ViewNavigator;