Hi,

this should give you an idea on how to achieve "popups" in HTML. This code is untested but it should give you a start.
First of all you need 2 containers (DIV) at the end of your document:

<div id="backdrop" class="backdrop"></div>

<div id="popup" class="popup">
    <div id="popup_container"></div>
    <button id="popup_btn1"></button>
    <button id="popup_btn2"></button>
</div>


in css you need 4 classes:

.backdrop {
    position: fixed;
    opacity: 0.3;
    background-color: rgb(0,0,0);
    top: 0;
    left: 0;
    width:100%;
    height: 100%
    display: none;
}

.backdrop.active {
    display: block;
}

.popup.active {
    display:block;
}

.popup {
    display:none;
    <!-- add your styling here -->
}

In Javascript you need something like this:

function showConfirm(message, callback) {
    showPopup(message, "Yes", "No", callback);
}

function showPopup(content, button1desc, button2desc, callback) {
    $('#popup').addClass("active");
    $('#backdrop').addClass("active");
    $('#popup_container').html(content);
    $('#popup_btn1').html(button1desc);
    if (button2desc) {
        $('#popup_btn2').html(button2desc).show();
    } else {
        //If we don't get a 2nd description it's a alert-message with one button
        $('#popup_btn2').hide();
    }
    $('#popup_btn1').off().on('click', function() {
        disable(); //First disable our popup
        if (callback) callback(0); //Returns the index of the button clicked
    });

    $('#popup_btn2').off().on('click', function() {
        disable();
        if (callback) callback(1);
    });

    var disable = function() {  
         $('#popup').removeClass("active");
         $('#backdrop').removeClass("active");
         $('#popup_container').html('');
    }
}

//Example of calling the confirm-message

showConfirm("Do you really want to delete?", function(choice) {
    if (choice===0) 
        alert("You pressed yes");
    } else {
        alert("You pressed no");
    }
});

Edit: You can achieve this also by using native-javascript: document.getElementById("popup_container").innerHTML = content; for example

Hei,

if you want to use Darkest-Dark theme:

Go to <eclipse-workspace>/.metadata/.plugin/org.eclipse.core.runtime/.settings/com.intersys.eclipse.studio.ui.prefs

and add this. I didn't found all colors but It's a good start. Edit theme in preferences if you find a color that you don't like or maybe isn't set by me. You can also reset to default-settings by removing every Studio_Color-Setting.

I also tweaked the colors for specific things because using the theme strictly results in too much blue ($-commands was blue etc.)

Studio_Color_BG_false=0,0,0

Studio_Color_BG_true=37,37,37

Studio_Color_brace=255,255,255

Studio_Color_bracket=255,255,255

Studio_Color_classmethodname=189,214,255

Studio_Color_classname=210,81,81

Studio_Color_collationname=189,214,255

Studio_Color_cosBitlogicOperator=210,81,81

Studio_Color_cosClassName=239,191,143

Studio_Color_cosCommandWordDeprecated=210,81,81

Studio_Color_cosCommandword=210,81,81

Studio_Color_cosDollarName=210,81,81

Studio_Color_cosDollarNameDeprecated=210,81,81

Studio_Color_cosEmbedDelim=0,128,0

Studio_Color_cosEmbeddingStartOrEnd=210,81,81

Studio_Color_cosExtrinsic=210,81,81

Studio_Color_cosGlobalname=141,203,226

Studio_Color_cosGlobalname_bold=true

Studio_Color_cosIOFormat=255,255,255

Studio_Color_cosJsonBrace=255,255,255

Studio_Color_cosJsonBracket=255,255,255

Studio_Color_cosJsonKeyword=210,81,81

Studio_Color_cosKeyword=210,81,81

Studio_Color_cosLabel=210,81,81

Studio_Color_cosLabelname=141,203,226

Studio_Color_cosLocalname=136,214,255

Studio_Color_cosMacroArgsAnything=136,214,255

Studio_Color_cosMacroCall=210,81,81

Studio_Color_cosOperator=255,255,255

Studio_Color_cosPPCommand=141,203,226

Studio_Color_cosPPFunc=210,81,81

Studio_Color_cosPattern=210,81,81

Studio_Color_cosSQLFieldName=210,81,81

Studio_Color_cosstring=255,198,0

Studio_Color_csqlIdentifier=255,198,0

