EDIT (last ! :-) : A workaround to this is putting the whole UI in an absolutePanel (thx megabyte1024).
It's not perfect since I cannot have a background color in the whole display area but it's at least much more confortable. (link to online test app is updated with this new version) and screen capture of the final version... much better :-)
I have a standalone webapp written in GAS that has a scrollPanel, the whole UI is rather small and occupies only a part of a (even small) display area in the browser window.
What bothers me is that I always have both an horizontal and a vertical scrollbar in the browser window and it interferes with the UI content when I use a mouse or a trackpad to scroll my scrollpanel in the UI window...
So, my question is : is there a way to avoid this, to tell the browser that there is no need to add scrollbars or to define a smaller "webapp area" ?
note that the size of these scroll bars are fully independent from the UI panel size as long as this last one is smaller than the browser window.
Here is a screen capture to illustrate what I'm saying (I do understand that it's a detail but it makes the use of this app sometimes just uncomfortable ;-) Here is also a link to a public version of the app.
Another detail I'd like to solve is the font color in the upper part of this UI : these are textBoxes that I set to 'read only' because I don't want them to be editable (it would give the illusion that the user could modify data which is not the case) and a side effect of this read only status is that fonts are "greyed" ... is there some way to avoid that while keeping the same aspect (except color) on this 'false table' ?
EDIT : found the second question about text color : .setStyleAttribute('color','#000000') as simple as that... too stupid from me not to have found it earlier ;-)
NOTE 2 : interestingly, UI designed with the GUI builder do not suffer the same problem ...
EDIT2 :here is the code of the doGet part (modified to run without functionality but to showup):
var key='0AnqSFd3iikE3dFV3ZlF5enZIV0JQQ0c1a3dWX1dQbGc'
var ss = SpreadsheetApp.openById(key)
var sh = ss.getSheetByName('Sheet1')
var idx = 0
var data = ss.getDataRange().getValues();
var len = data.length;
var scrit = ['All fields','Name','Lastname','Postal Adress','ZIP code','City','Country','email','phone#']
//public version
function doGet() {
var app = UiApp.createApplication().setTitle("BrowseList Test")
.setHeight(420).setWidth(800).setStyleAttribute("background-color","beige").setStyleAttribute('padding','20');
var title = app.createHTML('<B>DATABASE User Interface</B>').setStyleAttribute('color','#888888');
app.add(title);
var scroll = app.createScrollPanel().setPixelSize(750,150)
var vpanel = app.createVerticalPanel();
var cell = new Array();
var cellWidth = [45,135,150,250,50,100]
var row = new Array();
var cHandler = app.createServerHandler('showpicked').addCallbackElement(scroll);
for(vv=0;vv<15;++vv){
row[vv]=app.createHorizontalPanel();
vpanel.add(row[vv]);
for(hh=0;hh<cellWidth.length;++hh){
cell[hh+(vv)*cellWidth.length]=app.createTextBox().setWidth(cellWidth[hh]+"").setTitle('Click to show below')
.setReadOnly(true).setId((hh+vv*cellWidth.length)+'').addClickHandler(cHandler).setStyleAttribute('background','#eeeeff').setStyleAttribute('color','#000000');
row[vv].add(cell[hh+(vv)*cellWidth.length])
}
}
app.add(scroll.add(vpanel))
// Initial populate
var resindex = new Array()
for(vv=0;vv<15;++vv){
resindex.push(vv+1)
for(hh=0;hh<cellWidth.length;++hh){
var rowpos=vv+1+idx
var cellpos = hh+vv*cellWidth.length
cell[cellpos].setValue(data[rowpos][hh]);
}
}
var rHandler = app.createServerHandler('refresh');
//
var slist = app.createListBox().setName('critere').setId('slist').addClickHandler(rHandler);
for(nn=0;nn<scrit.length;++nn){
slist.addItem(scrit[nn]);
}
var search = app.createTextBox().setName('search').setId('search').setTitle('becomes yellow if no match is found');
var modeS = app.createRadioButton('chkmode','strict').setId('chkmodes').addClickHandler(rHandler);
var modeG = app.createRadioButton('chkmode','global').setValue(true).setId('chkmodeg').addClickHandler(rHandler);
var letter = app.createRadioButton('show','letter').setValue(true).setId('letter').addClickHandler(rHandler);
var raw = app.createRadioButton('show','raw data').setId('raw').addClickHandler(rHandler);
var index = app.createHTML('found/'+len).setId('index').setStyleAttribute('color','#aaaaaa');
var grid = app.createGrid(2,10).setWidth('750');
grid.setWidget(1, 0, app.createLabel('search'));
grid.setWidget(1, 1, search);
grid.setWidget(1, 2, modeS);
grid.setWidget(1, 3, modeG);
grid.setWidget(1, 5, slist);
grid.setWidget(1, 6, app.createLabel('show mode'));
grid.setWidget(1, 7, letter);
grid.setWidget(1, 8, raw);
grid.setWidget(1, 9, index);
app.add(grid);
var hidden = app.createHidden('hidden').setId('hidden').setValue(resindex.toString());
cHandler.addCallbackElement(grid).addCallbackElement(scroll).addCallbackElement(hidden);
var result = app.createRichTextArea().setPixelSize(745,160).setId('result')
.setStyleAttribute('background','white').setStyleAttribute('font-family',"Arial, sans-serif")
.setStyleAttribute('font-size','small');
result.setHTML('test ui');
app.add(result).add(hidden);
var sHandler = app.createServerHandler('searchH').addCallbackElement(grid).addCallbackElement(scroll);
search.addKeyUpHandler(sHandler);
rHandler.addCallbackElement(grid).addCallbackElement(scroll);
slist.addChangeHandler(rHandler);
return app
}
A possible solution to get rid of the scroll bar is to use an intermediate Absolute panel. The following code has the scroll bars.
function doGet() {
var app = UiApp.createApplication();
var panel = app.createScrollPanel().setSize('100%', '100%');
var content = app.createButton('Scroll Bars').setSize('100%', '100%');
panel.setWidget(content);
app.add(panel);
return app;
}
And the code bellow has no the scroll bars
function doGet() {
var app = UiApp.createApplication();
var panel = app.createScrollPanel().setSize('100%', '100%');
var subPanel = app.createAbsolutePanel().setSize('100%', '100%');
var content = app.createButton('No Scroll Bars').setSize('100%', '100%');
subPanel.add(content);
panel.setWidget(subPanel);
app.add(panel);
return app;
}
when embedding a HTMLService web app inside a Google Site, it looks like the iFrame gadget has to be about 50 pixels larger than the app height for the vertical scroll bar to disappear. Assuming same thing applies to UiApp.
Related
I have a stackpanel containing 3 panels.
After the user clicks a button on page-1, I want page-2 to become visible.
How can I achieve this?
Edit-1
As I thought the question I asked really is a general one, I did not provide code.
But Serge insas and Zig Mandell asked for code, so here it is.
function doGet()
{
var app = UiApp.createApplication();
var stackPanel = app.createStackPanel().setSize('100%', '100%'); //Create stack panel
var onClick = app.createServerHandler('onClick');
var button = app.createButton('Button on second panel...').setId('btnPageTwo').addClickHandler(onClick);
//add widgets to each stack panel, and name the stacked panels
stackPanel.add(app.createLabel('Text on first panel...'), 'One');
stackPanel.add(button, 'Two');
stackPanel.add(app.createLabel('Text on third Panel...'), 'Three');
app.add(stackPanel); //Add the panel to the application
return app;
}
function onClick(e)
{
Logger.log('In onClick --> show stackPanel "Three" now');
}
In this example I would like to show panel Two at startup and after clicking the button I would like to show panel 3.
I tried using focusPanels, but that didn't help
function doGet()
{
var app = UiApp.createApplication();
var stackPanel = app.createStackPanel().setSize('100%', '100%'); //Create stack panel
// FocusPanels are limited to contain ONE widget
var focusOne = app.createFocusPanel().setId('focusOne');
var focusTwo = app.createFocusPanel().setId('focusTwo');
var focusThree = app.createFocusPanel().setId('focusThree');
// Create panels to overcome the one-widget-limitation of focuspanels
var ver = app.createVerticalPanel().setId('ver');
var hor = app.createHorizontalPanel().setId('hor');
var tab = app.createTabPanel().setId('tab');
var tabOne = app.createVerticalPanel().setId('tabOne');
var tabTwo = app.createHorizontalPanel().setId('tabTwo');
tab.add(tabOne, 'One').add(tabTwo, 'Two');
focusOne.add(ver);
focusTwo.add(hor);
focusThree.add(tab);
var labOne = app.createLabel('Text on first panel...');
var labThree = app.createLabel('Text on second tab of third panel...');
var onClick = app.createServerHandler('onClick');
var button = app.createButton('Button on second panel...').setId('btnPageTwo').addClickHandler(onClick);
ver.add(labOne);
hor.add(button);
tabTwo.add(labThree);
tab.selectTab(1); // Select second tab
//add widgets to each stack panel, and name the stack panel
stackPanel.add(focusOne, 'stackOne').add(focusTwo, 'stackTwo').add(focusThree, 'stackThree');
app.add(stackPanel); //Add the panel to the application
return app;
}
function onClick(e)
{
Logger.log('In onClick --> show focusPanel "Three" now');
var app = UiApp.getActiveApplication();
var focusThree = app.getElementById('focusThree');
focusThree.setFocus(true);
return app;
}
Unfortunately this has been the subject of an enhancement request for quite a while (dec 2012) but I'm afraid Google won't do anything about it since they stopped UiApp development (they recommend switching to HTMLService).
You could eventually use tabPanel instead, this one has all the necessary features.
Test here
code below :
function doGet() {
var app = UiApp.createApplication();
var tabPanel = app.createTabPanel().setSize('100%', '100%').setId('tabP'); //Create tab panel
var onClick = app.createServerHandler('onClick');
var button = app.createButton('Button on second panel...').setId('btnPageTwo').addClickHandler(onClick);
//add widgets to each tab panel, and name the tabed panels
tabPanel.add(app.createLabel('Text on first panel...'), 'One');
tabPanel.add(button, 'Two');
tabPanel.add(app.createLabel('Text on third Panel...'), 'Three');
app.add(tabPanel); //Add the panel to the application
tabPanel.selectTab(1);
return app;
}
function onClick(e){
var app = UiApp.getActiveApplication();
var tabP = app.getElementById('tabP').selectTab(2);
return app;
}
I know how to use the createAnchor to place a link in a panel, however I would like to launch a link from a button. Is this possible, if so how?
After some trail and error I found an easier way of achieving the goal.
I just create an image of the button showing my desired text and use thse setAttribute method of the anchor.
Unfortunally I stll require an AbsolutePanel.
function doGet()
{
// Problem with Google Apps :
// Not possible to click a Button and show a site
// Solutions found on StackOverflow mostly use :
// a) var panel = createAbsolutePanel --> Necessary to create an Absolute Panel
// b) var image = createImage --> The image to be shown
// c) var anchor = createAnchor --> Anchor making it possible to activate a url
// d) position the anchor on top of the image
// e) make anchor and image of same size
// f) make anchor invisible (by zIndex, opacity or visibility)
//
// One of the people showing how this works is http://stackoverflow.com/users/1368381/serge-insas who's
// efforts inspired me to have a look at other possibilities. (thanks !)
//
// Result : next step in making it easy to overcome a limitation of GAS --> no createImage required anymore
// How : using setAttribute('backgroundImage', Url) method of anchor
// Limitation : still required to create an Absolute panel instead of a Vertical panel --> who's next to improve ??
//
// Author : SoftwareTester, may 13th, 2014
var app = UiApp.createApplication().setTitle("Image Anchor");
var picButton = 'https://drive.google.com/uc?id=0BxjtiwHnjnkrTVJiR1g2SlZTLVE'; // Can on be accessed be a few people
var widthButton = 128;
var heightButton = 24;
var anchor = app.createAnchor("", "http://www.opasittardgeleen.nl")
.setHeight(heightButton).setWidth(widthButton)
// .setHeight("150").setWidth("128") // Nice effect !!
// .setHeight("150").setWidth("512") // Even more
.setStyleAttribute('backgroundImage', 'url(' + picButton + ')');
var panel = app.createAbsolutePanel().setWidth('50%').setHeight('50%');
panel.add(anchor,100,50); // I would like to avoid positioning like this and just add the anchor to a Grid or VerticalPanel
app.add(panel);
return app.close();
}
I have answered a similar post with a workaround that works nicely although it is, I admit, a bit complex in regard to what it does...
Here is a test app
and the code reproduced from the other post is below : I used an invisible anchor superimposed to the image but it could of course be anything else... a button or whatever you want.
function doGet(){
var app = UiApp.createApplication().setStyleAttribute("background", "#CCCCFF").setTitle('Anchor Test')
var top = '100PX';// define dimensions and position
var left = '100PX';
var width = '80PX';
var height = '80PX';
var mainPanel = app.createVerticalPanel();
var customAnchor = app.createHorizontalPanel().setId('usethisId')
addStyle(customAnchor,top,left,width,height,'1','1')
var image = app.createImage("https://dl.dropbox.com/u/211279/Time-change-clock_animated_TR80.gif")
addStyle(image,top,left,width,height,'1','1')
var realAnchor = app.createAnchor('This is the Anchor', 'https://sites.google.com/site/appsscriptexperiments/home')
addStyle(realAnchor,top,left,width,height,'2','0')
customAnchor.add(realAnchor);
customAnchor.add(image)
mainPanel.add(customAnchor);
app.add(mainPanel);
return app;
}
function addStyle(widget,top,left,width,height,z,visibility){
widget.setStyleAttributes(
{'position': 'fixed',
'top' : top,
'left' : left,
'width' : width,
'height':height,
'opacity' : visibility,
'zIndex' : z});
}
EDIT : I forgot to mention this post with a button example... exactly what you wanted : How do I open a web browser using google apps script?
EDIT 2
Following Software tester answer, here is a compact version that one can place in a grid or anywhere else... just place the widget called 'container'.
To illustrate I placed it in a grid in the example below/
link to test
code :
function doGet(){
var app = UiApp.createApplication().setTitle("Image Anchor");
var picButton = 'https://dl.dropboxusercontent.com/u/211279/ProgressSpinner.gif';
var img = app.createImage(picButton).setPixelSize(25,25);
var grid = app.createGrid(5,2);
for(n=0;n<5;n++){
grid.setText(n,0,'some text').setBorderWidth(1);
}
var anchor = app.createAnchor(" - ", "https://sites.google.com/site/appsscriptexperiments/").setStyleAttribute('opacity','0').setPixelSize(25,25);
var container = app.createAbsolutePanel().setPixelSize(25,25);
container.add(img,0,0).add(anchor,0,0);
grid.setWidget(4,1,container);
app.add(grid);
return app;
}
The answer EDIT 2 of Serge Insas provides extra flexibility using a grid.
Improving the world little by little by learning and using each others good ideas, also holds for software of course. Thanks again Serge!
I noticed a few differences that might or might not be of interest in certain situations.
I always try to specify constants (like width and height) and minimize using similar code like .setPixelSize(width, height) making it easier 'not to forget something while changing'. That's why I prefer to avoid creating a separate image object.
Serge Insas uses .setStyleAttribute('opacity','0'); while I'm using .setStyleAttribute('backgroundImage', url); . I don't know what are the pro's and con's of both possibilities.
Below is the generalized code
function doGet()
{ // Generalized version using : image + opacity + container
var app = UiApp.createApplication().setTitle("Image Anchor");
var width = 25;
var height = 25;
var urlImage = 'https://dl.dropboxusercontent.com/u/211279/ProgressSpinner.gif';
var urlAnchor = "https://sites.google.com/site/appsscriptexperiments/";
var grid = createImageAnchor(urlImage, urlAnchor, width, height);
app.add(grid);
return app;
}
and the function createImageAnchor can be
function createImageAnchor(urlImage, urlAnchor, width, height)
{ // Using backgroundImage
var app = UiApp.getActiveApplication();
var anchor = app.createAnchor("", urlAnchor).setPixelSize(width, height)
.setStyleAttribute('backgroundImage', 'url(' + urlImage + ')');
var panel = app.createAbsolutePanel().setPixelSize(width, height).add(anchor, 0, 0);
var grid = app.createGrid(1, 1).setWidget(0, 0, panel); // Create grid and put anchor in it
return grid;
}
or
function createImageAnchor(urlImage, urlAnchor, width, height)
{ // Using opacity
var = UiApp.getActiveApplication();
var image = app.createImage(urlImage).setPixelSize(width, height);
var anchor = app.createAnchor("", urlAnchor) .setPixelSize(width, height)
.setStyleAttribute('opacity','0');
var panel = app.createAbsolutePanel().setPixelSize(width, height)
.add(image, 0, 0).add(anchor, 0, 0); // Same position for image and anchor
var grid = app.createGrid(1, 1).setWidget(0, 0, panel); // Create grid and put image + anchor in it
return grid;
}
Using createImageAnchor makes it easier to use this 'combined object' anywhere in code, especially after adding it into a library.
As I'm new to GAS (started may 7th after 5 years of inactivity) I know I need to learn a lot and would like to know what the pro's and con's of different methods are.
I have a scroll panel but I cann't put the visible title in it:
var app = UiApp.createApplication();
var panel = app.createVerticalPanel()
.setId('panel')
.setTitle("This is my Title");
panel.add(app.createHidden('checkbox_total', arrayList.length));
// add 1 checkbox + 1 hidden field per item
for(var i = 0; i < arrayList.length; i++){
var checkbox = app.createCheckBox().setName('checkbox_isChecked_'+i).setText(arrayList[i][0]);
panel.add(checkbox);
}
var handler = app.createServerHandler('submit').addCallbackElement(panel);
panel.add(app.createButton('Submit', handler));
var scroll = app.createScrollPanel().setPixelSize(500, 300).setTitle("My second try to put a title");
scroll.add(panel);
app.add(scroll);
mydoc.show(app);
What is going wrong with my titles?
It seems you are making a confusion on what 'title' means in UiApp... They are actually small popups that appear when your mouse hovers the widget, see illustration below.
If you intend to get a title shown in your Ui (in the common sense of 'title') use Label or HTML widgets.
One exception though since you are using uiApp in a spreadsheet or a document, the title of the app itself is shown as a title. See example below :
var app = UiApp.createApplication().setTitle('app title');
The label must be putted in UiApp object, this way:
var app = UiApp.createApplication();
var label = app.createLabel("This is my label, ie, the user visible title").setStyleAttribute("fontSize", 18);
app.add(label);
I want to use a web page to gather data in stages. When the user clicks next I want to have the current panel removed from the app and a new panel added and I am having trouble making it work.
Here is my current code:
function doGet() {
//Create Application
var app = UiApp.createApplication();
//Set Application Title
app.setTitle("New Lead Form");
/////////////////////////////////Step 1///////////////////////////////////
// Create a Panel for Step 1 Data
var nextbuttonstep1 = app.createButton("Next");
// Create the entry form, a 6 x 2 grid with text boxes for First Name, Last Name, Phone Number, Email, & Property State that is then added to a vertical panel
var grid = app.createGrid(5, 2);
var propertystate = app.createListBox().setName('propertystate').setId('propertystate')
propertystate.addItem('AL');
propertystate.addItem('FL');
propertystate.addItem('IN');
propertystate.addItem('KY');
grid.setWidget(0, 0, app.createLabel('First Name:'));
grid.setWidget(0, 1, app.createTextBox().setName('firstname').setId('firstname'));
grid.setWidget(1, 0, app.createLabel('Last Name:'));
grid.setWidget(1, 1, app.createTextBox().setName('lastname').setId('lastname'));
grid.setWidget(2, 0, app.createLabel('Phone Number:'));
grid.setWidget(2, 1, app.createTextBox().setName('phonenumber').setId('phonenumber'));
grid.setWidget(3, 0, app.createLabel('Email Address:'));
grid.setWidget(3, 1, app.createTextBox().setName('emailaddress').setId('emailaddress'));
grid.setWidget(4, 0, app.createLabel('Property State:'));
grid.setWidget(4, 1, propertystate);
// Create a vertical panel and add the grid to the panel
var step1panel = app.createFlowPanel();
step1panel.add(grid);
step1panel.add(nextbuttonstep1);
app.add(step1panel)
var handler = app.createServerHandler('proceedtostep2');
handler.addCallbackElement(step1panel)
nextbuttonstep1.addClickHandler(handler);
return app;
}
function proceedtostep2(eventInfo) {
var parameter = eventInfo.parameter;
var panel =parameter.step1panel;
var app = UiApp.getActiveApplication();
app.remove(panel);
return app;
}
Panels don't show up as parameters, only input fields do. You will have to retrieve the panel you want to remove using it's ID:
...
// Create a vertical panel and add the grid to the panel
var step1panel = app.createFlowPanel().setId('someUniqueIdString');
...
and
...
function proceedtostep2(eventInfo) {
var app = UiApp.getActiveApplication();
app.remove(app.getElementById('someUniqueIdString'));
return app;
}
But removing all elements from a application window does not close the window. The Window only remains in a ugly state.
Adding other widgets after the remove is OK but you should use some fresh ID's. If you use old ID strings with new widgets bad things happen.
I have created two user interfaces. How can I close the first one and activate the next? Is it possible to have two UI under Google apps script?
I have try something like:
var app = UiApp.getActiveApplication();
app.add(app.loadComponent("APPGui"));
var panel1 = app.getElementById("LoginPanel1");
panel1.setVisible(false);
return app;
The easiest way is probably to design both panels in the same GUI builder, one over each other in 2 separate panels, the 'login panel' being above the other it will mask the other one when active. As you set it 'invisible', you'll see the one underneath.
Depending on your use case the login panel might hide all or only a part of your main panel.
The GUI builder has all the necessary tools to decide which is in front or backwards.
Here's and example of three dialogs shown one after the other, maintaining state/data between them via the CacheService object.
(You could use UserProperties, ScriptProperties or even a Hidden Field as an alternative, each has their own scope though...)
Hopefully this makes sense without explaining what each dialog in the UI Builder contains.
function showDialog1(){
var app = UiApp.createApplication();
app.add( app.loadComponent("Dialog1") );
SpreadsheetApp.getActiveSpreadsheet().show(app);
}
function onDialog1OKButton(e){
CacheService.getPrivateCache().put("n1", e.parameter.n1);
var app = UiApp.getActiveApplication();
var d2 = app.loadComponent("Dialog2");
app.add(d2);
SpreadsheetApp.getActiveSpreadsheet().show(app);
}
function onDialog2OKButton(e){
var c = CacheService.getPrivateCache();
c.put("n2", e.parameter.n2);
var app = UiApp.getActiveApplication();
app.add(app.loadComponent("DialogResult"));
var n1 = c.get("n1");
var n2 = c.get("n2");
var l = app.getElementById("Label2");
l.setText( "" + n1 + " + " + n2 + " = " + (parseInt(n1) + parseInt(n2)) );
SpreadsheetApp.getActiveSpreadsheet().show(app);
}
I prefer to build multiple GUI. With this code you can jump between them.
function doGet() {
var app = UiApp.createApplication();
var base0 =app.createAbsolutePanel().setId('GUI_base0').setHeight('630px').setWidth('1125px');
app.createAbsolutePanel().setId('GUI_base1'); // create all abs_panells but not use
// you need to create all abspanels if you want to jump between them
app.createAbsolutePanel().setId('GUI_base2'); // create here all the absolute panels (1 for every GUI)
// app.createAbsolutePanel() ... GUI3, GUI4 ...
var component0 = app.loadComponent("GUI_password"); // load first GUI (his name is "password"
/// this is an example of code for the 1st GUI ////////////////////
/// I can check if the user can see the second GUI
var label_ID = app.getElementById('LB_ID');
var user = Session.getActiveUser().getEmail();
if ( user == 'XXX#yyyy.com' ) {
label_ID.setText(user).setTag(user); // only show if ....
}
////////////////////////////////////////////////////////////////////
base0.add(component0); // GUI_password over absolute panel
app.add(base0);
// handler Button1 // we can show a button only if the password is correct or is a valid user or ...
app.getElementById('BT_jump').addClickHandler(app.createServerHandler('NOW_gui1'));
return app;
};
function NOW_gui1(e) {
var app = UiApp.getActiveApplication();
var base0 = app.getElementById("GUI_base0").setVisible(false); // hide 1st abs_panel created with code
var base2 = app.getElementById("GUI_base2").setVisible(false); // hide 3rd abs_panel created with code
/// hide all others abs_panel
var base1 = app.createAbsolutePanel().setId('GUI_base1').setHeight('630px').setWidth('1125px'); // maybe get by ID ??, but this work
var component1 = app.loadComponent("GUI_1"); // load the second GUI
base1.add(component1); // load GUI_1 over 2n absolute panel
app.add(base1);
// HERE THE CODE OF THE GUI_1
// handler Button2
app.getElementById('BT_jump_1_to_2').addClickHandler(app.createServerHandler('NOW_gui2'));
return app;
};