All Downloads are FREE. Search and download functionalities are using the official Maven repository.

template-application-launchpad.resources.js.ui-layout.js.template Maven / Gradle / Ivy

/*
 * Copyright (c) 2017 SAP and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * Contributors:
 * SAP - initial API and implementation
 */

var ViewRegistry = function() {
	
	var _views = {};
	
	var _factories = {};
	
	this.factory = function(name, func){
		if(arguments.length == 1)
			return _factories[name];
			
		if(typeof func !== 'function')
			throw Error('Not a function');
		_factories[name] = func;
		return this;
	}
	
	this.factories = function(){
		return _factories;
	}
	
	this.view = function(viewid, factory, region, label, settings){
		if(viewid === undefined)
			throw Error('Illegal argument for viewid ' + viewid);
		//get view
		if(arguments.length == 1)
			return _views[viewid];
		//set view
		if(factory === undefined)
			throw Error('Illegal argument for factory ' + factory);
		var componentName;
		if(typeof factory === 'string')
			componentName = factory;
		else if(typeof factory === 'function'){
			this.factory(viewid, factory);
			componentName = viewid;
		}
		region = region || 'center-bottom';
		settings = settings || [];
		settings.label = label;
		
		_views[viewid] = {
			id: viewid,
			type: 'component',
			componentName: componentName,
			componentState: settings,
			defaultRegionId: region
		}
		return this;
	};
	
	this.views = function(){
		return _views;
	}
	
	return this;
	
};

