OSDN Git Service

Merge pull request #103 from mtsgi/feature/kit-disabled-kaf
[kit/kit.git] / system.js
index 757c6e9..b8f6871 100644 (file)
--- a/system.js
+++ b/system.js
@@ -52,7 +52,9 @@ function kit() {
     if( !localStorage.getItem( "kit-appdir" ) ) localStorage.setItem( "kit-appdir", "./app/" );
     S.appdir = localStorage.getItem( "kit-appdir" );
 
-    if( localStorage.getItem( "kit-installed" ) ) System.installed = JSON.parse( localStorage.getItem( "kit-installed" ) );
+    if( localStorage.getItem('kit-installed') ) System.installed = JSON.parse( localStorage.getItem('kit-installed') );
+
+    if( localStorage.getItem('kit-screentime') ) KWS.screenTime = JSON.parse( localStorage.getItem('kit-screentime') );
 
     if( localStorage["kit-userarea"] ) System.userarea = JSON.parse(localStorage["kit-userarea"]);
     if( localStorage["kit-recycle"] ) System.recycle = JSON.parse(localStorage["kit-recycle"]);
@@ -64,11 +66,11 @@ function kit() {
     else  clockmove = setInterval( System.clock, 10 );
 
     if ( localStorage.getItem("kit-shutted-down") == "false" ) {
-        Notification.push( "お知らせ", "kitは前回終了時、正しくシャットダウンされませんでした。", "system" );
+        Notification.push("お知らせ", "kitは前回終了時、正しくシャットダウンされませんでした。", "system");
     }
     localStorage.setItem("kit-shutted-down", false);
 
-    Notification.push( "kitへようこそ", localStorage["kit-username"] + "さん、こんにちは。", "system", null, null, 'documents/icon.png', [
+    Notification.push("kitへようこそ", localStorage["kit-username"] + "さん、こんにちは。", "system", null, null, 'documents/icon.png', [
         {
             label: 'kitについて',
             func: () => System.launch( 'settings', {'view': 'about'} )
@@ -502,86 +504,130 @@ async function launch( str, args, dir ) {
         try{
             $.getJSON( S.launchpath[_pid] + '/define.json', appData ).fail( () => {
                 Notification.push('kitアプリをロードできません。', `${str}を展開できませんでした。`, 'system');
+                System.launchLock = false;
+                pid++;
             } );
         }
         catch(error){
             Notification.push( "System Error", error, "system" );
+            System.launchLock = false;
         }
     }
 }
 
-async function appData( data ) {
+async function appData(data) {
+    let _support = {
+        fullscreen: false,
+        resize: false,
+        darkmode: false,
+        kaf: true,
+        multiple: true
+    }, _size = {}, _resize = false;
+    if (data.support) _support = {
+        fullscreen: typeof data.support.fullscreen == "undefined" ? false : data.support.fullscreen,
+        resize: typeof data.support.resize == "undefined" ? false : data.support.resize,
+        darkmode: typeof data.support.darkmode == "undefined" ? false : data.support.darkmode,
+        kaf: typeof data.support.kaf == "undefined" ? true : data.support.kaf,
+        multiple: typeof data.support.multiple == "undefined" ? true : data.support.multiple
+    }
+    if (data.size) _size = {
+        width: data.size.width || 'auto',
+        height: data.size.height || 'auto'
+    };
+    if (data.resize) _resize = data.resize;
+    const defobj = {
+        id: data.id || null,
+        name: data.name || 'アプリ名なし',
+        icon: data.icon || 'none',
+        version: data.version || null,
+        author: data.author || null,
+        support: _support,
+        size: _size,
+        resize: _resize,
+        view: data.view || 'default.html',
+        script: data.script || 'none',
+        css: data.css || 'none'
+    }
+    if (!data.id || !data.version || !data.author) {
+        Notification.push('起動エラー', '起動に失敗しました。詳細情報を得るためには、デバッグモードを有効化してください。', 'system');
+        Notification.push('debug', '起動エラー:id, version, authorは必須定義項目です。', 'system');
+        System.launchLock = false;
+        return;
+    }
+    if (defobj.support.multiple == false) {
+        if (Object.values(process).map(p => p.id).includes(defobj.id)) {
+            Notification.push('多重起動エラー', `アプリケーション「${defobj.name}」の多重起動は許可されていません。`, 'system');
+            System.launchLock = false;
+            return;
+        }
+    }
     let _pid = pid;
-    process[String( _pid )] = {
-        id: data.id,
+    process[String(_pid)] = {
+        id: defobj.id,
         time: System.time.obj.toLocaleString(),
         isactive: false,
         preventclose: false,
-        title: data.name
+        title: defobj.name
     };
     System.appCache[System.launchpath[pid]] = data;
     app = new App(_pid);
     let _taskAppend = `<span id='t${_pid}'>`;
-    if( data.icon && data.icon != "none" ) _taskAppend += `<img src='${S.launchpath[_pid]}/${data.icon}'>`;
-    _taskAppend += `<span id='tname${_pid}'>${data.name}<span></span>`;
-    $( "#tasks" ).append( _taskAppend );
-    $( "#t" + _pid ).addClass( "task" ).on({
+    const _iconPath = app.getPath(defobj.icon).toString();
+    const _viewPath = app.getPath(defobj.view).toString();
+    if(defobj.icon != 'none') _taskAppend += `<img src='${_iconPath}'>`;
+    _taskAppend += `<span id='tname${_pid}'>${defobj.name}<span></span>`;
+    $("#tasks").append(_taskAppend);
+    $("#t" + _pid).addClass("task").on({
         click: function() {
-            if( $(this).hasClass("t-active") || $(this).hasClass("task-min") ) KWS.min( _pid );
-            else{
+            if ( process[_pid].isactive || $(this).hasClass("task-min") ) KWS.min(_pid);
+            else {
                 $("#w"+_pid).css("z-index", KWS.windowIndex + 1);
                 KWS.refreshWindowIndex();
             }
         },
         mouseenter: function() {
-            $( "#task-ctx-name" ).text( data.name );
-            if( data.icon && data.icon != "none" ) $( "#task-ctx-img" ).attr( "src", System.launchpath[_pid] + "/" + data.icon );
-            else $( "#task-ctx-img" ).hide();
-            $( "#task-ctx-ver" ).text( data.version + "/pid:" + _pid );
-            $( "#task-ctx-info" ).off().on( "click", function() { System.appInfo( _pid )} );
-            $( "#task-ctx-sshot" ).off().on( "click", function() { S.screenshot(_pid, true) } );
-            $( "#task-ctx-min" ).off().on( "click", function() { KWS.min( String(_pid) ) } );
-            if( $(this).hasClass("t-active") ) $( "#task-ctx-front" ).hide();
-            else $( "#task-ctx-front" ).show();
-            $( "#task-ctx-front" ).off().on( "click", function() {
-                $("#w"+_pid).css("z-index", KWS.windowIndex + 1);
+            $("#task-ctx-name").text(defobj.name);
+            if(defobj.icon != "none") $("#task-ctx-img").show().attr("src", _iconPath);
+            else $("#task-ctx-img").hide();
+            $("#task-ctx-ver").text(`v${defobj.version} pid${_pid}`);
+            $("#task-ctx-info").off().on("click", function() { System.appInfo(_pid)});
+            $("#task-ctx-sshot").off().on("click", function() { System.screenshot(_pid, true) });
+            $("#task-ctx-min").off().on("click", function() { KWS.min(String(_pid)) });
+            if($(this).hasClass('t-active')) $('#task-ctx-front').hide();
+            else $('#task-ctx-front').show();
+            $("#task-ctx-front").off().on('click', function() {
+                $("#w"+_pid).css("z-index", KWS.windowIndex+1);
                 KWS.refreshWindowIndex();
-            } );
-            $( "#task-ctx-close" ).off().on("click", () => System.close(_pid));
-            $( "#task-ctx-kill" ).off().on("click", () => System.kill( data.id));
+            });
+            $("#task-ctx-close").off().on("click", () => System.close(_pid));
+            $("#task-ctx-kill").off().on("click", () => System.kill(defobj.id));
             const _ctxleft = $(this).offset().left, _ctxtop = window.innerHeight - $(this).offset().top;
-            if( _ctxleft != $( "#task-ctx" ).offset().left ) $( "#task-ctx" ).hide();
-            $( "#task-ctx" ).css( "left", _ctxleft ).css( "bottom", _ctxtop ).show();
+            if( _ctxleft != $("#task-ctx").offset().left ) $("#task-ctx").hide();
+            $("#task-ctx").css("left", _ctxleft).css("bottom", _ctxtop).show();
         }
     } );
-    $( "section, #kit-tasks" ).on( "mouseenter", function() {
-        $( "#task-ctx" ).fadeOut( 200 );
-    } );
-    $( "#t" + _pid ).on({
+    $("section, #kit-tasks").on('mouseenter', function() { $('#task-ctx').fadeOut(200) });
+    $("#t" + _pid).on({
         mouseenter: () => {
-            prevWindowIndex = $( "#w" + _pid ).css( "z-index" );
-            $( "#w" + _pid ).addClass( "win-highlight" );
+            prevWindowIndex = $("#w" + _pid).css('z-index');
+            $("#w" + _pid).addClass('win-highlight');
         },
-        mouseleave: () => $( "#w" + _pid ).removeClass( "win-highlight" )
+        mouseleave: () => $("#w" + _pid).removeClass('win-highlight')
     });
-
     let _windowAppend = "<div id='w" + _pid + "'><div id='wt" + _pid + "' class='wt'><i class='wmzx'><span id='wm" + _pid + "'></span>";
-    if( data.support && data.support['fullscreen'] == true ) _windowAppend += "<span id='wz" + _pid + "'></span>";
+    if( defobj.support.fullscreen == true ) _windowAppend += "<span id='wz" + _pid + "'></span>";
     _windowAppend += "<span id='wx" + _pid + "'></span></i>";
-    if( data.icon && data.icon != "none" ) _windowAppend += "<img src='" + S.launchpath[_pid] + "/" + data.icon + "'>";
-    _windowAppend += "<span id='wtname" + _pid + "'>" + data.name + "</span></div><div class='winc winc-" + data.id + "' id='winc" + _pid + "'></div></div>";
-    $( "#desktop-" + currentDesktop ).append( _windowAppend );
+    if( defobj.icon != "none" ) _windowAppend += "<img src='" + _iconPath + "'>";
+    _windowAppend += "<span id='wtname" + _pid + "'>" + defobj.name + "</span></div><div class='winc winc-" + defobj.id + "' id='winc" + _pid + "'></div></div>";
+    $("#desktop-" + currentDesktop).append(_windowAppend);
 
-    if( data.support && data.support['darkmode'] == true ) $("#winc"+_pid).addClass("winc-darkmode");
+    if( defobj.support.darkmode == true ) $("#winc"+_pid).addClass('winc-darkmode');
     if( KWS.darkmode ) $("#winc"+_pid).addClass("kit-darkmode");
-
-    if( data.size ){
-        $("#winc"+_pid).css("width", data.size.width).css("height", data.size.height);
-    }
-    if( data.resize ){
-        let _minwidth = 200, _minheight = 40;
-        if( data.resize.minWidth ) _minwidth = data.resize.minWidth;
-        if( data.resize.minHeight ) _minheight = data.resize.minHeight;
+    
+    $("#winc"+_pid).css("width", defobj.size.width).css("height", defobj.size.height);
+    if( defobj.resize ){
+        let _minwidth = defobj.resize.minWidth ? defobj.resize.minWidth : 200;
+        let _minheight = defobj.resize.minHeight ? defobj.resize.minHeight : 40;
         $("#winc"+_pid).windowResizable({
             minWidth: _minwidth,
             minHeight: _minheight
@@ -611,34 +657,35 @@ async function appData( data ) {
         KWS.refreshWindowIndex();
     } ).css( "left", windowPos + "px" ).css( "top", windowPos + "px" ).css( "z-index",  KWS.windowIndex );
     KWS.refreshWindowIndex();
-    $( `#wm${_pid}` ).addClass( "wm fa fa-window-minimize" ).on("click", () => KWS.min( _pid ) );
-    $( `#wz${_pid}` ).addClass( "wz fas fa-square" ).on("click", () => KWS.max( _pid ) );
-    $( `#wx${_pid}` ).addClass( "wx fa fa-times" ).on("click", () => System.close( _pid )  );
-    $( "#winc" + _pid ).resizable( {
-        minWidth: "200"
-    } ).load( System.launchpath[_pid] + "/" + data.view, (r, s, x) => {
-        if( s == "error" ){
-            Notification.push("起動に失敗:" + x.status, x.statusText);
+    if(defobj.support.fullscreen == true) $(`#wt${_pid}`).on("dblclick", () => KWS.max(_pid));
+    $( `#wm${_pid}` ).addClass("wm fa fa-window-minimize").on("click", () => KWS.min( _pid ));
+    $( `#wz${_pid}` ).addClass("wz fas fa-square").on("click", () => KWS.max( _pid ));
+    $( `#wx${_pid}` ).addClass("wx fa fa-times").on("click", () => System.close( _pid ));
+    $( "#winc" + _pid ).resizable().load(app.getPath(defobj.view), (r, s, x) => {
+        if(s == "error"){
+            Notification.push("起動に失敗しました " + x.status, 'テンプレートにアクセスできません。' + x.statusText, 'system');
+            System.launchLock = false;
+            pid++;
             return false;
         }
-        if( !data.script || data.script != "none" ) $.getScript( System.launchpath[_pid] + "/" + data.script, () => {
-            if( !data.support || data.support['kaf'] != false ) App.kaf(_pid);
-            pid++;
-        }).fail( () => {
-            App.kaf(_pid);
+        if( defobj.css != "none" && !document.querySelector(`#kit-style-${defobj.id}`) ){
+            $("head").append('<link href="' + app.getPath(defobj.css) + '" rel="stylesheet" id="kit-style-' + data.id + '"></link>');
+        }
+        if(defobj.script != "none") $.getScript(app.getPath(defobj.script), () => {
+            if( defobj.support.kaf == true ) App.kaf(_pid);
             pid++;
+        }).fail(() => {
+            if( defobj.support.kaf == true ) App.kaf(_pid)
+            pid ++;
         });
-        else if( !data.support || data.support['kaf'] != false ){
+        else if( defobj.support.kaf == true ){
             App.kaf(_pid);
             pid++;
         }
         else pid++;
-        if( data.css != "none" && $("#kit-style-"+data.id).length == 0 ){
-            $( "head" ).append( '<link href="' + System.launchpath[_pid] + '/' + data.css + '" rel="stylesheet" id="kit-style-' + data.id + '"></link>' );
-        }
         localStorage.setItem( "kit-pid", pid );
         System.launchLock = false;
-    } );
+    });
 }
 
 const System = new function() {
@@ -694,20 +741,15 @@ const System = new function() {
     this.noop = () => {}
 
     this.launchLock = false;
-    
-    this.waitLaunchUnlock = (callback) => {
-        setTimeout(()=>{
-            if(this.ajaxLock){
-                this.waitLaunchUnlock(callback);
-            }else{
-                return callback();
-            }
-        }, 100)
-    }
 
     this.ajaxWait = () =>{
         return new Promise(resolve =>{
-             System.waitLaunchUnlock(resolve);
+            let interval = setInterval(()=>{
+                if(this.launchLock === false) {
+                    clearInterval(interval);
+                    resolve();
+                }
+            }, 100)
         });
     }
 
@@ -1005,10 +1047,12 @@ const System = new function() {
             try{
                 $.getJSON( _path + '/define.json', appData ).fail( () => {
                     Notification.push('kitアプリをロードできません。', `${_path}を展開できませんでした。`, 'system');
+                    System.launchLock = false;
                 } );
             }
             catch(error){
                 Notification.push( "System Error", error, "system" );
+                System.launchLock = false;
             }
         }
     }
@@ -1071,7 +1115,7 @@ const System = new function() {
 }
 
 const KWS = new function(){
-    this.version = "3.2.2";
+    this.version = "3.2.3";
     this.active = null;
 
     this.darkmode = false;
@@ -1104,10 +1148,16 @@ const KWS = new function(){
     }
 
     this.max = function( _pid ){
-        if( KWS.fullscreen.pid ){
-            Notification.push("最大化に失敗", "最大化しているウィンドウがあります。");
+        let _appcache = System.appCache[System.launchpath[_pid]];
+        if( KWS.fullscreen.pid || _appcache.support.fullscreen != true ){
+            Notification.push('最大化に失敗', 'ウィンドウの最大化に失敗しました。', 'system');
             return;
         }
+        KWS.fullscreen.prevWidth = $("#winc"+_pid).outerWidth();
+        KWS.fullscreen.prevHeight = $("#winc"+_pid).outerHeight();
+        KWS.fullscreen.prevTop = $("#w"+_pid).offset().top;
+        KWS.fullscreen.prevLeft = $("#w"+_pid).offset().left;
+        KWS.fullscreen.pid = _pid;
         $( "#wt"+_pid ).addClass("wtmaximize");
         $( "#w"+_pid ).css({
             "top": "0px",
@@ -1116,18 +1166,9 @@ const KWS = new function(){
         .addClass("windowmaximize")
         .css("z-index", KWS.windowIndex + 1);
         KWS.refreshWindowIndex();
-
-        KWS.fullscreen.prevWidth = $("#winc"+_pid).outerWidth();
-        KWS.fullscreen.prevHeight = $("#winc"+_pid).outerHeight();
-        KWS.fullscreen.prevTop = $("#w"+_pid).offset().top;
-        KWS.fullscreen.prevLeft = $("#w"+_pid).offset().left;
-
         KWS.resize( _pid, System.display.width, System.display.height - 30 );
         $("footer").hide();
-        $("#kit-header-fullscreen").show().on("click", () => {
-            KWS.unmax( _pid );
-        });
-        KWS.fullscreen.pid = _pid;
+        $("#kit-header-fullscreen").show().on('click', () => KWS.unmax( _pid ));
     }
 
     this.unmax = function( _pid ){
@@ -1135,8 +1176,8 @@ const KWS = new function(){
             Notification.push("最大化解除に失敗", "対象がフルスクリーンウィンドウではありません。");
             return;
         }
-        $( "#wt"+_pid ).removeClass("wtmaximize");
-        $( "#w"+_pid ).css({
+        $('#wt'+_pid).removeClass("wtmaximize");
+        $('#w'+_pid).css({
             "top": KWS.fullscreen.prevTop,
             "left": KWS.fullscreen.prevLeft
         })
@@ -1149,6 +1190,10 @@ const KWS = new function(){
         KWS.fullscreen.prevHeight = null;
         KWS.fullscreen.prevTop = null;
         KWS.fullscreen.prevLeft = null;
+
+        if( !System.appCache[System.launchpath[_pid]].size.height ) {
+            System.qs(_pid)[0].style.height = "auto";
+        }
     }
 
     this.vacuum = function( _left, _top ){
@@ -1176,16 +1221,26 @@ const KWS = new function(){
         } );
         for( let i in array ){
             document.getElementById(array[i].id).style.zIndex = i;
+            let _pid = String(array[i].id).substring(1);
             if( i == num-1 ){
                 $("#"+array[i].id).addClass("windowactive");
-                $("#t"+String(array[i].id).substring(1)).addClass("t-active");
-                KWS.active = String(array[i].id).substring(1);
-                process[array[i].id.substring(1)].isactive = true;
+                $("#t"+_pid).addClass("t-active");
+                KWS.active = _pid;
+                process[_pid].isactive = true;
+                KWS.screenPrevSwitched = new Date();
+                localStorage.setItem('kit-screentime', JSON.stringify(KWS.screenTime));
             }
             else{
                 $("#"+array[i].id).removeClass("windowactive");
-                $("#t"+String(array[i].id).substring(1)).removeClass("t-active");
-                process[array[i].id.substring(1)].isactive = false;
+                $("#t"+_pid).removeClass("t-active");
+                process[_pid].isactive = false;
+                if( KWS.active == _pid ){
+                    let _diff = (new Date() - KWS.screenPrevSwitched);
+                    let _appid = process[_pid].id
+                    if( !KWS.screenTime[_appid] ) KWS.screenTime[_appid] = new Number();
+                    if( _diff < 0 ) Notification.push('debug', 'スクリーンタイムの記録に失敗しました。', 'system')
+                    else KWS.screenTime[_appid] += _diff;
+                }
             }
         }
         KWS.windowIndex = num;
@@ -1201,6 +1256,17 @@ const KWS = new function(){
         if( _height ) $("#winc"+_pid).css("height", _height);
     }
 
+    this.setTheme = function(_name){
+        localStorage.setItem('kit-theme', _name);
+        if(_name != 'none') $('#kit-theme-file').attr('href', `./system/theme/${localStorage.getItem('kit-theme')}`);
+        else $('#kit-theme-file').attr('href', '');
+        System.moveDesktop(currentDesktop);
+    }
+
+    this.screenTime = new Object();
+
+    this.screenPrevSwitched = new Date();
+
     this.fusen = new function(){
         this.fid = 0;
         this.list = new Object();
@@ -1225,7 +1291,6 @@ const KWS = new function(){
                 }
             })
             $(".kit-fusen-textarea").off().on("change",function(){
-                Notification.push($(this).attr("data-fid"), $(this).val(), "debug");
                 KWS.fusen.list[$(this).attr("data-fid")] = $(this).val();
                 localStorage.setItem("kit-fusen", JSON.stringify( KWS.fusen.list ));
             });
@@ -1377,17 +1442,34 @@ class App {
         if( _value !== undefined ) {
             S.dom(_pid, `[kit\\:bind=${_name}]`).val( _value );
             S.dom(_pid, `[kit\\:observe=${_name}]`).text( _value );
-            if( _value ) S.dom(_pid, `[kit\\:if=${_name}]`).show();
-            else S.dom(_pid, `[kit\\:if=${_name}]`).hide();
+            S.dom(_pid, `template[kit\\:for=${_name}] + kit-for`).text('');
+            if (typeof _value == 'object'){
+                for(let elem of S.qs(_pid, `template[kit\\:for=${_name}] + kit-for`)){
+                    let _rep = App.d[_pid][`__kaf_node_id_${elem.getAttribute('kaf-node-id')}`], _result = '';
+                    for(let i in _value) {
+                        _result += _rep.replace(/{{\s*key\s*}}/g, i).replace(/{{\s*value\s*}}/g, _value[i]);
+                    }
+                    elem.innerHTML = _result;
+                }
+            }
+            if( _value ) {
+                S.dom(_pid, `[kit\\:if=${_name}]`).show();
+                S.dom(_pid, `[kit\\:disabled=${_name}]`).prop('disabled', true);
+            }
+            else{
+                S.dom(_pid, `[kit\\:if=${_name}]`).hide();
+                S.dom(_pid, `[kit\\:disabled=${_name}]`).prop('disabled', false).removeClass('-disabled');
+            }
             return App.d[_pid][_name] = _value;
         }
         else if( _name ) return App.d[_pid][_name];
-        else return App.d[_pid];
+        else return Object.fromEntries( Object.entries(App.d[_pid] || {}).filter(d => d[0].indexOf("__") != 0) );
     }
 
     static event( _pid, _name, _event ) {
         if( !App.e[_pid] ) App.e[_pid] = new Object();
-        App.e[_pid][_name] = _event;
+        if( !_event && App.e[_pid][_name] ) App.e[_pid][_name].call();
+        else App.e[_pid][_name] = _event;
         return App;
     }
 
@@ -1412,11 +1494,13 @@ class App {
             "[kit-value]",
             "[kit-color]",
             "[kit\\:if]",
-            "[kit-if]"
+            "[kit-if]",
+            "[kit\\:for]"
         ]
         const PID = _pid;
         const DATA = App.data(_pid);
         const ARGS = System.args[_pid];
+        let _kaf_node_id = 0;
         for( let i of S.qs(_pid, ...attrs) ){
             if( i.hasAttribute("kit-ref") ){
                 $(i).on("click", () => App.load(_pid, i.getAttribute("kit-ref")) );
@@ -1425,7 +1509,9 @@ class App {
                 let _eqs = i.getAttribute("kit-e").split(",");
                 for( let k of _eqs ){
                     let _eq = k.split(" ");
-                    $(i).on( _eq[1]||"click", App.e[_pid][_eq[0]] );
+                    $(i).on( _eq[1]||'click', (e) => {
+                        if(e.target.classList.contains('-disabled') === false) App.e[_pid][_eq[0]]();
+                    } );
                 }
             }
             if( i.hasAttribute("kit-src") ){
@@ -1452,8 +1538,14 @@ class App {
                     let _name = i.getAttribute("kit:bind");
                     App.d[_pid][_name] = i.value;
                     S.dom(_pid, `[kit\\:observe=${_name}]`).text( i.value );
-                    if( i.value ) S.dom(_pid, `[kit\\:if=${_name}]`).show();
-                    else S.dom(_pid, `[kit\\:if=${_name}]`).hide();
+                    if( i.value ){
+                        S.dom(_pid, `[kit\\:if=${_name}]`).show();
+                        S.dom(_pid, `[kit\\:disabled=${_name}]`).prop('disabled', true).addClass('-disabled');
+                    }
+                    else{
+                        S.dom(_pid, `[kit\\:if=${_name}]`).hide();
+                        S.dom(_pid, `[kit\\:disabled=${_name}]`).prop('disabled', false).removeClass('-disabled');
+                    }
                 } );
             }
             if( i.hasAttribute("kit:observe") ){
@@ -1474,12 +1566,28 @@ class App {
                 }
                 else $(i).hide();
             }
+            if( i.hasAttribute("kit:disabled") ){
+                if( App.d[_pid][i.getAttribute("kit:if")] ){
+                    i.disabled = true;
+                    i.classList.add('-disabled');
+                }
+                else i.disabled = false;
+            }
             if( i.hasAttribute("kit-if") ){
                 if( eval( i.getAttribute("kit-if")) ){
                     $(i).show();
                 }
                 else $(i).hide();
             }
+            if( i.hasAttribute("kit:for") ){
+                if ('content' in document.createElement('template')) {
+                    i.setAttribute('kaf-node-id', _kaf_node_id);
+                    App.d[_pid][`__kaf_node_id_${_kaf_node_id}`] = i.innerHTML;
+                    i.insertAdjacentHTML('afterend', `<kit-for kaf-node-id="${_kaf_node_id}"></kit-for>`);
+                }
+                else i.style.display = 'none';
+            }
+            _kaf_node_id ++;
         }
     }
 
@@ -1488,6 +1596,10 @@ class App {
         _path = System.launchpath[_pid] + _path;
         S.dom(_pid).load( _path, () => {
             App.kaf(_pid);
+            let _appcache = System.appCache[System.launchpath[_pid]];
+            if( !KWS.fullscreen.pid && !_appcache.size.height ) {
+                System.qs(_pid)[0].style.height = "auto";
+            }
         } );
         return App;
     }