Studio_Color_delim=255,255,255

Studio_Color_foreignkeyname=121,171,255

Studio_Color_htmlAttributeName=121,171,255

Studio_Color_htmlAttributeValue=255,255,255

Studio_Color_indexname=189,214,255

Studio_Color_indexpropertyname=255,255,255

Studio_Color_jsKeyword=141,203,226

Studio_Color_jsOperator=255,255,255

Studio_Color_jsString=255,198,0

Studio_Color_jsonString=255,198,0

Studio_Color_methodname=255,255,255

Studio_Color_name=255,255,255

Studio_Color_number=129,180,71

Studio_Color_parameter=121,171,255

Studio_Color_parametername=189,214,255

Studio_Color_paren=255,255,255

Studio_Color_propertyname=189,214,255

Studio_Color_queryname=189,214,255

Studio_Color_sqlstring=255,255,128

Studio_Color_storagename=189,214,255

Studio_Color_triggername=189,214,255

Studio_Color_udlDocComment=201,222,12

Studio_Color_udlKeyword=80,175,226

Studio_Color_udlMCKeywordName=255,255,255

Studio_Color_udlMCKeywordValue=129,180,71

Studio_Color_udlNumericLiteral=129,180,71

Studio_Color_udlStringLiteral=255,198,0

Studio_Color_xdataname=189,214,255

Studio_Color_xmlAttributeName=255,255,255

That's not garbage. It's the ZIP-File itself I think. It's in binary and compressed. You need to decompress it. 

I would do it like that:

s sc=$zf(-1, "sftp command here &>/dev/null; unzip cmd here &>/dev/null")

 

 

I prefer REST. 

The built-in transportation is good way for ZEN but the fact that the most plugins can't handle it well is a good reason to use REST. Some plugins just want an URL to do a simple jQuery-AJAX-Call and parsing that into a callback function. I some cases you can submit a function serving the data instead of an URL but working that around that is a bit harsh and not really comfortable in my opinion (Typeahead for example). 

Let's assume that you code a ZEN-App working with Cordova and in the Browser as well. The data comes from the built-in feature of ZEN-Mojo. Everything works fine. 

Now a new iOS or Android-Update is distributed that have new security guidelines and Cordova dies out of it (maybe some stuff in the webview are not allowed anymore but is needed). That's why I keep my data-transportation as flexible as possible. Always thinking of "This 3rd party tool is maybe not supported anymore in the future. What are my alternatives?". In this case a native app without ZEN. 

You can still use the built-in feature by calling the methods that the REST calls. You are in the CSP-Enviroment in both cases. 

We are using Hashtags to load the templates.  

Thats the URL for example:

www(.)mypage.com/myzenapp/mojoclass.cls#Login?r=Dashboard

or

www(.)mypage.com/myzenapp/mojoclass.cls#Dashboard

or

www(.)mypage.com/myzenapp/mojoclass.cls

Our custom-script CAJAXNavigation uses onHashChange-Callback (that we bound to zenPage.onHashChanged). There we change the layout with pop and push document. This works very well with the browser history stack. The browser manages hashtag-changes automatically.

With this concept we are changing the layout by simply clicking an anchor with href="#MyLayout"

 <mojo:documentView
 id="mainView"
ongetdata="return zenPage.getContent('data',key,criteria);"
ongetlayout="return zenPage.getContent('layout',key,criteria);"
developerMode="false"
effectsEnabled="false"
maxPanels="2"
onload="zen('mainView').setDocumentKey(CAJAXNavigation.GetInstance().GetCurrentPage() || 'Home');zen('mainView').setLayoutKey(CAJAXNavigation.GetInstance().GetCurrentPage() || 'Home')"
>

Hi Fabio,

 

in template dispatch mode you can have a super layout class where you can have a collection of clientmethods implemented in every layout extending from it. Our menu is initialized by every layout each time it loads. You can define a method that initialize the menuitems as active for example or load dynamic data.

I'm sure that there is a better way of handling menus with onepage-concepts. Menus in general are difficult in client oriented template systems.

We implemented a way to display two layouts in a single pane; a detail part in a modal for example. I think this technique can be used to display a dynamic-menu. Just create a menu-layout that just provide the layout by criteria-parameters. In your superclass add a method that calls this layout and injects it into a div provided by every layout. Example:

Content-Layout:

var that = this;

return {...,children:[{type:'$div',id:'menu'}], $executeCodeAfterChildren: function() {  that.initMenu({"parm":"value"}) }}

Super-Layout:

ClientMethod initMenu(view, criteria) [ language = "javascript] 

{

    var layout = zenPage.getContent("layout","Menu",criteria)

     view = view || zen('mainView');
     var menucontainer view.getComponentById("menu"); *****

    //Empty all children-elements first before refresh and add the layout

    menucontainer.$children = menucontainer.children = layout.children;

    //rerenders menu

    menucontainer.$refresh();

}

***** I added following code in createLayoutObjects before return in bootstrap-plugin to get layout-elements by id instead of index:

 

var mainView instance.$documentView; //zen('mainView');
if (typeof mainView._IDIndex === "undefined") {
mainView._IDIndex [];
}
if (instance && instance.id) {
mainView._IDIndex[instance.id]=instance;
}
if (!mainView.getComponentById) {
mainView.getComponentById function(id) {
return this._IDIndex[id] || null;
}
}

 

 

Hi Daniel,

use bootstrap and javascript. There are a lot of frameworks out there. You can use ZEN-Mojo as framework for example. I wouldn't try to work with angular.js because it's more expert-level Javascript and templating. But when you do it correctly it could be the best option out there.

Responsive apps need a lot of CSS and Javascript-Knowledge. Bootstrap gives you an idea and a good basic on how to achieve that. Maybe it's enough to redesign your old CSP-Pages to bootstrap to make it responsive enough.

The easiest way to work with an REST-API is jQuery-ajax (for http-requests in general)

I also use a Java-Class like structure for my scripts. Here is an example:

function MyClass(initParm, initListener) {
    var m_PrivateProperty = initParm || null;
    var m_EventListener = (typeof initListener === "function" ? initListener : new MyClass.OnEventListener())
    this.PublicProperty = "Hello World";
    
    this.getOnEventListener = function() {
        return m_EventListener;
    }
    this.setOnEventListener = function(EventListener) {
        m_EventListener = EventListener;
    }
    
    var privateFunc = function(parms) {
        return stuff;
    }
    this.publicFunction = function(parms) {
        return privateFunc(stuff)
    }
    
    this.getPrivateProperty = function() {
        return m_PrivateProperty;
    }
    this.setPrivateProperty = function(PrivateProperty) {
        m_PrivateProperty;
    }
    
    this.sendToServer = function() {
        var that = this;
        jQuery.ajax("/rest/do/things/", {
            method : "GET",
            asynch : true,
            data : {
                parm1 : this.getPrivateProperty()
            }
        }).done(function(a,b,c) {
            //we need "that" here. "this" is not MyClass anymore; could be jqXHR-Object or window
            that.getOnEventListener().OnThingsListener(a);
        }).error(function(a,b,c) { 
            //read jquery-doc
        })
    }
    
}
MyClass.OnEventListener = function() {
    this.OnClickListener = function(parms) { };
    this.OnThingsListener = function(parms) { };
}
 
var a = new MyClass(parm1,parm2)

That should do it for both types:

ClientMethod StartEvent(
key,
eventType,
value) [ Language = javascript ]
{
if (zenPage.templateDispatchMode) {
var newArea "events-" key.split(":")[0];
if (zenPage.currArea!==newArea) {
zenPage.currArea newArea;
zenPage.loadTemplateNS(zenPage.templateDispatchBaseNamespace+"/events",key.split(":")[0],newArea);
}
}
var st zenPage.getTemplate();
st[eventType](key,value,'mainView');
}

As temp  solution you can use href:"javascript:zenPage.StartEvent('yourkey','onclick','yourvalue');"

In your Zenpage add following ClientMethod to call events (sometimes useful for handling zen-events with bootstrap-plugins):

 

ClientMethod StartEvent(
key,
eventType,
value) [ Language = javascript ]
{
var newArea "events-" key.split(":")[0];
if (zenPage.currArea!==newArea) {
zenPage.currArea newArea;
zenPage.loadTemplateNS(zenPage.templateDispatchBaseNamespace+"/events",key.split(":")[0],newArea);
}
var st zenPage.getTemplate();
st[eventType](key,value,'mainView');
}