function LayoutController(viewRegistry, messageHub){

	this.viewRegistry = viewRegistry;
	
	this.regions = {
		'main': {
			id: 'main',
			type: 'row',
			isClosable: false
		},
		'left': {
			id: 'left',
			type: 'column',
			width: 30,
			defaultRegionId: 'main'
		},
		'left-top': {
			id: 'left-top',
			type: 'stack',
			defaultRegionId: 'left'
		},
		'left-middle': {
			id: 'left-middle',
			type: 'stack',
			defaultRegionId: 'left'
		},
		'left-bottom': {
			id: 'left-bottom',
			type: 'stack',
			defaultRegionId: 'left'
		},
		'center': {
			id: 'center',
			type: 'column',
			defaultRegionId: 'main'
		},
		'center-top': {
			id: 'center-top',				
			type: 'stack',
			defaultRegionId: 'center'
		},
		'center-middle': {
			id: 'center-middle',				
			type: 'stack',
			defaultRegionId: 'center'
		},
		'center-bottom': {
			id: 'center-bottom',				
			type: 'stack',
			defaultRegionId: 'center'
		},
		'right': {
			id: 'right',
			type: 'column',
			defaultRegionId: 'main'
		},
		'right-top': {
			id: 'right-top',
			type: 'stack',
			defaultRegionId: 'right'
		},
		'right-middle': {
			id: 'right-middle',
			type: 'stack',
			defaultRegionId: 'right'
		},
		'right-bottom': {
			id: 'right-bottom',
			type: 'stack',
			defaultRegionId: 'right'
		}
	}
	
	var parentIds = function(node, regionId, arr){
		arr = arr || [];
		var _parentId = regionId || node.defaultRegionId;
		if(!_parentId)
			return arr;
		if(this.regions[_parentId]){
			arr.push(_parentId);
			return parentIds.call(this, this.regions[_parentId], undefined, arr);
		}
		return arr;
	};
	
	var copy = function(src){
		return JSON.parse(JSON.stringify(src));	
	};
	
	var gridItem = function(top, id){
		if(top.id === id)
			return top;
		if(top.content){
			var item = top.content.find(function(_item){
				return gridItem(_item, id);
			});
			return item && gridItem(item, id)
		}
	};
	
	var addView = function(views, view, grid){
		var node;
		var path = [view.id].concat(parentIds.call(this, view));
		//build branches top-bottom				
		path.reverse().forEach(function(compId, idx, arr){
			var _gridNode = gridItem(grid, compId);
			if(_gridNode){
				node = _gridNode;
				return;//next
			} else {
				var child = this.regions[compId] || views[compId];
				if(child){
					child = copy(child);
					if(!node.content)
						node.content = [];	
					if(!node.content.find(function(it){return it.id == child.id}))
						node.content.push(child);
					node = child;
				}
			}
		}.bind(this));
	}.bind(this);
	
	this.layoutViews = function(views){
		var grid = copy(this.regions.main);
		if(views){
			Object.values(views).map(function(view){
				if(view){
					view = copy(view);
					addView(views, view, grid);			
				}
				return view;
			}.bind(this));
		}	
		return grid;
	}.bind(this);
	
	this.messageHub = messageHub || new FramesMessageHub();
	
	this.listeners = {};
	
	this.addListener = function(eventType, callback){
		if(!this.listeners[eventType]){
			this.listeners[eventType] = [];
		}
		var subscriber = this.messageHub.subscribe(callback.bind(this), eventType);

		var entry = {
			id: uuidv4(),
			handler: subscriber
		};
		this.listeners[eventType].push(entry);
		return entry.id;
	};		
	this.removeListener = function(id, eventType){
		if(!this.listeners[eventType])
			return;
		for (var i = this.listeners[eventType].length - 1; i >= 0; i--) {
		  if (this.listeners[eventType][i].id == id) {
			var subscriber = this.listeners[eventType][i].handler;
			this.messageHub.unsibscribe(subscriber);
			this.listeners[eventType].splice(i, 1);
		  }
		}
	};
	/**
	 * Provide id to enable save/restore state for browser's localStorage. No id will implicitly always reconstruct the instance and will not attempt future state changes save.
	 * Providing an id and the reconstruct flag set to true will not retrive from local storage last state, but will subscribe and save the reconstructed instance future changes.
	 * The layout will be always reconstructed on first init regardless of the reconstruct flag.
	 */
	this.init = function(containerEl, viewNames, id, reconstruct){
		
		this.containerEl = containerEl;
		this.viewNames = viewNames;
		id = id || $(containerEl).attr("id");
		
		if(id){
			if(!reconstruct){
				//load from localStorage
				var savedState = localStorage.getItem('DIRIGIBLE.application.{{projectName}}.'+ id);
				if(savedState !== null) {
					this.config = JSON.parse(savedState);
				}				
			}
		} 
		if(!id || reconstruct || !this.config){
			//reconstruct (ignore previously saved state)
			var views = {};
			this.viewNames.forEach(function(viewName){
				views[viewName] = this.viewRegistry.view(viewName);
			}.bind(this));
			this.config = {
				dimensions: {
					headerHeight: 26,
					borderWidth: 3
				},
				content: [this.layoutViews.call(this, views)]
			};
		}
		
		this.layout = new GoldenLayout(this.config, containerEl);
		
		Object.keys(this.viewRegistry.factories()).forEach(function(factoryname){
			this.layout.registerComponent(factoryname, this.viewRegistry.factory(factoryname));
		}.bind(this));
		
		if(id){
			this.layout.on('stateChanged', function(){
				//TODO: debounce or do that only with save button! This fires a lot
				var state = JSON.stringify( this.layout.toConfig() );
				localStorage.setItem('DIRIGIBLE.application.{{projectName}}.'+ id, state );
			}.bind(this));			
		}
		
		this.layout.init();
	};
	
	this.openView = function(viewId/*, region*/){
		var viewDef = this.viewRegistry.view(viewId);
		if(viewDef){
			var node;
			var defaultPath = [viewId];
			defaultPath = defaultPath.concat(parentIds.call(this, viewDef/*, region*/));//TODO: consider custom region
			defaultPath.reverse().forEach(function(compId){
				if(!node)
					node = this.layout.root;
				else
					node = this.layout.root.getItemsById(node.id || node.config.id)[0];
				var child = node.getItemsById(compId)[0] || this.regions[compId] || this.viewRegistry.view(compId);
				if(!node.isRoot && child && this.layout.root.getItemsById(child.id || child.config.id).length<1){
					node.addChild(child);
				}
				node = child;
			}.bind(this));
		}
	};
	
	this.openEditor = function(resourcePath, resourceLabel, contentType, editorId){
		var newItemConfig = {
			id: resourcePath,
			title: resourceLabel,
			type: 'component',
			componentName: 'editor',
			componentState:{ 
				path: resourcePath,
				editorId: editorId,
				contentType: contentType
			}
		};
		//is an editor available to stack new children to it?
		if(this.layout.root.getItemsById('editor')[0]){
			//is already open?
			if(this.layout.root.getItemsById(newItemConfig.id).length){
				//replace content
				var panel = this.layout.root.getItemsById(newItemConfig.id)[0];
				//panel.instance.setContent(newItemConfig.id)
				panel.parent.setActiveContentItem(panel);
			} else {
				// open new tab
				this.layout.root.getItemsById('editor')[0].parent.addChild( newItemConfig );
			}
		} else {
			this.openView('editor');
			this.layout.root.getItemsById('editor')[0].parent.addChild( newItemConfig );
		}		
	}
	
	this.resize = function(){
		this.layout.updateSize();
	};
	
	this.reset = function(){
		this.layout.destroy();
		this.containerEl.empty();
		this.layout.init(this.containerEl, this.viewNames);
	};
};

function uuidv4() {
  return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
	(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
  )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy